michael@0: /* vim:set ts=2 sw=2 sts=2 et tw=80: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: this.EXPORTED_SYMBOLS = ["ScratchpadManager"]; michael@0: michael@0: const Cc = Components.classes; michael@0: const Ci = Components.interfaces; michael@0: const Cu = Components.utils; michael@0: michael@0: const SCRATCHPAD_WINDOW_URL = "chrome://browser/content/devtools/scratchpad.xul"; michael@0: const SCRATCHPAD_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no"; michael@0: michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: michael@0: /** michael@0: * The ScratchpadManager object opens new Scratchpad windows and manages the state michael@0: * of open scratchpads for session restore. There's only one ScratchpadManager in michael@0: * the life of the browser. michael@0: */ michael@0: this.ScratchpadManager = { michael@0: michael@0: _nextUid: 1, michael@0: _scratchpads: [], michael@0: michael@0: /** michael@0: * Get the saved states of open scratchpad windows. Called by michael@0: * session restore. michael@0: * michael@0: * @return array michael@0: * The array of scratchpad states. michael@0: */ michael@0: getSessionState: function SPM_getSessionState() michael@0: { michael@0: return this._scratchpads; michael@0: }, michael@0: michael@0: /** michael@0: * Restore scratchpad windows from the scratchpad session store file. michael@0: * Called by session restore. michael@0: * michael@0: * @param function aSession michael@0: * The session object with scratchpad states. michael@0: * michael@0: * @return array michael@0: * The restored scratchpad windows. michael@0: */ michael@0: restoreSession: function SPM_restoreSession(aSession) michael@0: { michael@0: if (!Array.isArray(aSession)) { michael@0: return []; michael@0: } michael@0: michael@0: let wins = []; michael@0: aSession.forEach(function(state) { michael@0: let win = this.openScratchpad(state); michael@0: wins.push(win); michael@0: }, this); michael@0: michael@0: return wins; michael@0: }, michael@0: michael@0: /** michael@0: * Iterate through open scratchpad windows and save their states. michael@0: */ michael@0: saveOpenWindows: function SPM_saveOpenWindows() { michael@0: this._scratchpads = []; michael@0: michael@0: function clone(src) { michael@0: let dest = {}; michael@0: michael@0: for (let key in src) { michael@0: if (src.hasOwnProperty(key)) { michael@0: dest[key] = src[key]; michael@0: } michael@0: } michael@0: michael@0: return dest; michael@0: } michael@0: michael@0: // We need to clone objects we get from Scratchpad instances michael@0: // because such (cross-window) objects have a property 'parent' michael@0: // that holds on to a ChromeWindow instance. This means that michael@0: // such objects are not primitive-values-only anymore so they michael@0: // can leak. michael@0: michael@0: let enumerator = Services.wm.getEnumerator("devtools:scratchpad"); michael@0: while (enumerator.hasMoreElements()) { michael@0: let win = enumerator.getNext(); michael@0: if (!win.closed && win.Scratchpad.initialized) { michael@0: this._scratchpads.push(clone(win.Scratchpad.getState())); michael@0: } michael@0: } michael@0: }, michael@0: michael@0: /** michael@0: * Open a new scratchpad window with an optional initial state. michael@0: * michael@0: * @param object aState michael@0: * Optional. The initial state of the scratchpad, an object michael@0: * with properties filename, text, and executionContext. michael@0: * michael@0: * @return nsIDomWindow michael@0: * The opened scratchpad window. michael@0: */ michael@0: openScratchpad: function SPM_openScratchpad(aState) michael@0: { michael@0: let params = Cc["@mozilla.org/embedcomp/dialogparam;1"] michael@0: .createInstance(Ci.nsIDialogParamBlock); michael@0: michael@0: params.SetNumberStrings(2); michael@0: params.SetString(0, this.createUid()); michael@0: michael@0: if (aState) { michael@0: if (typeof aState != 'object') { michael@0: return; michael@0: } michael@0: michael@0: params.SetString(1, JSON.stringify(aState)); michael@0: } michael@0: michael@0: let win = Services.ww.openWindow(null, SCRATCHPAD_WINDOW_URL, "_blank", michael@0: SCRATCHPAD_WINDOW_FEATURES, params); michael@0: michael@0: // Only add the shutdown observer if we've opened a scratchpad window. michael@0: ShutdownObserver.init(); michael@0: michael@0: return win; michael@0: }, michael@0: michael@0: /** michael@0: * Create a unique ID for a new Scratchpad. michael@0: */ michael@0: createUid: function SPM_createUid() michael@0: { michael@0: return JSON.stringify(this._nextUid++); michael@0: } michael@0: }; michael@0: michael@0: michael@0: /** michael@0: * The ShutdownObserver listens for app shutdown and saves the current state michael@0: * of the scratchpads for session restore. michael@0: */ michael@0: var ShutdownObserver = { michael@0: _initialized: false, michael@0: michael@0: init: function SDO_init() michael@0: { michael@0: if (this._initialized) { michael@0: return; michael@0: } michael@0: michael@0: Services.obs.addObserver(this, "quit-application-granted", false); michael@0: michael@0: this._initialized = true; michael@0: }, michael@0: michael@0: observe: function SDO_observe(aMessage, aTopic, aData) michael@0: { michael@0: if (aTopic == "quit-application-granted") { michael@0: ScratchpadManager.saveOpenWindows(); michael@0: this.uninit(); michael@0: } michael@0: }, michael@0: michael@0: uninit: function SDO_uninit() michael@0: { michael@0: Services.obs.removeObserver(this, "quit-application-granted"); michael@0: } michael@0: };