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

changeset 0
6474c204b198
     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;

mercurial