mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-21 06:37:30 +09:00
Move Add-on SDK source to toolkit/jetpack
This commit is contained in:
parent
0a03067ee3
commit
23b67db438
993 changed files with 484 additions and 100663 deletions
34
toolkit/jetpack/sdk/tabs/common.js
Normal file
34
toolkit/jetpack/sdk/tabs/common.js
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/* 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");
|
||||
const { data } = require("../self");
|
||||
|
||||
function Options(options) {
|
||||
if ('string' === typeof options)
|
||||
options = { url: options };
|
||||
|
||||
return validateOptions(options, {
|
||||
url: {
|
||||
is: ["string"],
|
||||
map: (v) => v ? data.url(v) : v
|
||||
},
|
||||
inBackground: {
|
||||
map: Boolean,
|
||||
is: ["undefined", "boolean"]
|
||||
},
|
||||
isPinned: { is: ["undefined", "boolean"] },
|
||||
isPrivate: { is: ["undefined", "boolean"] },
|
||||
inNewWindow: { is: ["undefined", "boolean"] },
|
||||
onOpen: { is: ["undefined", "function"] },
|
||||
onClose: { is: ["undefined", "function"] },
|
||||
onReady: { is: ["undefined", "function"] },
|
||||
onLoad: { is: ["undefined", "function"] },
|
||||
onPageShow: { is: ["undefined", "function"] },
|
||||
onActivate: { is: ["undefined", "function"] },
|
||||
onDeactivate: { is: ["undefined", "function"] }
|
||||
});
|
||||
}
|
||||
exports.Options = Options;
|
||||
39
toolkit/jetpack/sdk/tabs/events.js
Normal file
39
toolkit/jetpack/sdk/tabs/events.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/* 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",
|
||||
load: "load", // Used for non-HTML content
|
||||
pageshow: "pageshow", // Used for cached content
|
||||
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: createListenerName(name),
|
||||
dom: EVENTS[name]
|
||||
}
|
||||
});
|
||||
|
||||
function createListenerName (name) {
|
||||
if (name === 'pageshow')
|
||||
return 'onPageShow';
|
||||
else
|
||||
return ON_PREFIX + name.charAt(0).toUpperCase() + name.substr(1);
|
||||
}
|
||||
22
toolkit/jetpack/sdk/tabs/helpers.js
Normal file
22
toolkit/jetpack/sdk/tabs/helpers.js
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/* 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'
|
||||
};
|
||||
|
||||
|
||||
// NOTE: This file should only export Tab instances
|
||||
|
||||
|
||||
const { getTabForBrowser: getRawTabForBrowser } = require('./utils');
|
||||
const { modelFor } = require('../model/core');
|
||||
|
||||
exports.getTabForRawTab = modelFor;
|
||||
|
||||
function getTabForBrowser(browser) {
|
||||
return modelFor(getRawTabForBrowser(browser)) || null;
|
||||
}
|
||||
exports.getTabForBrowser = getTabForBrowser;
|
||||
10
toolkit/jetpack/sdk/tabs/namespace.js
Normal file
10
toolkit/jetpack/sdk/tabs/namespace.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
/* 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';
|
||||
|
||||
var { ns } = require('../core/namespace');
|
||||
|
||||
exports.tabsNS = ns();
|
||||
exports.tabNS = ns();
|
||||
exports.rawTabNS = ns();
|
||||
113
toolkit/jetpack/sdk/tabs/observer.js
Normal file
113
toolkit/jetpack/sdk/tabs/observer.js
Normal file
|
|
@ -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();
|
||||
249
toolkit/jetpack/sdk/tabs/tab-fennec.js
Normal file
249
toolkit/jetpack/sdk/tabs/tab-fennec.js
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
/* 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, rawTabNS } = require('./namespace');
|
||||
const { EventTarget } = require('../event/target');
|
||||
const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL,
|
||||
getTabContentWindow, getTabForBrowser, setTabURL, getOwnerWindow,
|
||||
getTabContentDocument, getTabContentType, getTabId, isTab } = require('./utils');
|
||||
const { emit } = require('../event/core');
|
||||
const { isPrivate } = require('../private-browsing/utils');
|
||||
const { isWindowPrivate } = require('../window/utils');
|
||||
const { when: unload } = require('../system/unload');
|
||||
const { BLANK } = require('../content/thumbnail');
|
||||
const { viewFor } = require('../view/core');
|
||||
const { EVENTS } = require('./events');
|
||||
const { modelFor } = require('../model/core');
|
||||
|
||||
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 };
|
||||
let tab = options.tab;
|
||||
|
||||
EventTarget.prototype.initialize.call(this, options);
|
||||
let tabInternals = tabNS(this);
|
||||
rawTabNS(tab).tab = this;
|
||||
|
||||
let window = tabInternals.window = options.window || getOwnerWindow(tab);
|
||||
tabInternals.tab = tab;
|
||||
|
||||
// TabReady
|
||||
let onReady = tabInternals.onReady = onTabReady.bind(this);
|
||||
tab.browser.addEventListener(EVENTS.ready.dom, onReady, false);
|
||||
|
||||
// TabPageShow
|
||||
let onPageShow = tabInternals.onPageShow = onTabPageShow.bind(this);
|
||||
tab.browser.addEventListener(EVENTS.pageshow.dom, onPageShow, false);
|
||||
|
||||
// TabLoad
|
||||
let onLoad = tabInternals.onLoad = onTabLoad.bind(this);
|
||||
tab.browser.addEventListener(EVENTS.load.dom, onLoad, true);
|
||||
|
||||
// TabClose
|
||||
let onClose = tabInternals.onClose = onTabClose.bind(this);
|
||||
window.BrowserApp.deck.addEventListener(EVENTS.close.dom, onClose, false);
|
||||
|
||||
unload(cleanupTab.bind(null, this));
|
||||
},
|
||||
|
||||
/**
|
||||
* The title of the page currently loaded in the tab.
|
||||
* Changing this property changes an actual title.
|
||||
* @type {String}
|
||||
*/
|
||||
get title() {
|
||||
return 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() {
|
||||
return tabNS(this).closed ? undefined : getTabURL(tabNS(this).tab);
|
||||
},
|
||||
set url(url) {
|
||||
setTabURL(tabNS(this).tab, url);
|
||||
},
|
||||
|
||||
getThumbnail: function() {
|
||||
// TODO: implement!
|
||||
console.error(ERR_FENNEC_MSG);
|
||||
|
||||
// return 80x45 blank default
|
||||
return BLANK;
|
||||
},
|
||||
|
||||
/**
|
||||
* tab's document readyState, or 'uninitialized' if it doesn't even exist yet.
|
||||
*/
|
||||
get readyState() {
|
||||
let doc = getTabContentDocument(tabNS(this).tab);
|
||||
return doc && doc.readyState || 'uninitialized';
|
||||
},
|
||||
|
||||
get id() {
|
||||
return getTabId(tabNS(this).tab);
|
||||
},
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
if (tabNS(this).closed) return undefined;
|
||||
|
||||
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() {
|
||||
return 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, getTabContentWindow(tabNS(this).tab));
|
||||
},
|
||||
|
||||
/**
|
||||
* Make this tab active.
|
||||
*/
|
||||
activate: function activate() {
|
||||
activateTab(tabNS(this).tab, tabNS(this).window);
|
||||
},
|
||||
|
||||
/**
|
||||
* Close the tab
|
||||
*/
|
||||
close: function close(callback) {
|
||||
let tab = this;
|
||||
this.once(EVENTS.close.name, function () {
|
||||
tabNS(tab).closed = true;
|
||||
if (callback) callback();
|
||||
});
|
||||
|
||||
closeTab(tabNS(this).tab);
|
||||
},
|
||||
|
||||
/**
|
||||
* Reload the tab
|
||||
*/
|
||||
reload: function reload() {
|
||||
tabNS(this).tab.browser.reload();
|
||||
}
|
||||
});
|
||||
exports.Tab = Tab;
|
||||
|
||||
// Implement `viewFor` polymorphic function for the Tab
|
||||
// instances.
|
||||
viewFor.define(Tab, x => tabNS(x).tab);
|
||||
|
||||
function cleanupTab(tab) {
|
||||
let tabInternals = tabNS(tab);
|
||||
if (!tabInternals.tab)
|
||||
return;
|
||||
|
||||
if (tabInternals.tab.browser) {
|
||||
tabInternals.tab.browser.removeEventListener(EVENTS.ready.dom, tabInternals.onReady, false);
|
||||
tabInternals.tab.browser.removeEventListener(EVENTS.pageshow.dom, tabInternals.onPageShow, false);
|
||||
tabInternals.tab.browser.removeEventListener(EVENTS.load.dom, tabInternals.onLoad, true);
|
||||
}
|
||||
tabInternals.onReady = null;
|
||||
tabInternals.onPageShow = null;
|
||||
tabInternals.onLoad = null;
|
||||
tabInternals.window.BrowserApp.deck.removeEventListener(EVENTS.close.dom, tabInternals.onClose, false);
|
||||
tabInternals.onClose = null;
|
||||
rawTabNS(tabInternals.tab).tab = null;
|
||||
tabInternals.tab = null;
|
||||
tabInternals.window = null;
|
||||
}
|
||||
|
||||
function onTabReady(event) {
|
||||
let win = event.target.defaultView;
|
||||
|
||||
// ignore frames
|
||||
if (win === win.top) {
|
||||
emit(this, 'ready', this);
|
||||
}
|
||||
}
|
||||
|
||||
function onTabLoad (event) {
|
||||
let win = event.target.defaultView;
|
||||
|
||||
// ignore frames
|
||||
if (win === win.top) {
|
||||
emit(this, 'load', this);
|
||||
}
|
||||
}
|
||||
|
||||
function onTabPageShow(event) {
|
||||
let win = event.target.defaultView;
|
||||
if (win === win.top)
|
||||
emit(this, 'pageshow', this, event.persisted);
|
||||
}
|
||||
|
||||
// TabClose
|
||||
function onTabClose(event) {
|
||||
let rawTab = getTabForBrowser(event.target);
|
||||
if (tabNS(this).tab !== rawTab)
|
||||
return;
|
||||
|
||||
emit(this, EVENTS.close.name, this);
|
||||
cleanupTab(this);
|
||||
};
|
||||
|
||||
isPrivate.implement(Tab, tab => {
|
||||
return isWindowPrivate(getTabContentWindow(tabNS(tab).tab));
|
||||
});
|
||||
|
||||
// Implement `modelFor` function for the Tab instances.
|
||||
modelFor.when(isTab, rawTab => {
|
||||
return rawTabNS(rawTab).tab;
|
||||
});
|
||||
353
toolkit/jetpack/sdk/tabs/tab-firefox.js
Normal file
353
toolkit/jetpack/sdk/tabs/tab-firefox.js
Normal file
|
|
@ -0,0 +1,353 @@
|
|||
/* 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 { Class } = require('../core/heritage');
|
||||
const { observer } = require('./observer');
|
||||
const { observer: windowObserver } = require('../windows/observer');
|
||||
const { addListItem, removeListItem } = require('../util/list');
|
||||
const { viewFor } = require('../view/core');
|
||||
const { modelFor } = require('../model/core');
|
||||
const { emit, setListeners } = require('../event/core');
|
||||
const { EventTarget } = require('../event/target');
|
||||
const { getBrowserForTab, setTabURL, getTabId, getTabURL, getTabForBrowser,
|
||||
getTabs, getTabTitle, setTabTitle, getIndex, closeTab, reload, move,
|
||||
activateTab, pin, unpin, isTab } = require('./utils');
|
||||
const { isBrowser, getInnerId, isWindowPrivate } = require('../window/utils');
|
||||
const { getThumbnailURIForWindow, BLANK } = require("../content/thumbnail");
|
||||
const { when } = require('../system/unload');
|
||||
const { ignoreWindow, isPrivate } = require('../private-browsing/utils')
|
||||
const { defer } = require('../lang/functional');
|
||||
const { getURL } = require('../url/utils');
|
||||
const { frames, remoteRequire } = require('../remote/parent');
|
||||
remoteRequire('sdk/content/tab-events');
|
||||
|
||||
const modelsFor = new WeakMap();
|
||||
const viewsFor = new WeakMap();
|
||||
const destroyed = new WeakMap();
|
||||
|
||||
const tabEvents = {};
|
||||
exports.tabEvents = tabEvents;
|
||||
|
||||
function browser(tab) {
|
||||
return getBrowserForTab(viewsFor.get(tab));
|
||||
}
|
||||
|
||||
function isDestroyed(tab) {
|
||||
return destroyed.has(tab);
|
||||
}
|
||||
|
||||
function isClosed(tab) {
|
||||
if (!viewsFor.has(tab))
|
||||
return true;
|
||||
return viewsFor.get(tab).closing;
|
||||
}
|
||||
|
||||
// private tab attribute where the remote cached value is stored
|
||||
const remoteReadyStateCached = Symbol("remoteReadyStateCached");
|
||||
|
||||
const Tab = Class({
|
||||
implements: [EventTarget],
|
||||
initialize: function(tabElement, options = null) {
|
||||
modelsFor.set(tabElement, this);
|
||||
viewsFor.set(this, tabElement);
|
||||
|
||||
if (options) {
|
||||
EventTarget.prototype.initialize.call(this, options);
|
||||
|
||||
if (options.isPinned)
|
||||
this.pin();
|
||||
|
||||
// Note that activate is defered and so will run after any open event
|
||||
// is sent out
|
||||
if (!options.inBackground)
|
||||
this.activate();
|
||||
}
|
||||
|
||||
getURL.implement(this, tab => tab.url);
|
||||
isPrivate.implement(this, tab => {
|
||||
return isWindowPrivate(viewsFor.get(tab).ownerDocument.defaultView);
|
||||
});
|
||||
},
|
||||
|
||||
get id() {
|
||||
return isDestroyed(this) ? undefined : getTabId(viewsFor.get(this));
|
||||
},
|
||||
|
||||
get title() {
|
||||
return isDestroyed(this) ? undefined : getTabTitle(viewsFor.get(this));
|
||||
},
|
||||
|
||||
set title(val) {
|
||||
if (isDestroyed(this))
|
||||
return;
|
||||
|
||||
setTabTitle(viewsFor.get(this), val);
|
||||
},
|
||||
|
||||
get url() {
|
||||
return isDestroyed(this) ? undefined : getTabURL(viewsFor.get(this));
|
||||
},
|
||||
|
||||
set url(val) {
|
||||
if (isDestroyed(this))
|
||||
return;
|
||||
|
||||
setTabURL(viewsFor.get(this), val);
|
||||
},
|
||||
|
||||
get contentType() {
|
||||
return isDestroyed(this) ? undefined : browser(this).documentContentType;
|
||||
},
|
||||
|
||||
get index() {
|
||||
return isDestroyed(this) ? undefined : getIndex(viewsFor.get(this));
|
||||
},
|
||||
|
||||
set index(val) {
|
||||
if (isDestroyed(this))
|
||||
return;
|
||||
|
||||
move(viewsFor.get(this), val);
|
||||
},
|
||||
|
||||
get isPinned() {
|
||||
return isDestroyed(this) ? undefined : viewsFor.get(this).pinned;
|
||||
},
|
||||
|
||||
get window() {
|
||||
if (isClosed(this))
|
||||
return undefined;
|
||||
|
||||
// TODO: Remove the dependency on the windows module, see bug 792670
|
||||
require('../windows');
|
||||
let tabElement = viewsFor.get(this);
|
||||
let domWindow = tabElement.ownerDocument.defaultView;
|
||||
return modelFor(domWindow);
|
||||
},
|
||||
|
||||
get readyState() {
|
||||
return isDestroyed(this) ? undefined : this[remoteReadyStateCached] || "uninitialized";
|
||||
},
|
||||
|
||||
pin: function() {
|
||||
if (isDestroyed(this))
|
||||
return;
|
||||
|
||||
pin(viewsFor.get(this));
|
||||
},
|
||||
|
||||
unpin: function() {
|
||||
if (isDestroyed(this))
|
||||
return;
|
||||
|
||||
unpin(viewsFor.get(this));
|
||||
},
|
||||
|
||||
close: function(callback) {
|
||||
let tabElement = viewsFor.get(this);
|
||||
|
||||
if (isDestroyed(this) || !tabElement || !tabElement.parentNode) {
|
||||
if (callback)
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
this.once('close', () => {
|
||||
this.destroy();
|
||||
if (callback)
|
||||
callback();
|
||||
});
|
||||
|
||||
closeTab(tabElement);
|
||||
},
|
||||
|
||||
reload: function() {
|
||||
if (isDestroyed(this))
|
||||
return;
|
||||
|
||||
reload(viewsFor.get(this));
|
||||
},
|
||||
|
||||
activate: defer(function() {
|
||||
if (isDestroyed(this))
|
||||
return;
|
||||
|
||||
activateTab(viewsFor.get(this));
|
||||
}),
|
||||
|
||||
getThumbnail: function() {
|
||||
if (isDestroyed(this))
|
||||
return BLANK;
|
||||
|
||||
// TODO: This is unimplemented in e10s: bug 1148601
|
||||
if (browser(this).isRemoteBrowser) {
|
||||
console.error('This method is not supported with E10S');
|
||||
return BLANK;
|
||||
}
|
||||
return getThumbnailURIForWindow(browser(this).contentWindow);
|
||||
},
|
||||
|
||||
attach: function(options) {
|
||||
if (isDestroyed(this))
|
||||
return;
|
||||
|
||||
let { Worker } = require('../content/worker');
|
||||
let { connect, makeChildOptions } = require('../content/utils');
|
||||
|
||||
let worker = Worker(options);
|
||||
worker.once("detach", () => {
|
||||
worker.destroy();
|
||||
});
|
||||
|
||||
let attach = frame => {
|
||||
let childOptions = makeChildOptions(options);
|
||||
frame.port.emit("sdk/tab/attach", childOptions);
|
||||
connect(worker, frame, { id: childOptions.id, url: this.url });
|
||||
};
|
||||
|
||||
// Do this synchronously if possible
|
||||
let frame = frames.getFrameForBrowser(browser(this));
|
||||
if (frame) {
|
||||
attach(frame);
|
||||
}
|
||||
else {
|
||||
let listener = (frame) => {
|
||||
if (frame.frameElement != browser(this))
|
||||
return;
|
||||
|
||||
frames.off("attach", listener);
|
||||
attach(frame);
|
||||
};
|
||||
frames.on("attach", listener);
|
||||
}
|
||||
|
||||
return worker;
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (isDestroyed(this))
|
||||
return;
|
||||
|
||||
destroyed.set(this, true);
|
||||
}
|
||||
});
|
||||
exports.Tab = Tab;
|
||||
|
||||
viewFor.define(Tab, tab => viewsFor.get(tab));
|
||||
|
||||
// Returns the high-level window for this DOM window if the windows module has
|
||||
// ever been loaded otherwise returns null
|
||||
function maybeWindowFor(domWindow) {
|
||||
try {
|
||||
return modelFor(domWindow);
|
||||
}
|
||||
catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function tabEmit(tab, event, ...args) {
|
||||
// Don't emit events for destroyed tabs
|
||||
if (isDestroyed(tab))
|
||||
return;
|
||||
|
||||
// If the windows module was never loaded this will return null. We don't need
|
||||
// to emit to the window.tabs object in this case as nothing can be listening.
|
||||
let tabElement = viewsFor.get(tab);
|
||||
let window = maybeWindowFor(tabElement.ownerDocument.defaultView);
|
||||
if (window)
|
||||
emit(window.tabs, event, tab, ...args);
|
||||
|
||||
emit(tabEvents, event, tab, ...args);
|
||||
emit(tab, event, tab, ...args);
|
||||
}
|
||||
|
||||
function windowClosed(domWindow) {
|
||||
if (!isBrowser(domWindow))
|
||||
return;
|
||||
|
||||
for (let tabElement of getTabs(domWindow)) {
|
||||
tabEventListener("close", tabElement);
|
||||
}
|
||||
}
|
||||
windowObserver.on('close', windowClosed);
|
||||
|
||||
// Don't want to send close events after unloaded
|
||||
when(_ => {
|
||||
windowObserver.off('close', windowClosed);
|
||||
});
|
||||
|
||||
// Listen for tabbrowser events
|
||||
function tabEventListener(event, tabElement, ...args) {
|
||||
let domWindow = tabElement.ownerDocument.defaultView;
|
||||
|
||||
if (ignoreWindow(domWindow))
|
||||
return;
|
||||
|
||||
// Don't send events for tabs that are already closing
|
||||
if (event != "close" && (tabElement.closing || !tabElement.parentNode))
|
||||
return;
|
||||
|
||||
let tab = modelsFor.get(tabElement);
|
||||
if (!tab)
|
||||
tab = new Tab(tabElement);
|
||||
|
||||
let window = maybeWindowFor(domWindow);
|
||||
|
||||
if (event == "open") {
|
||||
// Note, add to the window tabs first because if this is the first access to
|
||||
// window.tabs it will be prefilling itself with everything from tabs
|
||||
if (window)
|
||||
addListItem(window.tabs, tab);
|
||||
// The tabs module will take care of adding to its internal list
|
||||
}
|
||||
else if (event == "close") {
|
||||
if (window)
|
||||
removeListItem(window.tabs, tab);
|
||||
// The tabs module will take care of removing from its internal list
|
||||
}
|
||||
else if (event == "init" || event == "create" || event == "ready" || event == "load") {
|
||||
// Ignore load events from before browser windows have fully loaded, these
|
||||
// are for about:blank in the initial tab
|
||||
if (isBrowser(domWindow) && !domWindow.gBrowserInit.delayedStartupFinished)
|
||||
return;
|
||||
|
||||
// update the cached remote readyState value
|
||||
let { readyState } = args[0] || {};
|
||||
tab[remoteReadyStateCached] = readyState;
|
||||
}
|
||||
|
||||
if (event == "init") {
|
||||
// Do not emit events for the detected existent tabs, we only need to cache
|
||||
// their current document.readyState value.
|
||||
return;
|
||||
}
|
||||
|
||||
tabEmit(tab, event, ...args);
|
||||
|
||||
// The tab object shouldn't be reachable after closed
|
||||
if (event == "close") {
|
||||
viewsFor.delete(tab);
|
||||
modelsFor.delete(tabElement);
|
||||
}
|
||||
}
|
||||
observer.on('*', tabEventListener);
|
||||
|
||||
// Listen for tab events from content
|
||||
frames.port.on('sdk/tab/event', (frame, event, ...args) => {
|
||||
if (!frame.isTab)
|
||||
return;
|
||||
|
||||
let tabElement = getTabForBrowser(frame.frameElement);
|
||||
if (!tabElement)
|
||||
return;
|
||||
|
||||
tabEventListener(event, tabElement, ...args);
|
||||
});
|
||||
|
||||
// Implement `modelFor` function for the Tab instances..
|
||||
modelFor.when(isTab, view => {
|
||||
return modelsFor.get(view);
|
||||
});
|
||||
24
toolkit/jetpack/sdk/tabs/tab.js
Normal file
24
toolkit/jetpack/sdk/tabs/tab.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/* 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 { getTargetWindow } = require("../content/mod");
|
||||
const { getTabContentWindow, isTab } = require("./utils");
|
||||
const { viewFor } = require("../view/core");
|
||||
|
||||
if (require('../system/xul-app').name == 'Fennec') {
|
||||
module.exports = require('./tab-fennec');
|
||||
}
|
||||
else {
|
||||
module.exports = require('./tab-firefox');
|
||||
}
|
||||
|
||||
getTargetWindow.when(isTab, tab => getTabContentWindow(tab));
|
||||
|
||||
getTargetWindow.when(x => x instanceof module.exports.Tab,
|
||||
tab => getTabContentWindow(viewFor(tab)));
|
||||
135
toolkit/jetpack/sdk/tabs/tabs-firefox.js
Normal file
135
toolkit/jetpack/sdk/tabs/tabs-firefox.js
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/* 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 { Class } = require('../core/heritage');
|
||||
const { Tab, tabEvents } = require('./tab');
|
||||
const { EventTarget } = require('../event/target');
|
||||
const { emit, setListeners } = require('../event/core');
|
||||
const { pipe } = require('../event/utils');
|
||||
const { observer: windowObserver } = require('../windows/observer');
|
||||
const { List, addListItem, removeListItem } = require('../util/list');
|
||||
const { modelFor } = require('../model/core');
|
||||
const { viewFor } = require('../view/core');
|
||||
const { getTabs, getSelectedTab } = require('./utils');
|
||||
const { getMostRecentBrowserWindow, isBrowser } = require('../window/utils');
|
||||
const { Options } = require('./common');
|
||||
const { isPrivate } = require('../private-browsing');
|
||||
const { ignoreWindow, isWindowPBSupported } = require('../private-browsing/utils')
|
||||
const { isPrivateBrowsingSupported } = require('sdk/self');
|
||||
|
||||
const supportPrivateTabs = isPrivateBrowsingSupported && isWindowPBSupported;
|
||||
|
||||
const Tabs = Class({
|
||||
implements: [EventTarget],
|
||||
extends: List,
|
||||
initialize: function() {
|
||||
List.prototype.initialize.call(this);
|
||||
|
||||
// We must do the list manipulation here where the object is extensible
|
||||
this.on("open", tab => {
|
||||
addListItem(this, tab);
|
||||
});
|
||||
|
||||
this.on("close", tab => {
|
||||
removeListItem(this, tab);
|
||||
});
|
||||
},
|
||||
|
||||
get activeTab() {
|
||||
let activeDomWin = getMostRecentBrowserWindow();
|
||||
if (!activeDomWin)
|
||||
return null;
|
||||
return modelFor(getSelectedTab(activeDomWin));
|
||||
},
|
||||
|
||||
open: function(options) {
|
||||
options = Options(options);
|
||||
|
||||
// TODO: Remove the dependency on the windows module: bug 792670
|
||||
let windows = require('../windows').browserWindows;
|
||||
let activeWindow = windows.activeWindow;
|
||||
|
||||
let privateState = supportPrivateTabs && options.isPrivate;
|
||||
// When no isPrivate option was passed use the private state of the active
|
||||
// window
|
||||
if (activeWindow && privateState === undefined)
|
||||
privateState = isPrivate(activeWindow);
|
||||
|
||||
function getWindow(privateState) {
|
||||
for (let window of windows) {
|
||||
if (privateState === isPrivate(window)) {
|
||||
return window;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function openNewWindowWithTab() {
|
||||
windows.open({
|
||||
url: options.url,
|
||||
isPrivate: privateState,
|
||||
onOpen: function(newWindow) {
|
||||
let tab = newWindow.tabs[0];
|
||||
setListeners(tab, options);
|
||||
|
||||
if (options.isPinned)
|
||||
tab.pin();
|
||||
|
||||
// We don't emit the open event for the first tab in a new window so
|
||||
// do it now the listeners are attached
|
||||
emit(tab, "open", tab);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (options.inNewWindow)
|
||||
return openNewWindowWithTab();
|
||||
|
||||
// if the active window is in the state that we need then use it
|
||||
if (activeWindow && (privateState === isPrivate(activeWindow)))
|
||||
return activeWindow.tabs.open(options);
|
||||
|
||||
// find a window in the state that we need
|
||||
let window = getWindow(privateState);
|
||||
if (window)
|
||||
return window.tabs.open(options);
|
||||
|
||||
return openNewWindowWithTab();
|
||||
}
|
||||
});
|
||||
|
||||
const allTabs = new Tabs();
|
||||
// Export a new object with allTabs as the prototype, otherwise allTabs becomes
|
||||
// frozen and addListItem and removeListItem don't work correctly.
|
||||
module.exports = Object.create(allTabs);
|
||||
pipe(tabEvents, module.exports);
|
||||
|
||||
function addWindowTab(window, tabElement) {
|
||||
let tab = new Tab(tabElement);
|
||||
if (window)
|
||||
addListItem(window.tabs, tab);
|
||||
addListItem(allTabs, tab);
|
||||
emit(allTabs, "open", tab);
|
||||
}
|
||||
|
||||
// Find tabs in already open windows
|
||||
for (let tabElement of getTabs())
|
||||
addWindowTab(null, tabElement);
|
||||
|
||||
// Detect tabs in new windows
|
||||
windowObserver.on('open', domWindow => {
|
||||
if (!isBrowser(domWindow) || ignoreWindow(domWindow))
|
||||
return;
|
||||
|
||||
let window = null;
|
||||
try {
|
||||
modelFor(domWindow);
|
||||
}
|
||||
catch (e) { }
|
||||
|
||||
for (let tabElement of getTabs(domWindow)) {
|
||||
addWindowTab(window, tabElement);
|
||||
}
|
||||
});
|
||||
370
toolkit/jetpack/sdk/tabs/utils.js
Normal file
370
toolkit/jetpack/sdk/tabs/utils.js
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
/* 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'
|
||||
};
|
||||
|
||||
|
||||
// NOTE: This file should only deal with xul/native tabs
|
||||
|
||||
|
||||
const { Ci, Cu } = require('chrome');
|
||||
const { defer } = require("../lang/functional");
|
||||
const { windows, isBrowser } = require('../window/utils');
|
||||
const { isPrivateBrowsingSupported } = require('../self');
|
||||
const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm");
|
||||
|
||||
// Bug 834961: ignore private windows when they are not supported
|
||||
function getWindows() {
|
||||
return windows(null, { includePrivate: isPrivateBrowsingSupported });
|
||||
}
|
||||
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
|
||||
// Define predicate functions that can be used to detech weather
|
||||
// we deal with fennec tabs or firefox tabs.
|
||||
|
||||
// Predicate to detect whether tab is XUL "Tab" node.
|
||||
const isXULTab = tab =>
|
||||
tab instanceof Ci.nsIDOMNode &&
|
||||
tab.nodeName === "tab" &&
|
||||
tab.namespaceURI === XUL_NS;
|
||||
exports.isXULTab = isXULTab;
|
||||
|
||||
// Predicate to detecet whether given tab is a fettec tab.
|
||||
// Unfortunately we have to guess via duck typinng of:
|
||||
// http://mxr.mozilla.org/mozilla-central/source/mobile/android/chrome/content/browser.js#2583
|
||||
const isFennecTab = tab =>
|
||||
tab &&
|
||||
tab.QueryInterface &&
|
||||
Ci.nsIBrowserTab &&
|
||||
tab.QueryInterface(Ci.nsIBrowserTab) === tab;
|
||||
exports.isFennecTab = isFennecTab;
|
||||
|
||||
const isTab = x => isXULTab(x) || isFennecTab(x);
|
||||
exports.isTab = isTab;
|
||||
|
||||
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) {
|
||||
// bug 1009938 - may be null in SeaMonkey
|
||||
return window.gBrowser || window.getBrowser();
|
||||
}
|
||||
exports.getTabBrowser = getTabBrowser;
|
||||
|
||||
function getTabContainer(window) {
|
||||
return getTabBrowser(window).tabContainer;
|
||||
}
|
||||
exports.getTabContainer = getTabContainer;
|
||||
|
||||
/**
|
||||
* Returns the tabs for the `window` if given, or the tabs
|
||||
* across all the browser's windows otherwise.
|
||||
*
|
||||
* @param {nsIWindow} [window]
|
||||
* A reference to a window
|
||||
*
|
||||
* @returns {Array} an array of Tab objects
|
||||
*/
|
||||
function getTabs(window) {
|
||||
if (arguments.length === 0) {
|
||||
return getWindows().
|
||||
filter(isBrowser).
|
||||
reduce((tabs, window) => tabs.concat(getTabs(window)), []);
|
||||
}
|
||||
|
||||
// fennec
|
||||
if (window.BrowserApp)
|
||||
return window.BrowserApp.tabs;
|
||||
|
||||
// firefox - default
|
||||
return Array.filter(getTabContainer(window).children, t => !t.closing);
|
||||
}
|
||||
exports.getTabs = getTabs;
|
||||
|
||||
function getActiveTab(window) {
|
||||
return getSelectedTab(window);
|
||||
}
|
||||
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 (let window of getWindows()) {
|
||||
// this function may be called when not using fennec,
|
||||
// but BrowserApp is only defined on Fennec
|
||||
if (!window.BrowserApp)
|
||||
continue;
|
||||
|
||||
for (let tab of 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,
|
||||
isPrivate: options.isPrivate || false,
|
||||
parentId: window.BrowserApp.selectedTab.id
|
||||
});
|
||||
}
|
||||
|
||||
// firefox
|
||||
let newTab = window.gBrowser.addTab(url);
|
||||
if (!options.inBackground) {
|
||||
activateTab(newTab);
|
||||
}
|
||||
return newTab;
|
||||
};
|
||||
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) {
|
||||
// Bug 699450: the tab may already have been detached
|
||||
if (!tab.parentNode)
|
||||
return;
|
||||
return gBrowser.removeTab(tab);
|
||||
}
|
||||
|
||||
let window = getWindowHoldingTab(tab);
|
||||
// fennec?
|
||||
if (window && window.BrowserApp) {
|
||||
// Bug 699450: the tab may already have been detached
|
||||
if (!tab.browser)
|
||||
return;
|
||||
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 getTabId(tab) {
|
||||
if (tab.browser) // fennec
|
||||
return tab.id
|
||||
|
||||
return String.split(tab.linkedPanel, 'panel').pop();
|
||||
}
|
||||
exports.getTabId = getTabId;
|
||||
|
||||
function getTabForId(id) {
|
||||
return getTabs().find(tab => getTabId(tab) === id) || null;
|
||||
}
|
||||
exports.getTabForId = getTabForId;
|
||||
|
||||
function getTabTitle(tab) {
|
||||
return getBrowserForTab(tab).contentTitle || tab.label || "";
|
||||
}
|
||||
exports.getTabTitle = getTabTitle;
|
||||
|
||||
function setTabTitle(tab, title) {
|
||||
title = String(title);
|
||||
if (tab.browser) {
|
||||
// Fennec
|
||||
tab.browser.contentDocument.title = title;
|
||||
}
|
||||
else {
|
||||
let browser = getBrowserForTab(tab);
|
||||
// Note that we aren't actually setting the document title in e10s, just
|
||||
// the title the browser thinks the content has
|
||||
if (browser.isRemoteBrowser)
|
||||
browser._contentTitle = title;
|
||||
else
|
||||
browser.contentDocument.title = title;
|
||||
}
|
||||
tab.label = String(title);
|
||||
}
|
||||
exports.setTabTitle = setTabTitle;
|
||||
|
||||
function getTabContentDocument(tab) {
|
||||
return getBrowserForTab(tab).contentDocument;
|
||||
}
|
||||
exports.getTabContentDocument = getTabContentDocument;
|
||||
|
||||
function getTabContentWindow(tab) {
|
||||
return getBrowserForTab(tab).contentWindow;
|
||||
}
|
||||
exports.getTabContentWindow = getTabContentWindow;
|
||||
|
||||
/**
|
||||
* Returns all tabs' content windows across all the browsers' windows
|
||||
*/
|
||||
function getAllTabContentWindows() {
|
||||
return getTabs().map(getTabContentWindow);
|
||||
}
|
||||
exports.getAllTabContentWindows = getAllTabContentWindows;
|
||||
|
||||
// gets the tab containing the provided window
|
||||
function getTabForContentWindow(window) {
|
||||
return getTabs().find(tab => getTabContentWindow(tab) === window.top) || null;
|
||||
}
|
||||
exports.getTabForContentWindow = getTabForContentWindow;
|
||||
|
||||
// only sdk/selection.js is relying on shims
|
||||
function getTabForContentWindowNoShim(window) {
|
||||
function getTabContentWindowNoShim(tab) {
|
||||
let browser = getBrowserForTab(tab);
|
||||
return ShimWaiver.getProperty(browser, "contentWindow");
|
||||
}
|
||||
return getTabs().find(tab => getTabContentWindowNoShim(tab) === window.top) || null;
|
||||
}
|
||||
exports.getTabForContentWindowNoShim = getTabForContentWindowNoShim;
|
||||
|
||||
function getTabURL(tab) {
|
||||
return String(getBrowserForTab(tab).currentURI.spec);
|
||||
}
|
||||
exports.getTabURL = getTabURL;
|
||||
|
||||
function setTabURL(tab, url) {
|
||||
let browser = getBrowserForTab(tab);
|
||||
browser.loadURI(String(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;
|
||||
|
||||
|
||||
function getTabForBrowser(browser) {
|
||||
for (let window of getWindows()) {
|
||||
// this function may be called when not using fennec
|
||||
if (!window.BrowserApp)
|
||||
continue;
|
||||
|
||||
for (let tab of window.BrowserApp.tabs) {
|
||||
if (tab.browser === browser)
|
||||
return tab;
|
||||
}
|
||||
}
|
||||
|
||||
let tabbrowser = browser.getTabBrowser && browser.getTabBrowser()
|
||||
return !!tabbrowser && tabbrowser.getTabForBrowser(browser);
|
||||
}
|
||||
exports.getTabForBrowser = getTabForBrowser;
|
||||
|
||||
function pin(tab) {
|
||||
let gBrowser = getTabBrowserForTab(tab);
|
||||
// TODO: Implement Fennec support
|
||||
if (gBrowser) gBrowser.pinTab(tab);
|
||||
}
|
||||
exports.pin = pin;
|
||||
|
||||
function unpin(tab) {
|
||||
let gBrowser = getTabBrowserForTab(tab);
|
||||
// TODO: Implement Fennec support
|
||||
if (gBrowser) gBrowser.unpinTab(tab);
|
||||
}
|
||||
exports.unpin = unpin;
|
||||
|
||||
function isPinned(tab) {
|
||||
return !!tab.pinned;
|
||||
}
|
||||
exports.isPinned = isPinned;
|
||||
|
||||
function reload(tab) {
|
||||
getBrowserForTab(tab).reload();
|
||||
}
|
||||
exports.reload = reload
|
||||
|
||||
function getIndex(tab) {
|
||||
let gBrowser = getTabBrowserForTab(tab);
|
||||
// Firefox
|
||||
if (gBrowser) {
|
||||
return tab._tPos;
|
||||
}
|
||||
// Fennec
|
||||
else {
|
||||
let window = getWindowHoldingTab(tab)
|
||||
let tabs = window.BrowserApp.tabs;
|
||||
for (let i = tabs.length; i >= 0; i--)
|
||||
if (tabs[i] === tab) return i;
|
||||
}
|
||||
}
|
||||
exports.getIndex = getIndex;
|
||||
|
||||
function move(tab, index) {
|
||||
let gBrowser = getTabBrowserForTab(tab);
|
||||
// Firefox
|
||||
if (gBrowser) gBrowser.moveTabTo(tab, index);
|
||||
// TODO: Implement fennec support
|
||||
}
|
||||
exports.move = move;
|
||||
17
toolkit/jetpack/sdk/tabs/worker.js
Normal file
17
toolkit/jetpack/sdk/tabs/worker.js
Normal file
|
|
@ -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;
|
||||
Loading…
Add table
Add a link
Reference in a new issue