firefox.js 8.6 KB

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