michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: 'use strict'; michael@0: michael@0: module.metadata = { michael@0: "stability": "unstable" michael@0: }; michael@0: michael@0: const { Cc, Ci } = require('chrome'), michael@0: { setTimeout } = require('../timers'), michael@0: { Trait } = require('../deprecated/traits'), michael@0: { openDialog } = require('../window/utils'), michael@0: michael@0: ON_LOAD = 'load', michael@0: ON_UNLOAD = 'unload', michael@0: STATE_LOADED = 'complete'; michael@0: michael@0: /** michael@0: * Trait provides private `_window` property and requires `_onLoad` property michael@0: * that will be called when `_window` is loaded. If `_window` property value michael@0: * is changed with already loaded window `_onLoad` still will be called. michael@0: */ michael@0: const WindowLoader = Trait.compose({ michael@0: /** michael@0: * Internal listener that is called when window is loaded. michael@0: * Please keep in mind that this trait will not handle exceptions that may michael@0: * be thrown by this method so method itself should take care of michael@0: * handling them. michael@0: * @param {nsIWindow} window michael@0: */ michael@0: _onLoad: Trait.required, michael@0: _tabOptions: Trait.required, michael@0: /** michael@0: * Internal listener that is called when `_window`'s DOM 'unload' event michael@0: * is dispatched. Please note that this trait will not handle exceptions that michael@0: * may be thrown by this method so method itself should take care of michael@0: * handling them. michael@0: */ michael@0: _onUnload: Trait.required, michael@0: _load: function _load() { michael@0: if (this.__window) michael@0: return; michael@0: michael@0: this._window = openDialog({ michael@0: private: this._isPrivate, michael@0: args: this._tabOptions.map(function(options) options.url).join("|") michael@0: }); michael@0: }, michael@0: /** michael@0: * Private window who's load event is being tracked. Once window is loaded michael@0: * `_onLoad` is called. michael@0: * @type {nsIWindow} michael@0: */ michael@0: get _window() this.__window, michael@0: set _window(window) { michael@0: let _window = this.__window; michael@0: if (!window) window = null; michael@0: michael@0: if (window !== _window) { michael@0: if (_window) { michael@0: _window.removeEventListener(ON_UNLOAD, this.__unloadListener, false); michael@0: _window.removeEventListener(ON_LOAD, this.__loadListener, false); michael@0: } michael@0: michael@0: if (window) { michael@0: window.addEventListener( michael@0: ON_UNLOAD, michael@0: this.__unloadListener || michael@0: (this.__unloadListener = this._unloadListener.bind(this)) michael@0: , michael@0: false michael@0: ); michael@0: michael@0: this.__window = window; michael@0: michael@0: // If window is not loaded yet setting up a listener. michael@0: if (STATE_LOADED != window.document.readyState) { michael@0: window.addEventListener( michael@0: ON_LOAD, michael@0: this.__loadListener || michael@0: (this.__loadListener = this._loadListener.bind(this)) michael@0: , michael@0: false michael@0: ); michael@0: } michael@0: else { // If window is loaded calling listener next turn of event loop. michael@0: this._onLoad(window) michael@0: } michael@0: } michael@0: else { michael@0: this.__window = null; michael@0: } michael@0: } michael@0: }, michael@0: __window: null, michael@0: /** michael@0: * Internal method used for listening 'load' event on the `_window`. michael@0: * Method takes care of removing itself from 'load' event listeners once michael@0: * event is being handled. michael@0: */ michael@0: _loadListener: function _loadListener(event) { michael@0: let window = this._window; michael@0: if (!event.target || event.target.defaultView != window) return; michael@0: window.removeEventListener(ON_LOAD, this.__loadListener, false); michael@0: this._onLoad(window); michael@0: }, michael@0: __loadListener: null, michael@0: /** michael@0: * Internal method used for listening 'unload' event on the `_window`. michael@0: * Method takes care of removing itself from 'unload' event listeners once michael@0: * event is being handled. michael@0: */ michael@0: _unloadListener: function _unloadListener(event) { michael@0: let window = this._window; michael@0: if (!event.target michael@0: || event.target.defaultView != window michael@0: || STATE_LOADED != window.document.readyState michael@0: ) return; michael@0: window.removeEventListener(ON_UNLOAD, this.__unloadListener, false); michael@0: this._onUnload(window); michael@0: }, michael@0: __unloadListener: null michael@0: }); michael@0: exports.WindowLoader = WindowLoader; michael@0: