addon-sdk/source/lib/sdk/windows/firefox.js

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:7d9857232191
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 'use strict';
5
6 const { Cc, Ci, Cr } = require('chrome'),
7 { Trait } = require('../deprecated/traits'),
8 { List } = require('../deprecated/list'),
9 { EventEmitter } = require('../deprecated/events'),
10 { WindowTabs, WindowTabTracker } = require('./tabs-firefox'),
11 { WindowDom } = require('./dom'),
12 { WindowLoader } = require('./loader'),
13 { isBrowser, getWindowDocShell, windows: windowIterator } = require('../window/utils'),
14 { Options } = require('../tabs/common'),
15 apiUtils = require('../deprecated/api-utils'),
16 unload = require('../system/unload'),
17 windowUtils = require('../deprecated/window-utils'),
18 { WindowTrackerTrait } = windowUtils,
19 { ns } = require('../core/namespace'),
20 { observer: windowObserver } = require('./observer'),
21 { getOwnerWindow } = require('../private-browsing/window/utils');
22 const { windowNS } = require('../window/namespace');
23 const { isPrivateBrowsingSupported } = require('../self');
24 const { ignoreWindow } = require('sdk/private-browsing/utils');
25 const { viewFor } = require('../view/core');
26
27 /**
28 * Window trait composes safe wrappers for browser window that are E10S
29 * compatible.
30 */
31 const BrowserWindowTrait = Trait.compose(
32 EventEmitter,
33 WindowDom.resolve({ close: '_close' }),
34 WindowTabs,
35 WindowTabTracker,
36 WindowLoader,
37 /* WindowSidebars, */
38 Trait.compose({
39 _emit: Trait.required,
40 _close: Trait.required,
41 _load: Trait.required,
42 /**
43 * Constructor returns wrapper of the specified chrome window.
44 * @param {nsIWindow} window
45 */
46 constructor: function BrowserWindow(options) {
47 // Register this window ASAP, in order to avoid loop that would try
48 // to create this window instance over and over (see bug 648244)
49 windows.push(this);
50
51 // make sure we don't have unhandled errors
52 this.on('error', console.exception.bind(console));
53
54 if ('onOpen' in options)
55 this.on('open', options.onOpen);
56 if ('onClose' in options)
57 this.on('close', options.onClose);
58 if ('onActivate' in options)
59 this.on('activate', options.onActivate);
60 if ('onDeactivate' in options)
61 this.on('deactivate', options.onDeactivate);
62 if ('window' in options)
63 this._window = options.window;
64
65 if ('tabs' in options) {
66 this._tabOptions = Array.isArray(options.tabs) ?
67 options.tabs.map(Options) :
68 [ Options(options.tabs) ];
69 }
70 else if ('url' in options) {
71 this._tabOptions = [ Options(options.url) ];
72 }
73
74 this._isPrivate = isPrivateBrowsingSupported && !!options.isPrivate;
75
76 this._load();
77
78 windowNS(this._public).window = this._window;
79 getOwnerWindow.implement(this._public, getChromeWindow);
80 viewFor.implement(this._public, getChromeWindow);
81
82 return this;
83 },
84 destroy: function () this._onUnload(),
85 _tabOptions: [],
86 _onLoad: function() {
87 try {
88 this._initWindowTabTracker();
89 this._loaded = true;
90 }
91 catch(e) {
92 this._emit('error', e);
93 }
94
95 this._emitOnObject(browserWindows, 'open', this._public);
96 },
97 _onUnload: function() {
98 if (!this._window)
99 return;
100 if (this._loaded)
101 this._destroyWindowTabTracker();
102
103 this._emitOnObject(browserWindows, 'close', this._public);
104 this._window = null;
105 windowNS(this._public).window = null;
106 // Removing reference from the windows array.
107 windows.splice(windows.indexOf(this), 1);
108 this._removeAllListeners();
109 },
110 close: function close(callback) {
111 // maybe we should deprecate this with message ?
112 if (callback) this.on('close', callback);
113 return this._close();
114 }
115 })
116 );
117
118 /**
119 * Gets a `BrowserWindowTrait` for the given `chromeWindow` if previously
120 * registered, `null` otherwise.
121 */
122 function getRegisteredWindow(chromeWindow) {
123 for each (let window in windows) {
124 if (chromeWindow === window._window)
125 return window;
126 }
127
128 return null;
129 }
130
131 /**
132 * Wrapper for `BrowserWindowTrait`. Creates new instance if wrapper for
133 * window doesn't exists yet. If wrapper already exists then returns it
134 * instead.
135 * @params {Object} options
136 * Options that are passed to the the `BrowserWindowTrait`
137 * @returns {BrowserWindow}
138 * @see BrowserWindowTrait
139 */
140 function BrowserWindow(options) {
141 let window = null;
142
143 if ("window" in options)
144 window = getRegisteredWindow(options.window);
145
146 return (window || BrowserWindowTrait(options))._public;
147 }
148 // to have proper `instanceof` behavior will go away when #596248 is fixed.
149 BrowserWindow.prototype = BrowserWindowTrait.prototype;
150 exports.BrowserWindow = BrowserWindow;
151
152 const windows = [];
153
154 const browser = ns();
155
156 function onWindowActivation (chromeWindow, event) {
157 if (!isBrowser(chromeWindow)) return; // Ignore if it's not a browser window.
158
159 let window = getRegisteredWindow(chromeWindow);
160
161 if (window)
162 window._emit(event.type, window._public);
163 else
164 window = BrowserWindowTrait({ window: chromeWindow });
165
166 browser(browserWindows).internals._emit(event.type, window._public);
167 }
168
169 windowObserver.on("activate", onWindowActivation);
170 windowObserver.on("deactivate", onWindowActivation);
171
172 /**
173 * `BrowserWindows` trait is composed out of `List` trait and it represents
174 * "live" list of currently open browser windows. Instance mutates itself
175 * whenever new browser window gets opened / closed.
176 */
177 // Very stupid to resolve all `toStrings` but this will be fixed by #596248
178 const browserWindows = Trait.resolve({ toString: null }).compose(
179 List.resolve({ constructor: '_initList' }),
180 EventEmitter.resolve({ toString: null }),
181 WindowTrackerTrait.resolve({ constructor: '_initTracker', toString: null }),
182 Trait.compose({
183 _emit: Trait.required,
184 _add: Trait.required,
185 _remove: Trait.required,
186
187 // public API
188
189 /**
190 * Constructor creates instance of `Windows` that represents live list of open
191 * windows.
192 */
193 constructor: function BrowserWindows() {
194 browser(this._public).internals = this;
195
196 this._trackedWindows = [];
197 this._initList();
198 this._initTracker();
199 unload.ensure(this, "_destructor");
200 },
201 _destructor: function _destructor() {
202 this._removeAllListeners('open');
203 this._removeAllListeners('close');
204 this._removeAllListeners('activate');
205 this._removeAllListeners('deactivate');
206 this._clear();
207
208 delete browser(this._public).internals;
209 },
210 /**
211 * This property represents currently active window.
212 * Property is non-enumerable, in order to preserve array like enumeration.
213 * @type {Window|null}
214 */
215 get activeWindow() {
216 let window = windowUtils.activeBrowserWindow;
217 // Bug 834961: ignore private windows when they are not supported
218 if (ignoreWindow(window))
219 window = windowIterator()[0];
220 return window ? BrowserWindow({window: window}) : null;
221 },
222 open: function open(options) {
223 if (typeof options === "string") {
224 // `tabs` option is under review and may be removed.
225 options = {
226 tabs: [Options(options)],
227 isPrivate: isPrivateBrowsingSupported && options.isPrivate
228 };
229 }
230 return BrowserWindow(options);
231 },
232
233 /**
234 * Internal listener which is called whenever new window gets open.
235 * Creates wrapper and adds to this list.
236 * @param {nsIWindow} chromeWindow
237 */
238 _onTrack: function _onTrack(chromeWindow) {
239 if (!isBrowser(chromeWindow)) return;
240 let window = BrowserWindow({ window: chromeWindow });
241 this._add(window);
242 this._emit('open', window);
243 },
244
245 /**
246 * Internal listener which is called whenever window gets closed.
247 * Cleans up references and removes wrapper from this list.
248 * @param {nsIWindow} window
249 */
250 _onUntrack: function _onUntrack(chromeWindow) {
251 if (!isBrowser(chromeWindow)) return;
252 let window = BrowserWindow({ window: chromeWindow });
253 this._remove(window);
254 this._emit('close', window);
255
256 // Bug 724404: do not leak this module and linked windows:
257 // We have to do it on untrack and not only when `_onUnload` is called
258 // when windows are closed, otherwise, we will leak on addon disabling.
259 window.destroy();
260 }
261 }).resolve({ toString: null })
262 )();
263
264 function getChromeWindow(window) {
265 return windowNS(window).window;
266 }
267
268 exports.browserWindows = browserWindows;

mercurial