aboutsummaryrefslogtreecommitdiff
path: root/tools/addon-sdk-1.12/lib/sdk/tabs
diff options
context:
space:
mode:
Diffstat (limited to 'tools/addon-sdk-1.12/lib/sdk/tabs')
-rw-r--r--tools/addon-sdk-1.12/lib/sdk/tabs/common.js23
-rw-r--r--tools/addon-sdk-1.12/lib/sdk/tabs/events.js30
-rw-r--r--tools/addon-sdk-1.12/lib/sdk/tabs/helpers.js17
-rw-r--r--tools/addon-sdk-1.12/lib/sdk/tabs/namespace.js9
-rw-r--r--tools/addon-sdk-1.12/lib/sdk/tabs/observer.js99
-rw-r--r--tools/addon-sdk-1.12/lib/sdk/tabs/tab-fennec.js141
-rw-r--r--tools/addon-sdk-1.12/lib/sdk/tabs/tab-firefox.js201
-rw-r--r--tools/addon-sdk-1.12/lib/sdk/tabs/tab.js21
-rw-r--r--tools/addon-sdk-1.12/lib/sdk/tabs/tabs-firefox.js26
-rw-r--r--tools/addon-sdk-1.12/lib/sdk/tabs/tabs.js21
-rw-r--r--tools/addon-sdk-1.12/lib/sdk/tabs/utils.js222
-rw-r--r--tools/addon-sdk-1.12/lib/sdk/tabs/worker.js17
12 files changed, 827 insertions, 0 deletions
diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/common.js b/tools/addon-sdk-1.12/lib/sdk/tabs/common.js
new file mode 100644
index 0000000..dd5bb22
--- /dev/null
+++ b/tools/addon-sdk-1.12/lib/sdk/tabs/common.js
@@ -0,0 +1,23 @@
+/* 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';
+
+const { validateOptions } = require('../deprecated/api-utils');
+
+function Options(options) {
+ if ('string' === typeof options)
+ options = { url: options };
+
+ return validateOptions(options, {
+ url: { is: ["string"] },
+ inBackground: { is: ["undefined", "boolean"] },
+ isPinned: { is: ["undefined", "boolean"] },
+ onOpen: { is: ["undefined", "function"] },
+ onClose: { is: ["undefined", "function"] },
+ onReady: { is: ["undefined", "function"] },
+ onActivate: { is: ["undefined", "function"] },
+ onDeactivate: { is: ["undefined", "function"] }
+ });
+}
+exports.Options = Options;
diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/events.js b/tools/addon-sdk-1.12/lib/sdk/tabs/events.js
new file mode 100644
index 0000000..eb99f40
--- /dev/null
+++ b/tools/addon-sdk-1.12/lib/sdk/tabs/events.js
@@ -0,0 +1,30 @@
+/* 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 ON_PREFIX = "on";
+const TAB_PREFIX = "Tab";
+
+const EVENTS = {
+ ready: "DOMContentLoaded",
+ open: "TabOpen",
+ close: "TabClose",
+ activate: "TabSelect",
+ deactivate: null,
+ pinned: "TabPinned",
+ unpinned: "TabUnpinned"
+}
+exports.EVENTS = EVENTS;
+
+Object.keys(EVENTS).forEach(function(name) {
+ EVENTS[name] = {
+ name: name,
+ listener: ON_PREFIX + name.charAt(0).toUpperCase() + name.substr(1),
+ dom: EVENTS[name]
+ }
+});
diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/helpers.js b/tools/addon-sdk-1.12/lib/sdk/tabs/helpers.js
new file mode 100644
index 0000000..aaff5cb
--- /dev/null
+++ b/tools/addon-sdk-1.12/lib/sdk/tabs/helpers.js
@@ -0,0 +1,17 @@
+/* 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';
+
+const { getTabForContentWindow } = require('./utils');
+const { Tab } = require('./tab');
+
+function getTabForWindow(win) {
+ let tab = getTabForContentWindow(win);
+ // We were unable to find the related tab!
+ if (!tab)
+ return null;
+
+ return Tab({ tab: tab });
+}
+exports.getTabForWindow = getTabForWindow;
diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/namespace.js b/tools/addon-sdk-1.12/lib/sdk/tabs/namespace.js
new file mode 100644
index 0000000..d359cee
--- /dev/null
+++ b/tools/addon-sdk-1.12/lib/sdk/tabs/namespace.js
@@ -0,0 +1,9 @@
+/* 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';
+
+let { ns } = require('../core/namespace');
+
+exports.tabsNS = ns();
+exports.tabNS = ns();
diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/observer.js b/tools/addon-sdk-1.12/lib/sdk/tabs/observer.js
new file mode 100644
index 0000000..19a048c
--- /dev/null
+++ b/tools/addon-sdk-1.12/lib/sdk/tabs/observer.js
@@ -0,0 +1,99 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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 { EventEmitterTrait: EventEmitter } = require("../deprecated/events");
+const { DOMEventAssembler } = require("../deprecated/events/assembler");
+const { Trait } = require("../deprecated/light-traits");
+const { getActiveTab, getTabs, getTabContainer } = require("./utils");
+const { browserWindowIterator } = require("../deprecated/window-utils");
+const { isBrowser } = require('../window/utils');
+const { observer: windowObserver } = require("../windows/observer");
+
+const EVENTS = {
+ "TabOpen": "open",
+ "TabClose": "close",
+ "TabSelect": "select",
+ "TabMove": "move",
+ "TabPinned": "pinned",
+ "TabUnpinned": "unpinned"
+};
+
+
+// Event emitter objects used to register listeners and emit events on them
+// when they occur.
+const observer = Trait.compose(DOMEventAssembler, EventEmitter).create({
+ /**
+ * Method is implemented by `EventEmitter` and is used just for emitting
+ * events on registered listeners.
+ */
+ _emit: Trait.required,
+ /**
+ * 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) {
+ this._emit(EVENTS[event.type], event.target, event);
+ }
+});
+
+// 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.
+var selectedTab = null;
+function onTabSelect(tab) {
+ if (selectedTab !== tab) {
+ if (selectedTab) observer._emit('deactivate', selectedTab);
+ if (tab) observer._emit('activate', selectedTab = tab);
+ }
+};
+observer.on('select', onTabSelect);
+
+// We also observe opening / closing windows in order to add / remove it's
+// containers to the observed list.
+function onWindowOpen(chromeWindow) {
+ if (!isBrowser(chromeWindow)) return; // Ignore if it's not a browser window.
+ observer.observe(getTabContainer(chromeWindow));
+}
+windowObserver.on("open", onWindowOpen);
+
+function onWindowClose(chromeWindow) {
+ if (!isBrowser(chromeWindow)) return; // Ignore if it's not a browser window.
+ // Bug 751546: Emit `deactivate` event on window close immediatly
+ // Otherwise we are going to face "dead object" exception on `select` event
+ if (getActiveTab(chromeWindow) == selectedTab) {
+ observer._emit("deactivate", selectedTab);
+ selectedTab = null;
+ }
+ observer.ignore(getTabContainer(chromeWindow));
+}
+windowObserver.on("close", onWindowClose);
+
+
+// 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", function onWindowActivate(chromeWindow) {
+ if (!isBrowser(chromeWindow)) return; // Ignore if it's not a browser window.
+ observer._emit("select", getActiveTab(chromeWindow));
+});
+
+// We should synchronize state, since probably we already have at least one
+// window open.
+for each (let window in browserWindowIterator()) onWindowOpen(window);
+
+exports.observer = observer;
diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/tab-fennec.js b/tools/addon-sdk-1.12/lib/sdk/tabs/tab-fennec.js
new file mode 100644
index 0000000..42bf91b
--- /dev/null
+++ b/tools/addon-sdk-1.12/lib/sdk/tabs/tab-fennec.js
@@ -0,0 +1,141 @@
+/* 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';
+
+const { Cc, Ci } = require('chrome');
+const { Class } = require('../core/heritage');
+const { tabNS } = require('./namespace');
+const { EventTarget } = require('../event/target');
+const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL,
+ setTabURL, getOwnerWindow, getTabContentType } = require('./utils');
+const { emit } = require('../event/core');
+const { when: unload } = require('../system/unload');
+
+const { EVENTS } = require('./events');
+const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec';
+
+const Tab = Class({
+ extends: EventTarget,
+ initialize: function initialize(options) {
+ options = options.tab ? options : { tab: options };
+
+ EventTarget.prototype.initialize.call(this, options);
+ let tabInternals = tabNS(this);
+
+ tabInternals.window = options.window || getOwnerWindow(options.tab);
+ tabInternals.tab = options.tab;
+ },
+
+ /**
+ * The title of the page currently loaded in the tab.
+ * Changing this property changes an actual title.
+ * @type {String}
+ */
+ get title() getTabTitle(tabNS(this).tab),
+ set title(title) setTabTitle(tabNS(this).tab, title),
+
+ /**
+ * Location of the page currently loaded in this tab.
+ * Changing this property will loads page under under the specified location.
+ * @type {String}
+ */
+ get url() getTabURL(tabNS(this).tab),
+ set url(url) setTabURL(tabNS(this).tab, url),
+
+ /**
+ * URI of the favicon for the page currently loaded in this tab.
+ * @type {String}
+ */
+ get favicon() {
+ // TODO: provide the real favicon when it is available
+ console.error(ERR_FENNEC_MSG);
+
+ // return 16x16 blank default
+ return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAEklEQVQ4jWNgGAWjYBSMAggAAAQQAAF/TXiOAAAAAElFTkSuQmCC';
+ },
+
+ getThumbnail: function() {
+ // TODO: implement!
+ console.error(ERR_FENNEC_MSG);
+
+ // return 80x45 blank default
+ return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAAtCAYAAAA5reyyAAAAJElEQVRoge3BAQEAAACCIP+vbkhAAQAAAAAAAAAAAAAAAADXBjhtAAGQ0AF/AAAAAElFTkSuQmCC';
+ },
+
+ /**
+ * The index of the tab relative to other tabs in the application window.
+ * Changing this property will change order of the actual position of the tab.
+ * @type {Number}
+ */
+ get index() {
+ let tabs = tabNS(this).window.BrowserApp.tabs;
+ let tab = tabNS(this).tab;
+ for (var i = tabs.length; i >= 0; i--) {
+ if (tabs[i] === tab)
+ return i;
+ }
+ return null;
+ },
+ set index(value) {
+ console.error(ERR_FENNEC_MSG); // TODO
+ },
+
+ /**
+ * Whether or not tab is pinned (Is an app-tab).
+ * @type {Boolean}
+ */
+ get isPinned() {
+ console.error(ERR_FENNEC_MSG); // TODO
+ return false; // TODO
+ },
+ pin: function pin() {
+ console.error(ERR_FENNEC_MSG); // TODO
+ },
+ unpin: function unpin() {
+ console.error(ERR_FENNEC_MSG); // TODO
+ },
+
+ /**
+ * Returns the MIME type that the document loaded in the tab is being
+ * rendered as.
+ * @type {String}
+ */
+ get contentType() getTabContentType(tabNS(this).tab),
+
+ /**
+ * Create a worker for this tab, first argument is options given to Worker.
+ * @type {Worker}
+ */
+ attach: function attach(options) {
+ // BUG 792946 https://bugzilla.mozilla.org/show_bug.cgi?id=792946
+ // TODO: fix this circular dependency
+ let { Worker } = require('./worker');
+ return Worker(options, tabNS(this).tab.browser.contentWindow);
+ },
+
+ /**
+ * Make this tab active.
+ */
+ activate: function activate() {
+ activateTab(tabNS(this).tab, tabNS(this).window);
+ },
+
+ /**
+ * Close the tab
+ */
+ close: function close(callback) {
+ if (callback)
+ this.once(EVENTS.close.name, callback);
+
+ closeTab(tabNS(this).tab);
+ },
+
+ /**
+ * Reload the tab
+ */
+ reload: function reload() {
+ tabNS(this).tab.browser.reload();
+ }
+});
+exports.Tab = Tab;
diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/tab-firefox.js b/tools/addon-sdk-1.12/lib/sdk/tabs/tab-firefox.js
new file mode 100644
index 0000000..5a36302
--- /dev/null
+++ b/tools/addon-sdk-1.12/lib/sdk/tabs/tab-firefox.js
@@ -0,0 +1,201 @@
+/* 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';
+
+const { Trait } = require("../deprecated/traits");
+const { EventEmitter } = require("../deprecated/events");
+const { defer } = require("../lang/functional");
+const { EVENTS } = require("./events");
+const { getThumbnailURIForWindow } = require("../content/thumbnail");
+const { getFaviconURIForLocation } = require("../io/data");
+const { activateTab, getOwnerWindow, getBrowserForTab, getTabTitle, setTabTitle,
+ getTabURL, setTabURL, getTabContentType } = require('./utils');
+
+// Array of the inner instances of all the wrapped tabs.
+const TABS = [];
+
+/**
+ * Trait used to create tab wrappers.
+ */
+const TabTrait = Trait.compose(EventEmitter, {
+ on: Trait.required,
+ _emit: Trait.required,
+ /**
+ * Tab DOM element that is being wrapped.
+ */
+ _tab: null,
+ /**
+ * Window wrapper whose tab this object represents.
+ */
+ window: null,
+ constructor: function Tab(options) {
+ this._onReady = this._onReady.bind(this);
+ this._tab = options.tab;
+ let window = this.window = options.window || getOwnerWindow(this._tab);
+
+ // Setting event listener if was passed.
+ for each (let type in EVENTS) {
+ let listener = options[type.listener];
+ if (listener)
+ this.on(type.name, options[type.listener]);
+ if ('ready' != type.name) // window spreads this event.
+ window.tabs.on(type.name, this._onEvent.bind(this, type.name));
+ }
+
+ this.on(EVENTS.close.name, this.destroy.bind(this));
+ this._browser.addEventListener(EVENTS.ready.dom, this._onReady, true);
+
+ if (options.isPinned)
+ this.pin();
+
+ // Since we will have to identify tabs by a DOM elements facade function
+ // is used as constructor that collects all the instances and makes sure
+ // that they more then one wrapper is not created per tab.
+ return this;
+ },
+ destroy: function destroy() {
+ this._removeAllListeners();
+ this._browser.removeEventListener(EVENTS.ready.dom, this._onReady, true);
+ },
+
+ /**
+ * Internal listener that emits public event 'ready' when the page of this
+ * tab is loaded.
+ */
+ _onReady: function _onReady(event) {
+ // IFrames events will bubble so we need to ignore those.
+ if (event.target == this._contentDocument)
+ this._emit(EVENTS.ready.name, this._public);
+ },
+ /**
+ * Internal tab event router. Window will emit tab related events for all it's
+ * tabs, this listener will propagate all the events for this tab to it's
+ * listeners.
+ */
+ _onEvent: function _onEvent(type, tab) {
+ if (tab == this._public)
+ this._emit(type, tab);
+ },
+ /**
+ * Browser DOM element where page of this tab is currently loaded.
+ */
+ get _browser() getBrowserForTab(this._tab),
+ /**
+ * Window DOM element containing this tab.
+ */
+ get _window() getOwnerWindow(this._tab),
+ /**
+ * Document object of the page that is currently loaded in this tab.
+ */
+ get _contentDocument() this._browser.contentDocument,
+ /**
+ * Window object of the page that is currently loaded in this tab.
+ */
+ get _contentWindow() this._browser.contentWindow,
+
+ /**
+ * The title of the page currently loaded in the tab.
+ * Changing this property changes an actual title.
+ * @type {String}
+ */
+ get title() getTabTitle(this._tab),
+ set title(title) setTabTitle(this._tab, title),
+
+ /**
+ * Returns the MIME type that the document loaded in the tab is being
+ * rendered as.
+ * @type {String}
+ */
+ get contentType() getTabContentType(this._tab),
+
+ /**
+ * Location of the page currently loaded in this tab.
+ * Changing this property will loads page under under the specified location.
+ * @type {String}
+ */
+ get url() getTabURL(this._tab),
+ set url(url) setTabURL(this._tab, url),
+ /**
+ * URI of the favicon for the page currently loaded in this tab.
+ * @type {String}
+ */
+ get favicon() getFaviconURIForLocation(this.url),
+ /**
+ * The CSS style for the tab
+ */
+ get style() null, // TODO
+ /**
+ * The index of the tab relative to other tabs in the application window.
+ * Changing this property will change order of the actual position of the tab.
+ * @type {Number}
+ */
+ get index()
+ this._window.gBrowser.getBrowserIndexForDocument(this._contentDocument),
+ set index(value) this._window.gBrowser.moveTabTo(this._tab, value),
+ /**
+ * Thumbnail data URI of the page currently loaded in this tab.
+ * @type {String}
+ */
+ getThumbnail: function getThumbnail()
+ getThumbnailURIForWindow(this._contentWindow),
+ /**
+ * Whether or not tab is pinned (Is an app-tab).
+ * @type {Boolean}
+ */
+ get isPinned() this._tab.pinned,
+ pin: function pin() {
+ this._window.gBrowser.pinTab(this._tab);
+ },
+ unpin: function unpin() {
+ this._window.gBrowser.unpinTab(this._tab);
+ },
+
+ /**
+ * Create a worker for this tab, first argument is options given to Worker.
+ * @type {Worker}
+ */
+ attach: function attach(options) {
+ // BUG 792946 https://bugzilla.mozilla.org/show_bug.cgi?id=792946
+ // TODO: fix this circular dependency
+ let { Worker } = require('./worker');
+ return Worker(options, this._contentWindow);
+ },
+
+ /**
+ * Make this tab active.
+ * Please note: That this function is called asynchronous since in E10S that
+ * will be the case. Besides this function is called from a constructor where
+ * we would like to return instance before firing a 'TabActivated' event.
+ */
+ activate: defer(function activate() {
+ activateTab(this._tab);
+ }),
+ /**
+ * Close the tab
+ */
+ close: function close(callback) {
+ if (callback)
+ this.once(EVENTS.close.name, callback);
+ this._window.gBrowser.removeTab(this._tab);
+ },
+ /**
+ * Reload the tab
+ */
+ reload: function reload() {
+ this._window.gBrowser.reloadTab(this._tab);
+ }
+});
+
+function Tab(options) {
+ let chromeTab = options.tab;
+ for each (let tab in TABS) {
+ if (chromeTab == tab._tab)
+ return tab._public;
+ }
+ let tab = TabTrait(options);
+ TABS.push(tab);
+ return tab._public;
+}
+Tab.prototype = TabTrait.prototype;
+exports.Tab = Tab;
diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/tab.js b/tools/addon-sdk-1.12/lib/sdk/tabs/tab.js
new file mode 100644
index 0000000..420baee
--- /dev/null
+++ b/tools/addon-sdk-1.12/lib/sdk/tabs/tab.js
@@ -0,0 +1,21 @@
+/* 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'
+};
+
+if (require('../system/xul-app').is('Firefox')) {
+ module.exports = require('./tab-firefox');
+}
+else if (require('../system/xul-app').is('Fennec')) {
+ module.exports = require('./tab-fennec');
+}
+else {
+ throw new Error([
+ 'The tabs module currently supports only Firefox & Fennec. In the future',
+ ' we would like it to support other applications, however.'
+ ].join(''));
+}
diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/tabs-firefox.js b/tools/addon-sdk-1.12/lib/sdk/tabs/tabs-firefox.js
new file mode 100644
index 0000000..92834c2
--- /dev/null
+++ b/tools/addon-sdk-1.12/lib/sdk/tabs/tabs-firefox.js
@@ -0,0 +1,26 @@
+/* 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';
+
+// TODO: BUG 792670 - remove dependency below
+const { browserWindows } = require('../windows');
+const { tabs } = require('../windows/tabs-firefox');
+
+Object.defineProperties(tabs, {
+ open: { value: function open(options) {
+ if (options.inNewWindow)
+ // `tabs` option is under review and may be removed.
+ return browserWindows.open({ tabs: [ options ] });
+ // Open in active window if new window was not required.
+ return browserWindows.activeWindow.tabs.open(options);
+ }}
+});
+
+// Workaround for bug 674195. Freezing objects from other compartments fail,
+// so we use `Object.freeze` from the same component as objects
+// `hasOwnProperty`. Since `hasOwnProperty` here will be from other component
+// we override it to support our workaround.
+module.exports = Object.create(tabs, {
+ isPrototypeOf: { value: Object.prototype.isPrototypeOf }
+});
diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/tabs.js b/tools/addon-sdk-1.12/lib/sdk/tabs/tabs.js
new file mode 100644
index 0000000..fcb3868
--- /dev/null
+++ b/tools/addon-sdk-1.12/lib/sdk/tabs/tabs.js
@@ -0,0 +1,21 @@
+/* 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'
+};
+
+if (require('../system/xul-app').is('Firefox')) {
+ module.exports = require('./tabs-firefox');
+}
+else if (require('../system/xul-app').is('Fennec')) {
+ module.exports = require('../windows/tabs-fennec').tabs;
+}
+else {
+ throw new Error([
+ 'The tabs module currently supports only Firefox & Fennec. In the future',
+ ' we would like it to support other applications, however.'
+ ].join(''));
+}
diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/utils.js b/tools/addon-sdk-1.12/lib/sdk/tabs/utils.js
new file mode 100644
index 0000000..d923e4b
--- /dev/null
+++ b/tools/addon-sdk-1.12/lib/sdk/tabs/utils.js
@@ -0,0 +1,222 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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 { defer } = require("../lang/functional");
+const { windows } = require('../window/utils');
+const { Ci } = require('chrome');
+
+function activateTab(tab, window) {
+ let gBrowser = getTabBrowserForTab(tab);
+
+ // normal case
+ if (gBrowser) {
+ gBrowser.selectedTab = tab;
+ }
+ // fennec ?
+ else if (window && window.BrowserApp) {
+ window.BrowserApp.selectTab(tab);
+ }
+ return null;
+}
+exports.activateTab = activateTab;
+
+function getTabBrowser(window) {
+ return window.gBrowser;
+}
+exports.getTabBrowser = getTabBrowser;
+
+function getTabContainer(window) {
+ return getTabBrowser(window).tabContainer;
+}
+exports.getTabContainer = getTabContainer;
+
+function getTabs(window) {
+ // fennec
+ if (window.BrowserApp)
+ return window.BrowserApp.tabs;
+
+ // firefox - default
+ return Array.slice(getTabContainer(window).children);
+}
+exports.getTabs = getTabs;
+
+function getActiveTab(window) {
+ return window.gBrowser.selectedTab;
+}
+exports.getActiveTab = getActiveTab;
+
+function getOwnerWindow(tab) {
+ // normal case
+ if (tab.ownerDocument)
+ return tab.ownerDocument.defaultView;
+
+ // try fennec case
+ return getWindowHoldingTab(tab);
+}
+exports.getOwnerWindow = getOwnerWindow;
+
+// fennec
+function getWindowHoldingTab(rawTab) {
+ for each (let window in windows()) {
+ // this function may be called when not using fennec
+ if (!window.BrowserApp)
+ continue;
+
+ for each (let tab in window.BrowserApp.tabs) {
+ if (tab === rawTab)
+ return window;
+ }
+ }
+
+ return null;
+}
+
+function openTab(window, url, options) {
+ options = options || {};
+
+ // fennec?
+ if (window.BrowserApp) {
+ return window.BrowserApp.addTab(url, {
+ selected: options.inBackground ? false : true,
+ pinned: options.isPinned || false
+ });
+ }
+ return window.gBrowser.addTab(url);
+};
+exports.openTab = openTab;
+
+function isTabOpen(tab) {
+ // try normal case then fennec case
+ return !!((tab.linkedBrowser) || getWindowHoldingTab(tab));
+}
+exports.isTabOpen = isTabOpen;
+
+function closeTab(tab) {
+ let gBrowser = getTabBrowserForTab(tab);
+ // normal case?
+ if (gBrowser)
+ return gBrowser.removeTab(tab);
+
+ let window = getWindowHoldingTab(tab);
+ // fennec?
+ if (window && window.BrowserApp)
+ return window.BrowserApp.closeTab(tab);
+ return null;
+}
+exports.closeTab = closeTab;
+
+function getURI(tab) {
+ if (tab.browser) // fennec
+ return tab.browser.currentURI.spec;
+ return tab.linkedBrowser.currentURI.spec;
+}
+exports.getURI = getURI;
+
+function getTabBrowserForTab(tab) {
+ let outerWin = getOwnerWindow(tab);
+ if (outerWin)
+ return getOwnerWindow(tab).gBrowser;
+ return null;
+}
+exports.getTabBrowserForTab = getTabBrowserForTab;
+
+function getBrowserForTab(tab) {
+ if (tab.browser) // fennec
+ return tab.browser;
+
+ return tab.linkedBrowser;
+}
+exports.getBrowserForTab = getBrowserForTab;
+
+function getTabTitle(tab) {
+ return getBrowserForTab(tab).contentDocument.title || tab.label || "";
+}
+exports.getTabTitle = getTabTitle;
+
+function setTabTitle(tab, title) {
+ title = String(title);
+ if (tab.browser)
+ tab.browser.contentDocument.title = title;
+ tab.label = String(title);
+}
+exports.setTabTitle = setTabTitle;
+
+function getTabContentWindow(tab) {
+ return getBrowserForTab(tab).contentWindow;
+}
+exports.getTabContentWindow = getTabContentWindow;
+
+function getTabForContentWindow(window) {
+ // Retrieve the topmost frame container. It can be either <xul:browser>,
+ // <xul:iframe/> or <html:iframe/>. But in our case, it should be xul:browser.
+ let browser = window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell)
+ .chromeEventHandler;
+ // Is null for toplevel documents
+ if (!browser)
+ return false;
+ // Retrieve the owner window, should be browser.xul one
+ let chromeWindow = browser.ownerDocument.defaultView;
+
+ // Ensure that it is top-level browser window.
+ // We need extra checks because of Mac hidden window that has a broken
+ // `gBrowser` global attribute.
+ if ('gBrowser' in chromeWindow && chromeWindow.gBrowser &&
+ 'browsers' in chromeWindow.gBrowser) {
+ // Looks like we are on Firefox Desktop
+ // Then search for the position in tabbrowser in order to get the tab object
+ let browsers = chromeWindow.gBrowser.browsers;
+ let i = browsers.indexOf(browser);
+ if (i !== -1)
+ return chromeWindow.gBrowser.tabs[i];
+ return null;
+ }
+ else if ('BrowserApp' in chromeWindow) {
+ // Looks like we are on Firefox Mobile
+ return chromeWindow.BrowserApp.getTabForWindow(window)
+ }
+
+ return null;
+}
+exports.getTabForContentWindow = getTabForContentWindow;
+
+function getTabURL(tab) {
+ if (tab.browser) // fennec
+ return String(tab.browser.currentURI.spec);
+ return String(getBrowserForTab(tab).currentURI.spec);
+}
+exports.getTabURL = getTabURL;
+
+function setTabURL(tab, url) {
+ url = String(url);
+ if (tab.browser)
+ return tab.browser.loadURI(url);
+ return getBrowserForTab(tab).loadURI(url);
+}
+// "TabOpen" event is fired when it's still "about:blank" is loaded in the
+// changing `location` property of the `contentDocument` has no effect since
+// seems to be either ignored or overridden by internal listener, there for
+// location change is enqueued for the next turn of event loop.
+exports.setTabURL = defer(setTabURL);
+
+function getTabContentType(tab) {
+ return getBrowserForTab(tab).contentDocument.contentType;
+}
+exports.getTabContentType = getTabContentType;
+
+function getSelectedTab(window) {
+ if (window.BrowserApp) // fennec?
+ return window.BrowserApp.selectedTab;
+ if (window.gBrowser)
+ return window.gBrowser.selectedTab;
+ return null;
+}
+exports.getSelectedTab = getSelectedTab;
diff --git a/tools/addon-sdk-1.12/lib/sdk/tabs/worker.js b/tools/addon-sdk-1.12/lib/sdk/tabs/worker.js
new file mode 100644
index 0000000..d2ba336
--- /dev/null
+++ b/tools/addon-sdk-1.12/lib/sdk/tabs/worker.js
@@ -0,0 +1,17 @@
+/* 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';
+
+const ContentWorker = require('../content/worker').Worker;
+
+function Worker(options, window) {
+ options.window = window;
+
+ let worker = ContentWorker(options);
+ worker.once("detach", function detach() {
+ worker.destroy();
+ });
+ return worker;
+}
+exports.Worker = Worker; \ No newline at end of file