diff options
Diffstat (limited to 'toolkit/jetpack/sdk/tabs/observer.js')
-rw-r--r-- | toolkit/jetpack/sdk/tabs/observer.js | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/toolkit/jetpack/sdk/tabs/observer.js b/toolkit/jetpack/sdk/tabs/observer.js new file mode 100644 index 000000000..4e935cd62 --- /dev/null +++ b/toolkit/jetpack/sdk/tabs/observer.js @@ -0,0 +1,113 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +module.metadata = { + "stability": "unstable" +}; + +const { EventTarget } = require("../event/target"); +const { emit } = require("../event/core"); +const { DOMEventAssembler } = require("../deprecated/events/assembler"); +const { Class } = require("../core/heritage"); +const { getActiveTab, getTabs } = require("./utils"); +const { browserWindowIterator } = require("../deprecated/window-utils"); +const { isBrowser, windows, getMostRecentBrowserWindow } = require("../window/utils"); +const { observer: windowObserver } = require("../windows/observer"); +const { when } = require("../system/unload"); + +const EVENTS = { + "TabOpen": "open", + "TabClose": "close", + "TabSelect": "select", + "TabMove": "move", + "TabPinned": "pinned", + "TabUnpinned": "unpinned" +}; + +const selectedTab = Symbol("observer/state/selectedTab"); + +// Event emitter objects used to register listeners and emit events on them +// when they occur. +const Observer = Class({ + implements: [EventTarget, DOMEventAssembler], + initialize() { + this[selectedTab] = null; + // Currently Gecko does not dispatch any event on the previously selected + // tab before / after "TabSelect" is dispatched. In order to work around this + // limitation we keep track of selected tab and emit "deactivate" event with + // that before emitting "activate" on selected tab. + this.on("select", tab => { + const selected = this[selectedTab]; + if (selected !== tab) { + if (selected) { + emit(this, 'deactivate', selected); + } + + if (tab) { + this[selectedTab] = tab; + emit(this, 'activate', this[selectedTab]); + } + } + }); + + + // We also observe opening / closing windows in order to add / remove it's + // containers to the observed list. + windowObserver.on("open", chromeWindow => { + if (isBrowser(chromeWindow)) { + this.observe(chromeWindow); + } + }); + + windowObserver.on("close", chromeWindow => { + if (isBrowser(chromeWindow)) { + // Bug 751546: Emit `deactivate` event on window close immediatly + // Otherwise we are going to face "dead object" exception on `select` event + if (getActiveTab(chromeWindow) === this[selectedTab]) { + emit(this, "deactivate", this[selectedTab]); + this[selectedTab] = null; + } + this.ignore(chromeWindow); + } + }); + + + // Currently gecko does not dispatches "TabSelect" events when different + // window gets activated. To work around this limitation we emulate "select" + // event for this case. + windowObserver.on("activate", chromeWindow => { + if (isBrowser(chromeWindow)) { + emit(this, "select", getActiveTab(chromeWindow)); + } + }); + + // We should synchronize state, since probably we already have at least one + // window open. + for (let chromeWindow of browserWindowIterator()) { + this.observe(chromeWindow); + } + + when(_ => { + // Don't dispatch a deactivate event during unload. + this[selectedTab] = null; + }); + }, + /** + * Events that are supported and emitted by the module. + */ + supportedEventsTypes: Object.keys(EVENTS), + /** + * Function handles all the supported events on all the windows that are + * observed. Method is used to proxy events to the listeners registered on + * this event emitter. + * @param {Event} event + * Keyboard event being emitted. + */ + handleEvent: function handleEvent(event) { + emit(this, EVENTS[event.type], event.target, event); + } +}); + +exports.observer = new Observer(); |