diff -r 000000000000 -r 6474c204b198 addon-sdk/source/lib/sdk/deprecated/window-utils.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/addon-sdk/source/lib/sdk/deprecated/window-utils.js Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,227 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +module.metadata = { + 'stability': 'deprecated' +}; + +const { Cc, Ci } = require('chrome'); +const { EventEmitter } = require('../deprecated/events'); +const { Trait } = require('../deprecated/traits'); +const { when } = require('../system/unload'); +const events = require('../system/events'); +const { getInnerId, getOuterId, windows, isDocumentLoaded, isBrowser, + getMostRecentBrowserWindow, getMostRecentWindow } = require('../window/utils'); +const errors = require('../deprecated/errors'); +const { deprecateFunction } = require('../util/deprecate'); +const { ignoreWindow, isGlobalPBSupported } = require('sdk/private-browsing/utils'); +const { isPrivateBrowsingSupported } = require('../self'); + +const windowWatcher = Cc['@mozilla.org/embedcomp/window-watcher;1']. + getService(Ci.nsIWindowWatcher); +const appShellService = Cc['@mozilla.org/appshell/appShellService;1']. + getService(Ci.nsIAppShellService); + +// Bug 834961: ignore private windows when they are not supported +function getWindows() windows(null, { includePrivate: isPrivateBrowsingSupported || isGlobalPBSupported }); + +/** + * An iterator for XUL windows currently in the application. + * + * @return A generator that yields XUL windows exposing the + * nsIDOMWindow interface. + */ +function windowIterator() { + // Bug 752631: We only pass already loaded window in order to avoid + // breaking XUL windows DOM. DOM is broken when some JS code try + // to access DOM during "uninitialized" state of the related document. + let list = getWindows().filter(isDocumentLoaded); + for (let i = 0, l = list.length; i < l; i++) { + yield list[i]; + } +}; +exports.windowIterator = windowIterator; + +/** + * An iterator for browser windows currently open in the application. + * @returns {Function} + * A generator that yields browser windows exposing the `nsIDOMWindow` + * interface. + */ +function browserWindowIterator() { + for each (let window in windowIterator()) { + if (isBrowser(window)) + yield window; + } +} +exports.browserWindowIterator = browserWindowIterator; + +function WindowTracker(delegate) { + if (!(this instanceof WindowTracker)) { + return new WindowTracker(delegate); + } + + this._delegate = delegate; + this._loadingWindows = []; + + for each (let window in getWindows()) + this._regWindow(window); + windowWatcher.registerNotification(this); + this._onToplevelWindowReady = this._onToplevelWindowReady.bind(this); + events.on('toplevel-window-ready', this._onToplevelWindowReady); + + require('../system/unload').ensure(this); + + return this; +}; + +WindowTracker.prototype = { + _regLoadingWindow: function _regLoadingWindow(window) { + // Bug 834961: ignore private windows when they are not supported + if (ignoreWindow(window)) + return; + + this._loadingWindows.push(window); + window.addEventListener('load', this, true); + }, + + _unregLoadingWindow: function _unregLoadingWindow(window) { + var index = this._loadingWindows.indexOf(window); + + if (index != -1) { + this._loadingWindows.splice(index, 1); + window.removeEventListener('load', this, true); + } + }, + + _regWindow: function _regWindow(window) { + // Bug 834961: ignore private windows when they are not supported + if (ignoreWindow(window)) + return; + + if (window.document.readyState == 'complete') { + this._unregLoadingWindow(window); + this._delegate.onTrack(window); + } else + this._regLoadingWindow(window); + }, + + _unregWindow: function _unregWindow(window) { + if (window.document.readyState == 'complete') { + if (this._delegate.onUntrack) + this._delegate.onUntrack(window); + } else { + this._unregLoadingWindow(window); + } + }, + + unload: function unload() { + windowWatcher.unregisterNotification(this); + events.off('toplevel-window-ready', this._onToplevelWindowReady); + for each (let window in getWindows()) + this._unregWindow(window); + }, + + handleEvent: errors.catchAndLog(function handleEvent(event) { + if (event.type == 'load' && event.target) { + var window = event.target.defaultView; + if (window) + this._regWindow(window); + } + }), + + _onToplevelWindowReady: function _onToplevelWindowReady({subject}) { + let window = subject; + // ignore private windows if they are not supported + if (ignoreWindow(window)) + return; + this._regWindow(window); + }, + + observe: errors.catchAndLog(function observe(subject, topic, data) { + var window = subject.QueryInterface(Ci.nsIDOMWindow); + // ignore private windows if they are not supported + if (ignoreWindow(window)) + return; + if (topic == 'domwindowclosed') + this._unregWindow(window); + }) +}; +exports.WindowTracker = WindowTracker; + +const WindowTrackerTrait = Trait.compose({ + _onTrack: Trait.required, + _onUntrack: Trait.required, + constructor: function WindowTrackerTrait() { + WindowTracker({ + onTrack: this._onTrack.bind(this), + onUntrack: this._onUntrack.bind(this) + }); + } +}); +exports.WindowTrackerTrait = WindowTrackerTrait; + +var gDocsToClose = []; + +function onDocUnload(event) { + var index = gDocsToClose.indexOf(event.target); + if (index == -1) + throw new Error('internal error: unloading document not found'); + var document = gDocsToClose.splice(index, 1)[0]; + // Just in case, let's remove the event listener too. + document.defaultView.removeEventListener('unload', onDocUnload, false); +} + +onDocUnload = require('./errors').catchAndLog(onDocUnload); + +exports.closeOnUnload = function closeOnUnload(window) { + window.addEventListener('unload', onDocUnload, false); + gDocsToClose.push(window.document); +}; + +Object.defineProperties(exports, { + activeWindow: { + enumerable: true, + get: function() { + return getMostRecentWindow(null); + }, + set: function(window) { + try { + window.focus(); + } catch (e) {} + } + }, + activeBrowserWindow: { + enumerable: true, + get: getMostRecentBrowserWindow + } +}); + + +/** + * Returns the ID of the window's current inner window. + */ +exports.getInnerId = deprecateFunction(getInnerId, + 'require("window-utils").getInnerId is deprecated, ' + + 'please use require("sdk/window/utils").getInnerId instead' +); + +exports.getOuterId = deprecateFunction(getOuterId, + 'require("window-utils").getOuterId is deprecated, ' + + 'please use require("sdk/window/utils").getOuterId instead' +); + +exports.isBrowser = deprecateFunction(isBrowser, + 'require("window-utils").isBrowser is deprecated, ' + + 'please use require("sdk/window/utils").isBrowser instead' +); + +exports.hiddenWindow = appShellService.hiddenDOMWindow; + +when( + function() { + gDocsToClose.slice().forEach( + function(doc) { doc.defaultView.close(); }); + });