1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/lib/sdk/windows/firefox.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,268 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 +'use strict'; 1.8 + 1.9 +const { Cc, Ci, Cr } = require('chrome'), 1.10 + { Trait } = require('../deprecated/traits'), 1.11 + { List } = require('../deprecated/list'), 1.12 + { EventEmitter } = require('../deprecated/events'), 1.13 + { WindowTabs, WindowTabTracker } = require('./tabs-firefox'), 1.14 + { WindowDom } = require('./dom'), 1.15 + { WindowLoader } = require('./loader'), 1.16 + { isBrowser, getWindowDocShell, windows: windowIterator } = require('../window/utils'), 1.17 + { Options } = require('../tabs/common'), 1.18 + apiUtils = require('../deprecated/api-utils'), 1.19 + unload = require('../system/unload'), 1.20 + windowUtils = require('../deprecated/window-utils'), 1.21 + { WindowTrackerTrait } = windowUtils, 1.22 + { ns } = require('../core/namespace'), 1.23 + { observer: windowObserver } = require('./observer'), 1.24 + { getOwnerWindow } = require('../private-browsing/window/utils'); 1.25 +const { windowNS } = require('../window/namespace'); 1.26 +const { isPrivateBrowsingSupported } = require('../self'); 1.27 +const { ignoreWindow } = require('sdk/private-browsing/utils'); 1.28 +const { viewFor } = require('../view/core'); 1.29 + 1.30 +/** 1.31 + * Window trait composes safe wrappers for browser window that are E10S 1.32 + * compatible. 1.33 + */ 1.34 +const BrowserWindowTrait = Trait.compose( 1.35 + EventEmitter, 1.36 + WindowDom.resolve({ close: '_close' }), 1.37 + WindowTabs, 1.38 + WindowTabTracker, 1.39 + WindowLoader, 1.40 + /* WindowSidebars, */ 1.41 + Trait.compose({ 1.42 + _emit: Trait.required, 1.43 + _close: Trait.required, 1.44 + _load: Trait.required, 1.45 + /** 1.46 + * Constructor returns wrapper of the specified chrome window. 1.47 + * @param {nsIWindow} window 1.48 + */ 1.49 + constructor: function BrowserWindow(options) { 1.50 + // Register this window ASAP, in order to avoid loop that would try 1.51 + // to create this window instance over and over (see bug 648244) 1.52 + windows.push(this); 1.53 + 1.54 + // make sure we don't have unhandled errors 1.55 + this.on('error', console.exception.bind(console)); 1.56 + 1.57 + if ('onOpen' in options) 1.58 + this.on('open', options.onOpen); 1.59 + if ('onClose' in options) 1.60 + this.on('close', options.onClose); 1.61 + if ('onActivate' in options) 1.62 + this.on('activate', options.onActivate); 1.63 + if ('onDeactivate' in options) 1.64 + this.on('deactivate', options.onDeactivate); 1.65 + if ('window' in options) 1.66 + this._window = options.window; 1.67 + 1.68 + if ('tabs' in options) { 1.69 + this._tabOptions = Array.isArray(options.tabs) ? 1.70 + options.tabs.map(Options) : 1.71 + [ Options(options.tabs) ]; 1.72 + } 1.73 + else if ('url' in options) { 1.74 + this._tabOptions = [ Options(options.url) ]; 1.75 + } 1.76 + 1.77 + this._isPrivate = isPrivateBrowsingSupported && !!options.isPrivate; 1.78 + 1.79 + this._load(); 1.80 + 1.81 + windowNS(this._public).window = this._window; 1.82 + getOwnerWindow.implement(this._public, getChromeWindow); 1.83 + viewFor.implement(this._public, getChromeWindow); 1.84 + 1.85 + return this; 1.86 + }, 1.87 + destroy: function () this._onUnload(), 1.88 + _tabOptions: [], 1.89 + _onLoad: function() { 1.90 + try { 1.91 + this._initWindowTabTracker(); 1.92 + this._loaded = true; 1.93 + } 1.94 + catch(e) { 1.95 + this._emit('error', e); 1.96 + } 1.97 + 1.98 + this._emitOnObject(browserWindows, 'open', this._public); 1.99 + }, 1.100 + _onUnload: function() { 1.101 + if (!this._window) 1.102 + return; 1.103 + if (this._loaded) 1.104 + this._destroyWindowTabTracker(); 1.105 + 1.106 + this._emitOnObject(browserWindows, 'close', this._public); 1.107 + this._window = null; 1.108 + windowNS(this._public).window = null; 1.109 + // Removing reference from the windows array. 1.110 + windows.splice(windows.indexOf(this), 1); 1.111 + this._removeAllListeners(); 1.112 + }, 1.113 + close: function close(callback) { 1.114 + // maybe we should deprecate this with message ? 1.115 + if (callback) this.on('close', callback); 1.116 + return this._close(); 1.117 + } 1.118 + }) 1.119 +); 1.120 + 1.121 +/** 1.122 + * Gets a `BrowserWindowTrait` for the given `chromeWindow` if previously 1.123 + * registered, `null` otherwise. 1.124 + */ 1.125 +function getRegisteredWindow(chromeWindow) { 1.126 + for each (let window in windows) { 1.127 + if (chromeWindow === window._window) 1.128 + return window; 1.129 + } 1.130 + 1.131 + return null; 1.132 +} 1.133 + 1.134 +/** 1.135 + * Wrapper for `BrowserWindowTrait`. Creates new instance if wrapper for 1.136 + * window doesn't exists yet. If wrapper already exists then returns it 1.137 + * instead. 1.138 + * @params {Object} options 1.139 + * Options that are passed to the the `BrowserWindowTrait` 1.140 + * @returns {BrowserWindow} 1.141 + * @see BrowserWindowTrait 1.142 + */ 1.143 +function BrowserWindow(options) { 1.144 + let window = null; 1.145 + 1.146 + if ("window" in options) 1.147 + window = getRegisteredWindow(options.window); 1.148 + 1.149 + return (window || BrowserWindowTrait(options))._public; 1.150 +} 1.151 +// to have proper `instanceof` behavior will go away when #596248 is fixed. 1.152 +BrowserWindow.prototype = BrowserWindowTrait.prototype; 1.153 +exports.BrowserWindow = BrowserWindow; 1.154 + 1.155 +const windows = []; 1.156 + 1.157 +const browser = ns(); 1.158 + 1.159 +function onWindowActivation (chromeWindow, event) { 1.160 + if (!isBrowser(chromeWindow)) return; // Ignore if it's not a browser window. 1.161 + 1.162 + let window = getRegisteredWindow(chromeWindow); 1.163 + 1.164 + if (window) 1.165 + window._emit(event.type, window._public); 1.166 + else 1.167 + window = BrowserWindowTrait({ window: chromeWindow }); 1.168 + 1.169 + browser(browserWindows).internals._emit(event.type, window._public); 1.170 +} 1.171 + 1.172 +windowObserver.on("activate", onWindowActivation); 1.173 +windowObserver.on("deactivate", onWindowActivation); 1.174 + 1.175 +/** 1.176 + * `BrowserWindows` trait is composed out of `List` trait and it represents 1.177 + * "live" list of currently open browser windows. Instance mutates itself 1.178 + * whenever new browser window gets opened / closed. 1.179 + */ 1.180 +// Very stupid to resolve all `toStrings` but this will be fixed by #596248 1.181 +const browserWindows = Trait.resolve({ toString: null }).compose( 1.182 + List.resolve({ constructor: '_initList' }), 1.183 + EventEmitter.resolve({ toString: null }), 1.184 + WindowTrackerTrait.resolve({ constructor: '_initTracker', toString: null }), 1.185 + Trait.compose({ 1.186 + _emit: Trait.required, 1.187 + _add: Trait.required, 1.188 + _remove: Trait.required, 1.189 + 1.190 + // public API 1.191 + 1.192 + /** 1.193 + * Constructor creates instance of `Windows` that represents live list of open 1.194 + * windows. 1.195 + */ 1.196 + constructor: function BrowserWindows() { 1.197 + browser(this._public).internals = this; 1.198 + 1.199 + this._trackedWindows = []; 1.200 + this._initList(); 1.201 + this._initTracker(); 1.202 + unload.ensure(this, "_destructor"); 1.203 + }, 1.204 + _destructor: function _destructor() { 1.205 + this._removeAllListeners('open'); 1.206 + this._removeAllListeners('close'); 1.207 + this._removeAllListeners('activate'); 1.208 + this._removeAllListeners('deactivate'); 1.209 + this._clear(); 1.210 + 1.211 + delete browser(this._public).internals; 1.212 + }, 1.213 + /** 1.214 + * This property represents currently active window. 1.215 + * Property is non-enumerable, in order to preserve array like enumeration. 1.216 + * @type {Window|null} 1.217 + */ 1.218 + get activeWindow() { 1.219 + let window = windowUtils.activeBrowserWindow; 1.220 + // Bug 834961: ignore private windows when they are not supported 1.221 + if (ignoreWindow(window)) 1.222 + window = windowIterator()[0]; 1.223 + return window ? BrowserWindow({window: window}) : null; 1.224 + }, 1.225 + open: function open(options) { 1.226 + if (typeof options === "string") { 1.227 + // `tabs` option is under review and may be removed. 1.228 + options = { 1.229 + tabs: [Options(options)], 1.230 + isPrivate: isPrivateBrowsingSupported && options.isPrivate 1.231 + }; 1.232 + } 1.233 + return BrowserWindow(options); 1.234 + }, 1.235 + 1.236 + /** 1.237 + * Internal listener which is called whenever new window gets open. 1.238 + * Creates wrapper and adds to this list. 1.239 + * @param {nsIWindow} chromeWindow 1.240 + */ 1.241 + _onTrack: function _onTrack(chromeWindow) { 1.242 + if (!isBrowser(chromeWindow)) return; 1.243 + let window = BrowserWindow({ window: chromeWindow }); 1.244 + this._add(window); 1.245 + this._emit('open', window); 1.246 + }, 1.247 + 1.248 + /** 1.249 + * Internal listener which is called whenever window gets closed. 1.250 + * Cleans up references and removes wrapper from this list. 1.251 + * @param {nsIWindow} window 1.252 + */ 1.253 + _onUntrack: function _onUntrack(chromeWindow) { 1.254 + if (!isBrowser(chromeWindow)) return; 1.255 + let window = BrowserWindow({ window: chromeWindow }); 1.256 + this._remove(window); 1.257 + this._emit('close', window); 1.258 + 1.259 + // Bug 724404: do not leak this module and linked windows: 1.260 + // We have to do it on untrack and not only when `_onUnload` is called 1.261 + // when windows are closed, otherwise, we will leak on addon disabling. 1.262 + window.destroy(); 1.263 + } 1.264 + }).resolve({ toString: null }) 1.265 +)(); 1.266 + 1.267 +function getChromeWindow(window) { 1.268 + return windowNS(window).window; 1.269 +} 1.270 + 1.271 +exports.browserWindows = browserWindows;