Bug 1455471 - Backed out changeset 95b0887a1895 for test failures. a=jorgk DONTBUILD
This commit is contained in:
Родитель
11d3280ec0
Коммит
cb5ce05aee
|
@ -24,24 +24,10 @@ chat/**
|
|||
editor/**
|
||||
im/**
|
||||
ldap/**
|
||||
mail/**
|
||||
mailnews/**
|
||||
suite/**
|
||||
|
||||
# mail exclusions
|
||||
mail/app/**
|
||||
mail/base/**
|
||||
mail/branding/**
|
||||
mail/config/**
|
||||
mail/extensions/**
|
||||
mail/installer/**
|
||||
mail/locales/**
|
||||
mail/test/**
|
||||
mail/themes/**
|
||||
|
||||
# mail/components exclusions
|
||||
mail/components/*
|
||||
!mail/components/extensions
|
||||
|
||||
# calendar/ exclusions
|
||||
|
||||
# prefs files
|
||||
|
|
|
@ -335,7 +335,7 @@
|
|||
<!-- navigation-toolbox with main menubar and tabs toolbar -->
|
||||
#include mainNavigationToolbox.inc
|
||||
|
||||
<toolbar id="tabs-toolbar" class="chromeclass-toolbar">
|
||||
<toolbar id="tabs-toolbar">
|
||||
<!-- class tabmail-tabs is unused and only maintained for add-ons. -->
|
||||
<tabs flex="1"
|
||||
id="tabmail-tabs"
|
||||
|
|
|
@ -722,10 +722,7 @@ function loadExtraTabs()
|
|||
if ((!tab) || (typeof tab != "object"))
|
||||
return;
|
||||
|
||||
if ("wrappedJSObject" in tab)
|
||||
tab = tab.wrappedJSObject;
|
||||
|
||||
let tabmail = document.getElementById("tabmail");
|
||||
let tabmail = document.getElementById("tabmail");
|
||||
|
||||
// we got no action, so suppose its "legacy" code
|
||||
if (!("action" in tab)) {
|
||||
|
@ -753,9 +750,10 @@ function loadExtraTabs()
|
|||
}
|
||||
|
||||
if (tab.action == "open") {
|
||||
|
||||
for (let i = 0; i < tab.tabs.length; i++)
|
||||
if("tabType" in tab.tabs[i])
|
||||
tabmail.openTab(tab.tabs[i].tabType,tab.tabs[i].tabParams);
|
||||
if("tabType" in tabs.tab[i])
|
||||
tabmail.openTab(tabs.tab[i].tabType,tabs.tab[i].tabParams);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -1796,12 +1794,6 @@ var TabsInTitlebar = {
|
|||
|
||||
let allowed = this.systemSupported &&
|
||||
(Object.keys(this._disallowed)).length == 0;
|
||||
|
||||
if (document.documentElement.getAttribute("chromehidden").includes("toolbar")) {
|
||||
// Don't draw in titlebar in case of a popup window
|
||||
allowed = false;
|
||||
}
|
||||
|
||||
if (allowed) {
|
||||
document.documentElement.setAttribute("tabsintitlebar", "true");
|
||||
if (AppConstants.platform == "macosx") {
|
||||
|
|
|
@ -296,11 +296,7 @@ var contentTabBaseType = {
|
|||
// about:preferences
|
||||
null],
|
||||
|
||||
shouldSwitchTo: function onSwitchTo({contentPage: aContentPage, duplicate: aDuplicate}) {
|
||||
if (aDuplicate) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
shouldSwitchTo: function onSwitchTo({contentPage: aContentPage}) {
|
||||
let tabmail = document.getElementById("tabmail");
|
||||
let tabInfo = tabmail.tabInfo;
|
||||
|
||||
|
@ -310,7 +306,8 @@ var contentTabBaseType = {
|
|||
|
||||
let contentUrl = aContentPage.replace(regEx, "");
|
||||
|
||||
for (let selectedIndex = 0; selectedIndex < tabInfo.length; ++selectedIndex) {
|
||||
for (let selectedIndex = 0; selectedIndex < tabInfo.length;
|
||||
++selectedIndex) {
|
||||
if (tabInfo[selectedIndex].mode.name == this.name &&
|
||||
tabInfo[selectedIndex].browser.currentURI.spec
|
||||
.replace(regEx, "") == contentUrl) {
|
||||
|
@ -822,9 +819,8 @@ var specialTabs = {
|
|||
restoreTab: function onRestoreTab(aTabmail, aPersistedState) {
|
||||
aTabmail.openTab("contentTab", { contentPage: aPersistedState.tabURI,
|
||||
clickHandler: aPersistedState.clickHandler,
|
||||
duplicate: aPersistedState.duplicate,
|
||||
background: true } );
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -274,7 +274,7 @@
|
|||
</xul:tabbox>
|
||||
</content>
|
||||
|
||||
<implementation implements="nsIController, nsIWebProgressListener, nsIWebProgressListener2">
|
||||
<implementation implements="nsIController">
|
||||
<constructor>
|
||||
window.controllers.insertControllerAt(0, this);
|
||||
this._restoringTabState = null;
|
||||
|
@ -319,9 +319,6 @@
|
|||
<field name="mLastTabOpener">
|
||||
null
|
||||
</field>
|
||||
<field name="mTabsProgressListeners">
|
||||
new Set();
|
||||
</field>
|
||||
<method name="createTooltip">
|
||||
<parameter name="event"/>
|
||||
<body><![CDATA[
|
||||
|
@ -506,7 +503,7 @@
|
|||
this.saveCurrentTabState();
|
||||
|
||||
let tab = {mode: tabMode, busy: false, canClose: true,
|
||||
thinking: false, beforeTabOpen: true, _ext: {}};
|
||||
thinking: false, _ext: {}};
|
||||
tabMode.tabs.push(tab);
|
||||
|
||||
var t = document.createElementNS(
|
||||
|
@ -604,22 +601,6 @@
|
|||
// do themselves when we open them.
|
||||
UpdateMailToolbar("tabmail");
|
||||
|
||||
let moving = restoreState ? restoreState.moving : null;
|
||||
|
||||
// Dispatch tab opening event
|
||||
let evt = new CustomEvent("TabOpen", { bubbles: true, detail: { tabInfo: tab, moving } });
|
||||
t.dispatchEvent(evt);
|
||||
delete tab.beforeTabOpen;
|
||||
|
||||
// Register browser progress listeners
|
||||
let browser = this.getBrowserForTab(tab);
|
||||
if (browser) {
|
||||
// It would probably be better to have the tabs register this listener, since the
|
||||
// browser can change. This wasn't trivial to do while implementing basic WebExtension
|
||||
// support, so let's assume one browser only for now.
|
||||
browser.webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_ALL);
|
||||
}
|
||||
|
||||
return tab;
|
||||
} catch (e) {
|
||||
logException(e);
|
||||
|
@ -735,12 +716,6 @@
|
|||
if (tryCloseFunc && !tryCloseFunc.call(tab.mode.tabType, tab))
|
||||
return;
|
||||
|
||||
let evt = new CustomEvent("TabClose", {
|
||||
bubbles: true,
|
||||
detail: { tabInfo: tab, moving: tab.moving }
|
||||
});
|
||||
tabNode.dispatchEvent(evt);
|
||||
|
||||
for (let tabMonitor of this.tabMonitors) {
|
||||
if ("onTabClosing" in tabMonitor)
|
||||
tabMonitor.onTabClosing(tab);
|
||||
|
@ -827,8 +802,6 @@
|
|||
</method>
|
||||
<method name="replaceTabWithWindow">
|
||||
<parameter name="aTab"/>
|
||||
<parameter name="aTargetWindow"/>
|
||||
<parameter name="aTargetPosition"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (this.tabInfo.length <= 1)
|
||||
|
@ -848,65 +821,48 @@
|
|||
// object with absolutely no references to our current window.
|
||||
tab = JSON.parse(JSON.stringify(tab));
|
||||
|
||||
// Set up an identifier for the move, consumers may want to correlate TabClose and
|
||||
// TabOpen events.
|
||||
let moveSession = Cc["@mozilla.org/uuid-generator;1"]
|
||||
.getService(Ci.nsIUUIDGenerator)
|
||||
.generateUUID().toString();
|
||||
|
||||
tab.moving = moveSession;
|
||||
aTab.moving = moveSession;
|
||||
|
||||
this.closeTab(aTab,true);
|
||||
|
||||
if (aTargetWindow && aTargetWindow !== "popup") {
|
||||
let targetTabmail = aTargetWindow.document.getElementById("tabmail");
|
||||
targetTabmail.restoreTab(tab);
|
||||
|
||||
if (aTargetPosition) {
|
||||
let droppedTab = targetTabmail.tabInfo[targetTabmail.tabInfo.length - 1];
|
||||
targetTabmail.moveTabTo(droppedTab, aTargetPosition);
|
||||
}
|
||||
return aTargetWindow;
|
||||
} else {
|
||||
let features = ["chrome"];
|
||||
if (aTargetWindow === "popup") {
|
||||
features.push("dialog", "resizable", "minimizable", "centerscreen", "titlebar", "close");
|
||||
} else {
|
||||
features.push("dialog=no", "all", "status", "toolbar");
|
||||
}
|
||||
|
||||
return window.openDialog("chrome://messenger/content/", "_blank",
|
||||
features.join(","), null,
|
||||
{ action : "restore", tabs: [tab] } ).focus();
|
||||
}
|
||||
return window.openDialog("chrome://messenger/content/", "_blank",
|
||||
"chrome,dialog=no,all", null,
|
||||
{ action : "restore", tabs: [tab] } ).focus();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
<method name="moveTabTo">
|
||||
<parameter name="aTabIndexNodeOrInfo"/>
|
||||
<parameter name="aTab"/>
|
||||
<parameter name="aIndex"/>
|
||||
<body><![CDATA[
|
||||
let [oldIdx, tab, tabNode] =
|
||||
this._getTabContextForTabbyThing(aTabIndexNodeOrInfo, false);
|
||||
|
||||
if (!tab || !tabNode || tabNode.tagName != "tab" || oldIdx < 0 || oldIdx == aIndex) {
|
||||
if ((!aTab) || (aTab.tagName != "tab"))
|
||||
return -1;
|
||||
}
|
||||
|
||||
// remove the entries from tabInfo, tabMode and the tabContainer
|
||||
let oldIdx = this.tabContainer.getIndexOfItem(aTab);
|
||||
if (oldIdx < 0)
|
||||
return -1;
|
||||
|
||||
if (oldIdx == aIndex)
|
||||
return -1;
|
||||
|
||||
// Cache the old tabInfo
|
||||
let tab = this.tabInfo[oldIdx]
|
||||
|
||||
if (!tab)
|
||||
return -1;
|
||||
|
||||
// remove the entries form tabInfo, tabMode and the tabContainer
|
||||
this.tabInfo.splice(oldIdx, 1);
|
||||
tab.mode.tabs.splice(tab.mode.tabs.indexOf(tab), 1);
|
||||
tabNode.remove();
|
||||
aTab.remove();
|
||||
|
||||
|
||||
// as we removed items, we might need to update indices
|
||||
if (oldIdx < aIndex) {
|
||||
aIndex--;
|
||||
}
|
||||
if (oldIdx < aIndex)
|
||||
aIndex --;
|
||||
|
||||
// Read it into tabInfo and the tabContainer
|
||||
this.tabInfo.splice(aIndex, 0, tab);
|
||||
this.tabContainer.insertBefore(tabNode, this.tabContainer.childNodes[aIndex]);
|
||||
this.tabContainer.insertBefore(aTab, this.tabContainer.childNodes[aIndex]);
|
||||
|
||||
// Now it's getting a bit ugly, as tabModes stores redundant
|
||||
// information we need to get it in sync with tabInfo.
|
||||
|
@ -918,9 +874,10 @@
|
|||
// found our tab, we insert the moved tab directly behind into tabModes
|
||||
|
||||
// In case find no tab we simply append it
|
||||
let modeIdx = tab.mode.tabs.length + 1;
|
||||
let modeIdx = tab.mode.tabs.length+1;
|
||||
|
||||
for (let i = 0; i < tab.mode.tabs.length; i++) {
|
||||
|
||||
if (this.tabInfo.indexOf(tab.mode.tabs[i]) < aIndex)
|
||||
continue;
|
||||
|
||||
|
@ -930,11 +887,6 @@
|
|||
|
||||
tab.mode.tabs.splice(modeIdx, 0, tab);
|
||||
|
||||
let evt = new CustomEvent("TabMove", {
|
||||
bubbles: true, view: window, detail: { idx: oldIdx, tabInfo: tab }
|
||||
});
|
||||
tabNode.dispatchEvent(evt);
|
||||
|
||||
return aIndex;
|
||||
]]>
|
||||
</body>
|
||||
|
@ -1104,16 +1056,13 @@
|
|||
</body>
|
||||
</method>
|
||||
|
||||
<property name="selectedTab">
|
||||
<property name="selectedTab" readonly="true">
|
||||
<getter><![CDATA[
|
||||
if (!this.currentTabInfo)
|
||||
this.currentTabInfo = this.tabInfo[0];
|
||||
|
||||
return this.currentTabInfo;
|
||||
]]></getter>
|
||||
<setter><![CDATA[
|
||||
this.switchToTab(val);
|
||||
]]></setter>
|
||||
</property>
|
||||
|
||||
<!-- getBrowserForSelectedTab is required as some toolkit functions
|
||||
|
@ -1127,18 +1076,13 @@
|
|||
if (!tab)
|
||||
return null;
|
||||
|
||||
return this.getBrowserForTab(tab);
|
||||
let browserFunc = tab.mode.getBrowser || tab.mode.tabType.getBrowser;
|
||||
if (browserFunc)
|
||||
return browserFunc.call(tab.mode.tabType, tab);
|
||||
|
||||
return null;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="getBrowserForTab">
|
||||
<parameter name="aTab"/>
|
||||
<body><![CDATA[
|
||||
let browserFunc = aTab ? aTab.mode.getBrowser || aTab.mode.tabType.getBrowser : null;
|
||||
return browserFunc ? browserFunc.call(aTab.mode.tabType, aTab) : null;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!-- getBrowserForDocument is used to find the browser for a specific
|
||||
document that's been loaded -->
|
||||
<method name="getBrowserForDocument">
|
||||
|
@ -1177,17 +1121,6 @@
|
|||
return null;
|
||||
]]></body>
|
||||
</method>
|
||||
<method name="getTabForBrowser">
|
||||
<parameter name="aBrowser"/>
|
||||
<body><![CDATA[
|
||||
for (let tabInfo of this.tabInfo) {
|
||||
if (this.getBrowserForTab(tabInfo) == aBrowser) {
|
||||
return tabInfo;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
]]></body>
|
||||
</method>
|
||||
<method name="removeCurrentTab">
|
||||
<body><![CDATA[
|
||||
this.removeTabByNode(
|
||||
|
@ -1252,10 +1185,6 @@
|
|||
// We switched tabs, so we don't need to know the last tab
|
||||
// opener anymore.
|
||||
this.mLastTabOpener = null;
|
||||
|
||||
let evt = new CustomEvent("TabSelect", { bubbles: true, detail: { tabInfo: tab } });
|
||||
this.tabContainer.selectedItem.dispatchEvent(evt);
|
||||
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
|
@ -1298,14 +1227,6 @@
|
|||
let tabNode =
|
||||
this.tabContainer.childNodes[iTab];
|
||||
|
||||
let defaultTabTitle = document.documentElement.getAttribute("defaultTabTitle");
|
||||
let oldLabel = tabNode.getAttribute("label");
|
||||
let newLabel = aTabNodeOrInfo ? tab.title : defaultTabTitle;
|
||||
|
||||
if (oldLabel == newLabel) {
|
||||
return;
|
||||
}
|
||||
|
||||
let titleChangeFunc = tab.mode.onTitleChanged ||
|
||||
tab.mode.tabType.onTitleChanged;
|
||||
if (titleChangeFunc)
|
||||
|
@ -1317,21 +1238,13 @@
|
|||
|
||||
// If the displayed tab is the one at the moment of creation
|
||||
// (aTabNodeOrInfo is null), set the default title as its title.
|
||||
tabNode.setAttribute("label", newLabel);
|
||||
tabNode.setAttribute("label", aTabNodeOrInfo ?
|
||||
tab.title :
|
||||
document.documentElement.getAttribute("defaultTabTitle"));
|
||||
|
||||
// Update the window title if we're the displayed tab.
|
||||
if (iTab == this.tabContainer.selectedIndex)
|
||||
this.setDocumentTitle(tab);
|
||||
|
||||
// Notify tab title change
|
||||
if (!tab.beforeTabOpen) {
|
||||
let evt = new CustomEvent("TabAttrModified", {
|
||||
bubbles: true,
|
||||
cancelable: false,
|
||||
detail: { changed: ["label"], tabInfo: tab }
|
||||
});
|
||||
tabNode.dispatchEvent(evt);
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
|
@ -1350,16 +1263,6 @@
|
|||
|
||||
if (tab) {
|
||||
let tabNode = this.tabContainer.childNodes[iTab];
|
||||
let oldIcon = tabNode.getAttribute("image");
|
||||
|
||||
if (oldIcon != aIcon && !tab.beforeTabOpen) {
|
||||
let evt = new CustomEvent("TabAttrModified", {
|
||||
bubbles: true,
|
||||
cancelable: false,
|
||||
detail: { changed: ["image"], tabInfo: tab }
|
||||
});
|
||||
tabNode.dispatchEvent(evt);
|
||||
}
|
||||
|
||||
if (aIcon)
|
||||
tabNode.setAttribute("image", aIcon);
|
||||
|
@ -1622,127 +1525,6 @@
|
|||
]]>
|
||||
</body>
|
||||
</method>
|
||||
<method name="addTabsProgressListener">
|
||||
<parameter name="aListener"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
this.mTabsProgressListeners.add(aListener);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
<method name="removeTabsProgressListener">
|
||||
<parameter name="aListener"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
this.mTabsProgressListeners.delete(aListener);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
<method name="_callTabListeners">
|
||||
<parameter name="aMethod"/>
|
||||
<parameter name="aArgs"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
for (let listener of this.mTabsProgressListeners.values()) {
|
||||
if (aMethod in listener) {
|
||||
try {
|
||||
listener[aMethod](...aArgs);
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
<method name="onProgressChange">
|
||||
<parameter name="aWebProgress"/>
|
||||
<parameter name="aRequest"/>
|
||||
<parameter name="aCurSelf"/>
|
||||
<parameter name="aMaxSelf"/>
|
||||
<parameter name="aCurTotal"/>
|
||||
<parameter name="aMaxTotal"/>
|
||||
<body><![CDATA[
|
||||
let browser = aWebProgress.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.sameTypeRootTreeItem
|
||||
.chromeEventHandler;
|
||||
this._callTabListeners("onProgressChange", [browser, ...arguments]);
|
||||
]]></body>
|
||||
</method>
|
||||
<method name="onProgressChange64">
|
||||
<parameter name="aWebProgress"/>
|
||||
<parameter name="aRequest"/>
|
||||
<parameter name="aCurSelf"/>
|
||||
<parameter name="aMaxSelf"/>
|
||||
<parameter name="aCurTotal"/>
|
||||
<parameter name="aMaxTotal"/>
|
||||
<body><![CDATA[
|
||||
let browser = aWebProgress.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.sameTypeRootTreeItem
|
||||
.chromeEventHandler;
|
||||
this._callTabListeners("onProgressChange64", [browser, ...arguments]);
|
||||
]]></body>
|
||||
</method>
|
||||
<method name="onLocationChange">
|
||||
<parameter name="aWebProgress"/>
|
||||
<parameter name="aRequest"/>
|
||||
<parameter name="aLocationURI"/>
|
||||
<parameter name="aFlags"/>
|
||||
<body><![CDATA[
|
||||
let browser = aWebProgress.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.sameTypeRootTreeItem
|
||||
.chromeEventHandler;
|
||||
this._callTabListeners("onLocationChange", [browser, ...arguments]);
|
||||
]]></body>
|
||||
</method>
|
||||
<method name="onStateChange">
|
||||
<parameter name="aWebProgress"/>
|
||||
<parameter name="aRequest"/>
|
||||
<parameter name="aStateFlags"/>
|
||||
<parameter name="aStatus"/>
|
||||
<body><![CDATA[
|
||||
let browser = aWebProgress.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.sameTypeRootTreeItem
|
||||
.chromeEventHandler;
|
||||
this._callTabListeners("onStateChange", [browser, ...arguments]);
|
||||
]]></body>
|
||||
</method>
|
||||
<method name="onStatusChange">
|
||||
<parameter name="aWebProgress"/>
|
||||
<parameter name="aRequest"/>
|
||||
<parameter name="aStatus"/>
|
||||
<parameter name="aMessage"/>
|
||||
<body><![CDATA[
|
||||
let browser = aWebProgress.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.sameTypeRootTreeItem
|
||||
.chromeEventHandler;
|
||||
this._callTabListeners("onStatusChange", [browser, ...arguments]);
|
||||
]]></body>
|
||||
</method>
|
||||
<method name="onSecurityChange">
|
||||
<parameter name="aWebProgress"/>
|
||||
<parameter name="aRequest"/>
|
||||
<parameter name="aState"/>
|
||||
<body><![CDATA[
|
||||
let browser = aWebProgress.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.sameTypeRootTreeItem
|
||||
.chromeEventHandler;
|
||||
this._callTabListeners("onSecurityChange", [browser, ...arguments]);
|
||||
]]></body>
|
||||
</method>
|
||||
<method name="onRefreshAttempted">
|
||||
<parameter name="aWebProgress"/>
|
||||
<parameter name="aURI"/>
|
||||
<parameter name="aDelay"/>
|
||||
<parameter name="aSameURI"/>
|
||||
<body><![CDATA[
|
||||
let browser = aWebProgress.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.sameTypeRootTreeItem
|
||||
.chromeEventHandler;
|
||||
this._callTabListeners("onRefreshAttempted", [browser, ...arguments]);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
/* 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";
|
||||
|
||||
extensions.registerModules({
|
||||
tabs: {
|
||||
url: "chrome://messenger/content/child/ext-tabs.js",
|
||||
scopes: ["addon_child"],
|
||||
paths: [
|
||||
["tabs"],
|
||||
],
|
||||
}
|
||||
});
|
|
@ -1,39 +0,0 @@
|
|||
/* 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";
|
||||
|
||||
this.tabs = class extends ExtensionAPI {
|
||||
getAPI(context) {
|
||||
return {
|
||||
tabs: {
|
||||
connect: function(tabId, connectInfo) {
|
||||
let name = "";
|
||||
if (connectInfo && connectInfo.name !== null) {
|
||||
name = connectInfo.name;
|
||||
}
|
||||
let recipient = {
|
||||
extensionId: context.extension.id,
|
||||
tabId,
|
||||
};
|
||||
if (connectInfo && connectInfo.frameId !== null) {
|
||||
recipient.frameId = connectInfo.frameId;
|
||||
}
|
||||
return context.messenger.connect(context.messageManager, name, recipient);
|
||||
},
|
||||
|
||||
sendMessage: function(tabId, message, options, responseCallback) {
|
||||
let recipient = {
|
||||
extensionId: context.extension.id,
|
||||
tabId: tabId,
|
||||
};
|
||||
if (options && options.frameId !== null) {
|
||||
recipient.frameId = options.frameId;
|
||||
}
|
||||
return context.messenger.sendMessage(context.messageManager, message, recipient, responseCallback);
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"tabs": {
|
||||
"url": "chrome://messenger/content/parent/ext-tabs.js",
|
||||
"schema": "chrome://messenger/content/schemas/tabs.json",
|
||||
"scopes": ["addon_parent"],
|
||||
"paths": [
|
||||
["tabs"]
|
||||
]
|
||||
},
|
||||
"windows": {
|
||||
"url": "chrome://messenger/content/parent/ext-windows.js",
|
||||
"schema": "chrome://messenger/content/schemas/windows.json",
|
||||
"scopes": ["addon_parent"],
|
||||
"paths": [
|
||||
["windows"]
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
category webextension-modules mail chrome://messenger/content/ext-mail.json
|
||||
|
||||
category webextension-scripts c-mail chrome://messenger/content/parent/ext-mail.js
|
||||
category webextension-scripts-addon mail chrome://messenger/content/child/ext-mail.js
|
|
@ -1,16 +0,0 @@
|
|||
# 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/.
|
||||
|
||||
messenger.jar:
|
||||
content/messenger/ext-mail.json (ext-mail.json)
|
||||
|
||||
content/messenger/parent/ext-mail.js (parent/ext-mail.js)
|
||||
content/messenger/parent/ext-tabs.js (parent/ext-tabs.js)
|
||||
content/messenger/parent/ext-windows.js (parent/ext-windows.js)
|
||||
|
||||
content/messenger/child/ext-tabs.js (child/ext-tabs.js)
|
||||
content/messenger/child/ext-mail.js (child/ext-mail.js)
|
||||
|
||||
content/messenger/schemas/tabs.json (schemas/tabs.json)
|
||||
content/messenger/schemas/windows.json (schemas/windows.json)
|
|
@ -1,9 +0,0 @@
|
|||
# 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/.
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'extensions-mail.manifest',
|
||||
]
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
|
@ -1,954 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
|
||||
var {
|
||||
ExtensionError,
|
||||
defineLazyGetter,
|
||||
} = ExtensionUtils;
|
||||
|
||||
let tabTracker;
|
||||
let windowTracker;
|
||||
|
||||
// This function is pretty tightly tied to Extension.jsm.
|
||||
// Its job is to fill in the |tab| property of the sender.
|
||||
const getSender = (extension, target, sender) => {
|
||||
let tabId = -1;
|
||||
if ("tabId" in sender) {
|
||||
// The message came from a privileged extension page running in a tab. In
|
||||
// that case, it should include a tabId property (which is filled in by the
|
||||
// page-open listener below).
|
||||
tabId = sender.tabId;
|
||||
delete sender.tabId;
|
||||
} else if (ExtensionUtils.instanceOf(target, "XULElement") ||
|
||||
ExtensionUtils.instanceOf(target, "HTMLIFrameElement")) {
|
||||
tabId = tabTracker.getBrowserData(target).tabId;
|
||||
}
|
||||
|
||||
if (tabId != null && tabId >= 0) {
|
||||
let tab = extension.tabManager.get(tabId, null);
|
||||
if (tab) {
|
||||
sender.tab = tab.convert();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Used by Extension.jsm
|
||||
global.tabGetSender = getSender;
|
||||
|
||||
global.makeWidgetId = id => {
|
||||
id = id.toLowerCase();
|
||||
// FIXME: This allows for collisions.
|
||||
return id.replace(/[^a-z0-9_-]/g, "_");
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the tab browser for the tabmail tabInfo
|
||||
*
|
||||
* @param {NativeTabInfo} nativeTabInfo The tabInfo object to get the browser for
|
||||
* @return {?XULElement} The browser element for the tab
|
||||
*/
|
||||
function getTabBrowser(nativeTabInfo) {
|
||||
if (!nativeTabInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (nativeTabInfo.mode.getBrowser) {
|
||||
return nativeTabInfo.mode.getBrowser(nativeTabInfo);
|
||||
}
|
||||
|
||||
if (nativeTabInfo.mode.tabType.getBrowser) {
|
||||
return nativeTabInfo.mode.tabType.getBrowser(nativeTabInfo);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
global.getTabBrowser = getTabBrowser;
|
||||
|
||||
/**
|
||||
* Thw window tracker tracks opening and closing Thunderbird windows. Each window has an id, which
|
||||
* is mapped to native window objects.
|
||||
*/
|
||||
class WindowTracker extends WindowTrackerBase {
|
||||
/**
|
||||
* Adds a tab progress listener to the given mail window
|
||||
*
|
||||
* @param {DOMWindow} window The mail window to which to add the listener.
|
||||
* @param {Object} listener The listener to add
|
||||
*/
|
||||
addProgressListener(window, listener) {
|
||||
let tabmail = window.document.getElementById("tabmail");
|
||||
tabmail.addTabsProgressListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a tab progress listener from the given mail window.
|
||||
*
|
||||
* @param {DOMWindow} window The mail window from which to remove the listener.
|
||||
* @param {Object} listener The listener to remove
|
||||
*/
|
||||
removeProgressListener(window, listener) {
|
||||
let tabmail = window.document.getElementById("tabmail");
|
||||
tabmail.removeTabsProgressListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the passed window object is a mail window. The function name is for base class
|
||||
* compatibility with gecko.
|
||||
*
|
||||
* @param {DOMWindow} window The window to check
|
||||
* @return {Boolean} True, if the window is a mail window
|
||||
*/
|
||||
isBrowserWindow(window) {
|
||||
let { documentElement } = window.document;
|
||||
|
||||
return documentElement.getAttribute("windowtype") === "mail:3pane";
|
||||
}
|
||||
|
||||
/**
|
||||
* The currently active, or topmost, mail window, or null if no mail window is currently open.
|
||||
*
|
||||
* @property {?DOMWindow} topWindow
|
||||
* @readonly
|
||||
*/
|
||||
get topWindow() {
|
||||
return Services.wm.getMostRecentWindow("mail:3pane");
|
||||
}
|
||||
|
||||
/**
|
||||
* The currently active, or topmost, mail window, or null if no mail window is currently open.
|
||||
* Will only return the topmost "normal" (i.e., not popup) window.
|
||||
*
|
||||
* @property {?DOMWindow} topNormalWindow
|
||||
* @readonly
|
||||
*/
|
||||
get topNormalWindow() {
|
||||
let win = null;
|
||||
if (AppConstants.platform == "win") {
|
||||
let windowList = Services.wm.getZOrderDOMWindowEnumerator("mail:3pane", true);
|
||||
if (!windowList.hasMoreElements()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
win = windowList.getNext();
|
||||
while (win.document.documentElement.getAttribute("chromehidden")) {
|
||||
if (!windowList.hasMoreElements()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
win = windowList.getNext();
|
||||
}
|
||||
} else {
|
||||
// Platforms other than Windows have a broken z-order...
|
||||
win = Services.wm.getMostRecentWindow("mail:3pane", true);
|
||||
|
||||
// If we're lucky, this isn't a popup, and we can just return this.
|
||||
if (win && win.document.documentElement.getAttribute("chromehidden")) {
|
||||
win = null;
|
||||
let windowList = Services.wm.getEnumerator("mail:3pane", true);
|
||||
// This is oldest to newest, so this gets a bit ugly.
|
||||
while (windowList.hasMoreElements()) {
|
||||
let nextWin = windowList.getNext();
|
||||
if (!nextWin.document.documentElement.getAttribute("chromehidden")) {
|
||||
win = nextWin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return win;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An event manager API provider which listens for a DOM event in any mail
|
||||
* window, and calls the given listener function whenever an event is received.
|
||||
* That listener function receives a `fire` object, which it can use to dispatch
|
||||
* events to the extension, and a DOM event object.
|
||||
*
|
||||
* @param {BaseContext} context
|
||||
* The extension context which the event manager belongs to.
|
||||
* @param {string} name
|
||||
* The API name of the event manager, e.g.,"runtime.onMessage".
|
||||
* @param {string} event
|
||||
* The name of the DOM event to listen for.
|
||||
* @param {function} listener
|
||||
* The listener function to call when a DOM event is received.
|
||||
*/
|
||||
global.WindowEventManager = class extends EventManager {
|
||||
constructor(context, name, event, listener) {
|
||||
super(context, name, fire => {
|
||||
let listener2 = listener.bind(null, fire);
|
||||
|
||||
windowTracker.addListener(event, listener2);
|
||||
return () => {
|
||||
windowTracker.removeListener(event, listener2);
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Tracks the opening and closing of tabs and maps them between their numeric WebExtension ID and
|
||||
* the native tab info objects
|
||||
*/
|
||||
class TabTracker extends TabTrackerBase {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this._tabs = new WeakMap();
|
||||
this._browsers = new WeakMap();
|
||||
this._tabIds = new Map();
|
||||
this._nextId = 1;
|
||||
this._movingTabs = new Map();
|
||||
|
||||
this._handleTabDestroyed = this._handleTabDestroyed.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize tab tracking listeners the first time that an event listener is added
|
||||
*/
|
||||
init() {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
this.initialized = true;
|
||||
|
||||
this._handleWindowOpen = this._handleWindowOpen.bind(this);
|
||||
this._handleWindowClose = this._handleWindowClose.bind(this);
|
||||
|
||||
windowTracker.addListener("TabClose", this);
|
||||
windowTracker.addListener("TabOpen", this);
|
||||
windowTracker.addListener("TabSelect", this);
|
||||
windowTracker.addOpenListener(this._handleWindowOpen);
|
||||
windowTracker.addCloseListener(this._handleWindowClose);
|
||||
|
||||
/* eslint-disable mozilla/balanced-listeners */
|
||||
this.on("tab-detached", this._handleTabDestroyed);
|
||||
this.on("tab-removed", this._handleTabDestroyed);
|
||||
/* eslint-enable mozilla/balanced-listeners */
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the numeric ID for the given native tab.
|
||||
*
|
||||
* @param {NativeTabInfo} nativeTabInfo The tabmail tabInfo for which to return an ID
|
||||
* @return {Integer} The tab's numeric ID
|
||||
*/
|
||||
getId(nativeTabInfo) {
|
||||
let id = this._tabs.get(nativeTabInfo);
|
||||
if (id) {
|
||||
return id;
|
||||
}
|
||||
|
||||
this.init();
|
||||
|
||||
id = this._nextId++;
|
||||
this.setId(nativeTabInfo, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tab id corresponding to the given browser element
|
||||
*
|
||||
* @param {XULElement} browser The <browser> element to retrieve for
|
||||
* @return {Integer} The tab's numeric ID
|
||||
*/
|
||||
getBrowserTabId(browser) {
|
||||
let id = this._browsers.get(browser);
|
||||
if (id) {
|
||||
return id;
|
||||
}
|
||||
|
||||
let tabmail = browser.ownerDocument.getElementById("tabmail");
|
||||
let tab = tabmail && tabmail.tabInfo.find(info => getTabBrowser(info) == browser);
|
||||
|
||||
if (tab) {
|
||||
id = this.getId(tab);
|
||||
this._browsers.set(browser, id);
|
||||
return id;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the tab information for the given tabInfo object
|
||||
*
|
||||
* @param {NativeTabInfo} nativeTabInfo The tab info to record for
|
||||
* @param {Integer} id The tab id to record
|
||||
*/
|
||||
setId(nativeTabInfo, id) {
|
||||
this._tabs.set(nativeTabInfo, id);
|
||||
let browser = getTabBrowser(nativeTabInfo);
|
||||
if (browser) {
|
||||
this._browsers.set(browser, id);
|
||||
}
|
||||
this._tabIds.set(id, nativeTabInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to call when a tab was close, deletes tab information for the tab
|
||||
*
|
||||
* @param {Event} event The event triggering the detroyal
|
||||
* @param {{ nativeTabInfo:NativeTabInfo}} The object containing tab info
|
||||
*/
|
||||
_handleTabDestroyed(event, { nativeTabInfo }) {
|
||||
let id = this._tabs.get(nativeTabInfo);
|
||||
if (id) {
|
||||
this._tabs.delete(nativeTabInfo);
|
||||
if (this._tabIds.get(id) === nativeTabInfo) {
|
||||
this._tabIds.delete(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the native tab with the given numeric ID.
|
||||
*
|
||||
* @param {Integer} tabId The numeric ID of the tab to return.
|
||||
* @param {*} default_ The value to return if no tab exists with the given ID.
|
||||
* @return {NativeTabInfo} The tab information for the given id.
|
||||
*/
|
||||
getTab(tabId, default_ = undefined) {
|
||||
let nativeTabInfo = this._tabIds.get(tabId);
|
||||
if (nativeTabInfo) {
|
||||
return nativeTabInfo;
|
||||
}
|
||||
if (default_ !== undefined) {
|
||||
return default_;
|
||||
}
|
||||
throw new ExtensionError(`Invalid tab ID: ${tabId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles load events for recently-opened windows, and adds additional
|
||||
* listeners which may only be safely added when the window is fully loaded.
|
||||
*
|
||||
* @param {Event} event A DOM event to handle.
|
||||
*/
|
||||
handleEvent(event) {
|
||||
let nativeTabInfo = event.detail.tabInfo;
|
||||
if (!getTabBrowser(nativeTabInfo)) {
|
||||
// We don't care about events for tabs that don't have a browser
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case "TabOpen": {
|
||||
// Save the current tab, since the newly-created tab will likely be
|
||||
// active by the time the promise below resolves and the event is
|
||||
// dispatched.
|
||||
let tabmail = event.target.ownerDocument.getElementById("tabmail");
|
||||
let currentTab = tabmail.selectedTab;
|
||||
|
||||
// We need to delay sending this event until the next tick, since the
|
||||
// tab does not have its final index when the TabOpen event is dispatched.
|
||||
Promise.resolve().then(() => {
|
||||
if (event.detail.moving) {
|
||||
let srcTabId = this._movingTabs.get(event.detail.moving);
|
||||
this.setId(nativeTabInfo, srcTabId);
|
||||
this._movingTabs.delete(event.detail.moving);
|
||||
|
||||
this.emitAttached(nativeTabInfo);
|
||||
} else {
|
||||
this.emitCreated(nativeTabInfo, currentTab);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "TabClose": {
|
||||
if (event.detail.moving) {
|
||||
this._movingTabs.set(event.detail.moving, this.getId(nativeTabInfo));
|
||||
this.emitDetached(nativeTabInfo);
|
||||
} else {
|
||||
this.emitRemoved(nativeTabInfo, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "TabSelect":
|
||||
// Because we are delaying calling emitCreated above, we also need to
|
||||
// delay sending this event because it shouldn't fire before onCreated.
|
||||
Promise.resolve().then(() => {
|
||||
this.emitActivated(nativeTabInfo);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A private method which is called whenever a new mail window is opened, and dispatches the
|
||||
* necessary events for it.
|
||||
*
|
||||
* @param {DOMWindow} window The window being opened.
|
||||
*/
|
||||
_handleWindowOpen(window) {
|
||||
let tabmail = window.document.getElementById("tabmail");
|
||||
for (let nativeTabInfo of tabmail.tabInfo) {
|
||||
if (!getTabBrowser(nativeTabInfo)) {
|
||||
continue;
|
||||
}
|
||||
this.emitCreated(nativeTabInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A private method which is called whenever a mail window is closed, and dispatches the necessary
|
||||
* events for it.
|
||||
*
|
||||
* @param {DOMWindow} window The window being closed.
|
||||
*/
|
||||
_handleWindowClose(window) {
|
||||
let tabmail = window.document.getElementById("tabmail");
|
||||
for (let nativeTabInfo of tabmail.tabInfo) {
|
||||
if (!getTabBrowser(nativeTabInfo)) {
|
||||
continue;
|
||||
}
|
||||
this.emitRemoved(nativeTabInfo, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a "tab-activated" event for the given tab info.
|
||||
*
|
||||
* @param {NativeTabInfo} nativeTabInfo The tab info which has been activated.
|
||||
*/
|
||||
emitActivated(nativeTabInfo) {
|
||||
let browser = getTabBrowser(nativeTabInfo);
|
||||
|
||||
this.emit("tab-activated", {
|
||||
tabId: this.getId(nativeTabInfo),
|
||||
windowId: windowTracker.getId(browser.ownerGlobal)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a "tab-attached" event for the given tab info.
|
||||
*
|
||||
* @param {NativeTabInfo} nativeTabInfo The tab info which is being attached.
|
||||
*/
|
||||
emitAttached(nativeTabInfo) {
|
||||
let tabId = this.getId(nativeTabInfo);
|
||||
let browser = getTabBrowser(nativeTabInfo);
|
||||
let tabmail = browser.ownerDocument.getElementById("tabmail");
|
||||
let tabIndex = tabmail._getTabContextForTabbyThing(nativeTabInfo)[0];
|
||||
let newWindowId = windowTracker.getId(browser.ownerGlobal);
|
||||
|
||||
this.emit("tab-attached", { nativeTabInfo, tabId, newWindowId, newPosition: tabIndex });
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a "tab-detached" event for the given tab info.
|
||||
*
|
||||
* @param {NativeTabInfo} nativeTabInfo The tab info which is being detached.
|
||||
*/
|
||||
emitDetached(nativeTabInfo) {
|
||||
let tabId = this.getId(nativeTabInfo);
|
||||
let browser = getTabBrowser(nativeTabInfo);
|
||||
let tabmail = browser.ownerDocument.getElementById("tabmail");
|
||||
let tabIndex = tabmail._getTabContextForTabbyThing(nativeTabInfo)[0];
|
||||
let oldWindowId = windowTracker.getId(browser.ownerGlobal);
|
||||
|
||||
this.emit("tab-detached", { nativeTabInfo, tabId, oldWindowId, oldPosition: tabIndex });
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a "tab-created" event for the given tab info.
|
||||
*
|
||||
* @param {NativeTabInfo} nativeTabInfo The tab info which is being created.
|
||||
* @param {?NativeTab} currentTab The tab info for the currently active tab.
|
||||
*/
|
||||
emitCreated(nativeTabInfo, currentTab) {
|
||||
this.emit("tab-created", { nativeTabInfo, currentTab });
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a "tab-removed" event for the given tab info.
|
||||
*
|
||||
* @param {NativeTabInfo} nativeTabInfo The tab info in the window to which the tab is being
|
||||
* removed
|
||||
* @param {Boolean} isWindowClosing If true, the window with these tabs is closing
|
||||
*/
|
||||
emitRemoved(nativeTabInfo, isWindowClosing) {
|
||||
let browser = getTabBrowser(nativeTabInfo);
|
||||
let windowId = windowTracker.getId(browser.ownerGlobal);
|
||||
let tabId = this.getId(nativeTabInfo);
|
||||
|
||||
this.emit("tab-removed", { nativeTabInfo, tabId, windowId, isWindowClosing });
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns tab id and window id for the given browser element
|
||||
*
|
||||
* @param {Element} browser The browser element to check
|
||||
* @return {{ tabId:Integer, windowId:Integer }} The browsing data for the element
|
||||
*/
|
||||
getBrowserData(browser) {
|
||||
return {
|
||||
tabId: this.getBrowserTabId(browser),
|
||||
windowId: windowTracker.getId(browser.ownerGlobal)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the active tab info for the given window
|
||||
*
|
||||
* @property {?NativeTabInfo} activeTab The active tab
|
||||
* @readonly
|
||||
*/
|
||||
get activeTab() {
|
||||
let window = windowTracker.topWindow;
|
||||
let tabmail = window && window.document.getElementById("tabmail");
|
||||
return tabmail ? tabmail.selectedTab : null;
|
||||
}
|
||||
}
|
||||
|
||||
tabTracker = new TabTracker();
|
||||
windowTracker = new WindowTracker();
|
||||
Object.assign(global, { tabTracker, windowTracker });
|
||||
|
||||
/**
|
||||
* Extension-specific wrapper around a Thunderbird tab
|
||||
*/
|
||||
class Tab extends TabBase {
|
||||
/** Returns the XUL browser for the tab. */
|
||||
get browser() {
|
||||
return getTabBrowser(this.nativeTab);
|
||||
}
|
||||
|
||||
/** Returns the tabmail element for the tab. */
|
||||
get tabmail() {
|
||||
return this.browser.ownerDocument.getElementById("tabmail");
|
||||
}
|
||||
|
||||
/** Returns the frame loader for the tab */
|
||||
get frameLoader() {
|
||||
// If we don't have a frameLoader yet, just return a dummy with no width and
|
||||
// height.
|
||||
return super.frameLoader || { lazyWidth: 0, lazyHeight: 0 };
|
||||
}
|
||||
|
||||
/** Returns the favIcon, without permission checks */
|
||||
get _favIconUrl() {
|
||||
return this.browser.mIconURL;
|
||||
}
|
||||
|
||||
/** Returns the last accessed time */
|
||||
get lastAccessed() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Returns the audible state */
|
||||
get audible() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns the cookie store id */
|
||||
get cookieStoreId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Returns the discarded state */
|
||||
get discarded() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns the tab height */
|
||||
get height() {
|
||||
return this.frameLoader.lazyHeight;
|
||||
}
|
||||
|
||||
/** Returns hidden status */
|
||||
get hidden() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns the tab index */
|
||||
get index() {
|
||||
return this.tabmail.tabInfo.filter(info => getTabBrowser(info)).indexOf(this.nativeTab);
|
||||
}
|
||||
|
||||
/** Returns information about the muted state of the tab */
|
||||
get mutedInfo() {
|
||||
return { muted: false };
|
||||
}
|
||||
|
||||
/** Returns information about the sharing state of the tab */
|
||||
get sharingState() {
|
||||
return { camera: false, microphone: false, screen: false };
|
||||
}
|
||||
|
||||
/** Returns the pinned state of the tab */
|
||||
get pinned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns the active state of the tab */
|
||||
get active() {
|
||||
return this.nativeTab == this.tabmail.selectedTab;
|
||||
}
|
||||
|
||||
/** Returns the highlighted state of the tab */
|
||||
get highlighted() {
|
||||
return this.active;
|
||||
}
|
||||
|
||||
/** Returns the selected state of the tab */
|
||||
get selected() {
|
||||
return this.active;
|
||||
}
|
||||
|
||||
/** Returns the loading status of the tab */
|
||||
get status() {
|
||||
return this.browser.webProgress.isLoadingDocument ? "loading" : "complete";
|
||||
}
|
||||
|
||||
/** Returns the title of the tab, without permission checks */
|
||||
get _title() {
|
||||
let tabNode = this.tabmail._getTabContextForTabbyThing(this.nativeTab)[2];
|
||||
return tabNode.getAttribute("label");
|
||||
}
|
||||
|
||||
/** Returns the width of the tab */
|
||||
get width() {
|
||||
return this.frameLoader.lazyWidth;
|
||||
}
|
||||
|
||||
/** Returns the native window object of the tab */
|
||||
get window() {
|
||||
return this.browser.ownerGlobal;
|
||||
}
|
||||
|
||||
/** Returns the window id of the tab */
|
||||
get windowId() {
|
||||
return windowTracker.getId(this.window);
|
||||
}
|
||||
|
||||
/** Returns the article state of the tab */
|
||||
get isArticle() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns the reader mode state of the tab */
|
||||
get isInReaderMode() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension-specific wrapper around a Thunderbird window
|
||||
*/
|
||||
class Window extends WindowBase {
|
||||
/**
|
||||
* Update the geometry of the mail window.
|
||||
*
|
||||
* @param {Object} options
|
||||
* An object containing new values for the window's geometry.
|
||||
* @param {integer} [options.left]
|
||||
* The new pixel distance of the left side of the mail window from
|
||||
* the left of the screen.
|
||||
* @param {integer} [options.top]
|
||||
* The new pixel distance of the top side of the mail window from
|
||||
* the top of the screen.
|
||||
* @param {integer} [options.width]
|
||||
* The new pixel width of the window.
|
||||
* @param {integer} [options.height]
|
||||
* The new pixel height of the window.
|
||||
*/
|
||||
updateGeometry(options) {
|
||||
let { window } = this;
|
||||
|
||||
if (options.left !== null || options.top !== null) {
|
||||
let left = options.left === null ? window.screenX : options.left;
|
||||
let top = options.top === null ? window.screenY : options.top;
|
||||
window.moveTo(left, top);
|
||||
}
|
||||
|
||||
if (options.width !== null || options.height !== null) {
|
||||
let width = options.width === null ? window.outerWidth : options.width;
|
||||
let height = options.height === null ? window.outerHeight : options.height;
|
||||
window.resizeTo(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the tabmail element for the tab. */
|
||||
get tabmail() {
|
||||
return this.window.document.getElementById("tabmail");
|
||||
}
|
||||
|
||||
/** Returns the title of the tab, without permission checks */
|
||||
get _title() {
|
||||
return this.window.document.title;
|
||||
}
|
||||
|
||||
/** Returns the title of the tab, checking tab permissions */
|
||||
get title() {
|
||||
// Thunderbird can have an empty active tab while a window is loading
|
||||
if (this.activeTab && this.activeTab.hasTabPermission) {
|
||||
return this._title;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the title preface of the window.
|
||||
*
|
||||
* @param {String} titlePreface The title preface to set
|
||||
*/
|
||||
setTitlePreface(titlePreface) {
|
||||
this.window.document.documentElement.setAttribute("titlepreface", titlePreface);
|
||||
}
|
||||
|
||||
/** Gets the foucsed state of the window */
|
||||
get focused() {
|
||||
return this.window.document.hasFocus();
|
||||
}
|
||||
|
||||
/** Gets the top position of the window */
|
||||
get top() {
|
||||
return this.window.screenY;
|
||||
}
|
||||
|
||||
/** Gets the left position of the window */
|
||||
get left() {
|
||||
return this.window.screenX;
|
||||
}
|
||||
|
||||
/** Gets the width of the window */
|
||||
get width() {
|
||||
return this.window.outerWidth;
|
||||
}
|
||||
|
||||
/** Gets the height of the window */
|
||||
get height() {
|
||||
return this.window.outerHeight;
|
||||
}
|
||||
|
||||
/** Gets the private browsing status of the window */
|
||||
get incognito() {
|
||||
return PrivateBrowsingUtils.isWindowPrivate(this.window);
|
||||
}
|
||||
|
||||
/** Checks if the window is considered always on top */
|
||||
get alwaysOnTop() {
|
||||
return this.xulWindow.zLevel >= Ci.nsIXULWindow.raisedZ;
|
||||
}
|
||||
|
||||
/** Checks if the window was the last one focused */
|
||||
get isLastFocused() {
|
||||
return this.window === windowTracker.topWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the window state for the given window
|
||||
*
|
||||
* @param {DOMWindow} window The window to check
|
||||
* @return {String} "maximized", "minimized", "normal" or "fullscreen"
|
||||
*/
|
||||
static getState(window) {
|
||||
const STATES = {
|
||||
[window.STATE_MAXIMIZED]: "maximized",
|
||||
[window.STATE_MINIMIZED]: "minimized",
|
||||
[window.STATE_NORMAL]: "normal",
|
||||
};
|
||||
let state = STATES[window.windowState];
|
||||
if (window.fullScreen) {
|
||||
state = "fullscreen";
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
/** Returns the window state for this specific window */
|
||||
get state() {
|
||||
return Window.getState(this.window);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the window state for this speific window
|
||||
*
|
||||
* @param {String} state "maximized", "minimized", "normal" or "fullscreen"
|
||||
*/
|
||||
set state(state) {
|
||||
let { window } = this;
|
||||
if (state !== "fullscreen" && window.fullScreen) {
|
||||
window.fullScreen = false;
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case "maximized":
|
||||
window.maximize();
|
||||
break;
|
||||
|
||||
case "minimized":
|
||||
case "docked":
|
||||
window.minimize();
|
||||
break;
|
||||
|
||||
case "normal":
|
||||
// Restore sometimes returns the window to its previous state, rather
|
||||
// than to the "normal" state, so it may need to be called anywhere from
|
||||
// zero to two times.
|
||||
window.restore();
|
||||
if (window.windowState !== window.STATE_NORMAL) {
|
||||
window.restore();
|
||||
}
|
||||
if (window.windowState !== window.STATE_NORMAL) {
|
||||
// And on OS-X, where normal vs. maximized is basically a heuristic,
|
||||
// we need to cheat.
|
||||
window.sizeToContent();
|
||||
}
|
||||
break;
|
||||
|
||||
case "fullscreen":
|
||||
window.fullScreen = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Unexpected window state: ${state}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the (relevant) tabs in this window
|
||||
*
|
||||
* @yields {Tab} The wrapped Tab in this window
|
||||
*/
|
||||
* getTabs() {
|
||||
let { tabManager } = this.extension;
|
||||
|
||||
for (let nativeTabInfo of this.tabmail.tabInfo) {
|
||||
if (getTabBrowser(nativeTabInfo)) {
|
||||
// Only tabs that have a browser element
|
||||
yield tabManager.getWrapper(nativeTabInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Retrieves the active tab in this window */
|
||||
get activeTab() {
|
||||
let { tabManager } = this.extension;
|
||||
let selectedTab = this.tabmail.selectedTab;
|
||||
if (selectedTab) {
|
||||
return tabManager.getWrapper(selectedTab);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the tab at the given index
|
||||
*
|
||||
* @param {Number} index The index to look at
|
||||
* @return {Tab} The wrapped tab at the index
|
||||
*/
|
||||
getTabAtIndex(index) {
|
||||
let nativeTabInfo = this.tabmail.tabInfo.filter(info => getTabBrowser(info))[index];
|
||||
if (nativeTabInfo) {
|
||||
return tabManager.getWrapper(nativeTabInfo);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(global, { Tab, Window });
|
||||
|
||||
/**
|
||||
* Manages native tabs, their wrappers, and their dynamic permissions for a particular extension.
|
||||
*/
|
||||
class TabManager extends TabManagerBase {
|
||||
/**
|
||||
* Returns a Tab wrapper for the tab with the given ID.
|
||||
*
|
||||
* @param {integer} tabId The ID of the tab for which to return a wrapper.
|
||||
* @param {*} default_ The value to return if no tab exists with the given ID.
|
||||
* @return {Tab|*} The wrapped tab, or the default value
|
||||
*/
|
||||
get(tabId, default_ = undefined) {
|
||||
let nativeTabInfo = tabTracker.getTab(tabId, default_);
|
||||
|
||||
if (nativeTabInfo) {
|
||||
return this.getWrapper(nativeTabInfo);
|
||||
}
|
||||
return default_;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the extension has requested activeTab permission, grant it those permissions for the current
|
||||
* inner window in the given native tab.
|
||||
*
|
||||
* @param {NativeTabInfo} nativeTabInfo The native tab for which to grant permissions.
|
||||
*/
|
||||
addActiveTabPermission(nativeTabInfo = tabTracker.activeTab) {
|
||||
super.addActiveTabPermission(nativeTabInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke the extension's activeTab permissions for the current inner window of the given native
|
||||
* tab.
|
||||
*
|
||||
* @param {NativeTabInfo} nativeTabInfo The native tab for which to revoke permissions.
|
||||
*/
|
||||
revokeActiveTabPermission(nativeTabInfo = tabTracker.activeTab) {
|
||||
super.revokeActiveTabPermission(nativeTabInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Tab instance wrapping the given native tab info.
|
||||
*
|
||||
* @param {NativeTabInfo} nativeTabInfo The native tab for which to return a wrapper.
|
||||
* @return {Tab} The wrapped native tab
|
||||
*/
|
||||
wrapTab(nativeTabInfo) {
|
||||
return new Tab(this.extension, nativeTabInfo, tabTracker.getId(nativeTabInfo));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages native browser windows and their wrappers for a particular extension.
|
||||
*/
|
||||
class WindowManager extends WindowManagerBase {
|
||||
/**
|
||||
* Returns a Window wrapper for the mail window with the given ID.
|
||||
*
|
||||
* @param {Integer} windowId The ID of the browser window for which to return a wrapper.
|
||||
* @param {BaseContext} context The extension context for which the matching is being performed.
|
||||
* Used to determine the current window for relevant properties.
|
||||
* @return {Window} The wrapped window
|
||||
*/
|
||||
get(windowId, context) {
|
||||
let window = windowTracker.getWindow(windowId, context);
|
||||
return this.getWrapper(window);
|
||||
}
|
||||
|
||||
/**
|
||||
* Yields an iterator of WindowBase wrappers for each currently existing browser window.
|
||||
*
|
||||
* @yields {Window}
|
||||
*/
|
||||
* getAll() {
|
||||
for (let window of windowTracker.browserWindows()) {
|
||||
yield this.getWrapper(window);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Window instance wrapping the given mail window.
|
||||
*
|
||||
* @param {DOMWindow} window The mail window for which to return a wrapper.
|
||||
* @returns {Window} The wrapped window
|
||||
*/
|
||||
wrapWindow(window) {
|
||||
return new Window(this.extension, window, windowTracker.getId(window));
|
||||
}
|
||||
}
|
||||
|
||||
extensions.on("startup", (type, extension) => { // eslint-disable-line mozilla/balanced-listeners
|
||||
defineLazyGetter(extension, "tabManager",
|
||||
() => new TabManager(extension));
|
||||
defineLazyGetter(extension, "windowManager",
|
||||
() => new WindowManager(extension));
|
||||
});
|
|
@ -1,617 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "PromiseUtils",
|
||||
"resource://gre/modules/PromiseUtils.jsm");
|
||||
var { ExtensionError } = ExtensionUtils;
|
||||
|
||||
/**
|
||||
* A listener that allows waiting until tabs are fully loaded, e.g. off of about:blank
|
||||
*/
|
||||
let tabListener = {
|
||||
tabReadyInitialized: false,
|
||||
tabReadyPromises: new WeakMap(),
|
||||
initializingTabs: new WeakSet(),
|
||||
|
||||
/**
|
||||
* Initialize the progress listener for tab ready changes
|
||||
*/
|
||||
initTabReady() {
|
||||
if (!this.tabReadyInitialized) {
|
||||
windowTracker.addListener("progress", this);
|
||||
|
||||
this.tabReadyInitialized = true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Web Progress listener method for the location change
|
||||
*
|
||||
* @param {Element} browser The browser element that caused the change
|
||||
* @param {nsIWebProgress} webProgress The web progress for the location change
|
||||
* @param {nsIRequest} request The xpcom request for this change
|
||||
* @param {nsIURI} locationURI The target uri
|
||||
* @param {Integer} flags The web progress flags for this change
|
||||
*/
|
||||
onLocationChange(browser, webProgress, request, locationURI, flags) {
|
||||
if (webProgress.isTopLevel) {
|
||||
let tabmail = browser.ownerDocument.getElementById("tabmail");
|
||||
let nativeTabInfo = tabmail.getTabForBrowser(browser);
|
||||
|
||||
// Now we are certain that the first page in the tab was loaded.
|
||||
this.initializingTabs.delete(nativeTabInfo);
|
||||
|
||||
// browser.innerWindowID is now set, resolve the promises if any.
|
||||
let deferred = this.tabReadyPromises.get(nativeTabInfo);
|
||||
if (deferred) {
|
||||
deferred.resolve(nativeTabInfo);
|
||||
this.tabReadyPromises.delete(nativeTabInfo);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Promise that the given tab completes loading
|
||||
*
|
||||
* @param {NativeTabInfo} nativeTabInfo The tabInfo describing the tab
|
||||
* @return {Promise<NativeTabInfo>} Resolves when the tab completes loading
|
||||
*/
|
||||
awaitTabReady(nativeTabInfo) {
|
||||
let deferred = this.tabReadyPromises.get(nativeTabInfo);
|
||||
if (!deferred) {
|
||||
deferred = PromiseUtils.defer();
|
||||
let browser = getTabBrowser(nativeTabInfo);
|
||||
if (!this.initializingTabs.has(nativeTabInfo) &&
|
||||
(browser.innerWindowID || browser.currentURI.spec === "about:blank")) {
|
||||
deferred.resolve(nativeTabInfo);
|
||||
} else {
|
||||
this.initTabReady();
|
||||
this.tabReadyPromises.set(nativeTabInfo, deferred);
|
||||
}
|
||||
}
|
||||
return deferred.promise;
|
||||
}
|
||||
};
|
||||
|
||||
// Attributes and properties used in the TabsUpdateFilterManager
|
||||
const allAttrs = new Set(["favIconUrl", "title"]);
|
||||
const allProperties = new Set([
|
||||
"favIconUrl",
|
||||
"status",
|
||||
"title",
|
||||
]);
|
||||
const restricted = new Set(["url", "favIconUrl", "title"]);
|
||||
|
||||
/**
|
||||
* An EventManager for the tabs.onUpdated listener
|
||||
*/
|
||||
class TabsUpdateFilterEventManager extends EventManager {
|
||||
constructor(context) {
|
||||
let { extension } = context;
|
||||
let { tabManager } = extension;
|
||||
|
||||
let register = (fire, filterProps) => {
|
||||
let filter = { ...filterProps };
|
||||
if (filter.urls) {
|
||||
filter.urls = new MatchPatternSet(filter.urls);
|
||||
}
|
||||
let needsModified = true;
|
||||
if (filter.properties) {
|
||||
// Default is to listen for all events.
|
||||
needsModified = filter.properties.some(prop => allAttrs.has(prop));
|
||||
filter.properties = new Set(filter.properties);
|
||||
} else {
|
||||
filter.properties = allProperties;
|
||||
}
|
||||
|
||||
function sanitize(changeInfo) {
|
||||
let result = {};
|
||||
let nonempty = false;
|
||||
let hasTabs = extension.hasPermission("tabs");
|
||||
for (let prop in changeInfo) {
|
||||
if (hasTabs || !restricted.has(prop)) {
|
||||
nonempty = true;
|
||||
result[prop] = changeInfo[prop];
|
||||
}
|
||||
}
|
||||
return nonempty && result;
|
||||
}
|
||||
|
||||
function getWindowID(windowId) {
|
||||
if (windowId === Window.WINDOW_ID_CURRENT) {
|
||||
return windowTracker.getId(windowTracker.topWindow);
|
||||
}
|
||||
return windowId;
|
||||
}
|
||||
|
||||
function matchFilters(tab, changed) {
|
||||
if (!filterProps) {
|
||||
return true;
|
||||
}
|
||||
if (filter.tabId != null && tab.id != filter.tabId) {
|
||||
return false;
|
||||
}
|
||||
if (filter.windowId != null && tab.windowId != getWindowID(filter.windowId)) {
|
||||
return false;
|
||||
}
|
||||
if (filter.urls) {
|
||||
// We check permission first because tab.uri is null if !hasTabPermission.
|
||||
return tab.hasTabPermission && filter.urls.matches(tab.uri);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
let fireForTab = (tab, changed) => {
|
||||
if (!matchFilters(tab, changed)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let changeInfo = sanitize(changed);
|
||||
if (changeInfo) {
|
||||
fire.async(tab.id, changeInfo, tab.convert());
|
||||
}
|
||||
};
|
||||
|
||||
let listener = event => {
|
||||
let needed = [];
|
||||
if (event.type == "TabAttrModified") {
|
||||
let changed = event.detail.changed;
|
||||
if (changed.includes("image") && filter.properties.has("favIconUrl")) {
|
||||
needed.push("favIconUrl");
|
||||
}
|
||||
if (changed.includes("label") && filter.properties.has("title")) {
|
||||
needed.push("title");
|
||||
}
|
||||
}
|
||||
|
||||
let tab = tabManager.getWrapper(event.detail.tabInfo);
|
||||
|
||||
let changeInfo = {};
|
||||
for (let prop of needed) {
|
||||
changeInfo[prop] = tab[prop];
|
||||
}
|
||||
|
||||
fireForTab(tab, changeInfo);
|
||||
};
|
||||
|
||||
let statusListener = ({ browser, status, url }) => {
|
||||
let tabmail = browser.ownerDocument.getElementById("tabmail");
|
||||
let nativeTabInfo = tabmail.getTabForBrowser(browser);
|
||||
if (nativeTabInfo) {
|
||||
let changed = { status };
|
||||
if (url) {
|
||||
changed.url = url;
|
||||
}
|
||||
|
||||
fireForTab(tabManager.getWrapper(nativeTabInfo), changed);
|
||||
}
|
||||
};
|
||||
|
||||
if (needsModified) {
|
||||
windowTracker.addListener("TabAttrModified", listener);
|
||||
}
|
||||
|
||||
if (filter.properties.has("status")) {
|
||||
windowTracker.addListener("status", statusListener);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (needsModified) {
|
||||
windowTracker.removeListener("TabAttrModified", listener);
|
||||
}
|
||||
if (filter.properties.has("status")) {
|
||||
windowTracker.removeListener("status", statusListener);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
super({
|
||||
context,
|
||||
name: "tabs.onUpdated",
|
||||
register,
|
||||
});
|
||||
}
|
||||
|
||||
addListener(callback, filter) {
|
||||
let { extension } = this.context;
|
||||
if (filter && filter.urls &&
|
||||
(!extension.hasPermission("tabs") && !extension.hasPermission("activeTab"))) {
|
||||
Cu.reportError("Url filtering in tabs.onUpdated requires \"tabs\" or \"activeTab\" permission.");
|
||||
return false;
|
||||
}
|
||||
return super.addListener(callback, filter);
|
||||
}
|
||||
}
|
||||
|
||||
this.tabs = class extends ExtensionAPI {
|
||||
getAPI(context) {
|
||||
let { extension } = context;
|
||||
let { tabManager } = extension;
|
||||
|
||||
/**
|
||||
* Gets the tab for the given tab id, or the active tab if the id is null
|
||||
*
|
||||
* @param {?Integer} tabId The tab id to get
|
||||
* @return {Tab} The matching tab, or the active tab
|
||||
*/
|
||||
function getTabOrActive(tabId) {
|
||||
if (tabId !== null) {
|
||||
return tabTracker.getTab(tabId);
|
||||
}
|
||||
return tabTracker.activeTab;
|
||||
}
|
||||
|
||||
/**
|
||||
* Promise that the tab with the given tab id is ready
|
||||
*
|
||||
* @param {Integer} tabId The tab id to check
|
||||
* @return {Promise<NativeTabInfo>} Resolved when the loading is complete
|
||||
*/
|
||||
async function promiseTabWhenReady(tabId) {
|
||||
let tab;
|
||||
if (tabId === null) {
|
||||
tab = tabManager.getWrapper(tabTracker.activeTab);
|
||||
} else {
|
||||
tab = tabManager.get(tabId);
|
||||
}
|
||||
|
||||
await tabListener.awaitTabReady(tab.nativeTab);
|
||||
|
||||
return tab;
|
||||
}
|
||||
|
||||
return {
|
||||
tabs: {
|
||||
onActivated: new EventManager({
|
||||
context,
|
||||
name: "tabs.onActivated",
|
||||
register: fire => {
|
||||
let listener = (eventName, event) => {
|
||||
fire.async(event);
|
||||
};
|
||||
|
||||
tabTracker.on("tab-activated", listener);
|
||||
return () => {
|
||||
tabTracker.off("tab-activated", listener);
|
||||
};
|
||||
}
|
||||
}).api(),
|
||||
|
||||
onCreated: new EventManager({
|
||||
context,
|
||||
name: "tabs.onCreated",
|
||||
register: fire => {
|
||||
let listener = (eventName, event) => {
|
||||
fire.async(tabManager.convert(event.nativeTabInfo, event.currentTab));
|
||||
};
|
||||
|
||||
tabTracker.on("tab-created", listener);
|
||||
return () => {
|
||||
tabTracker.off("tab-created", listener);
|
||||
};
|
||||
}
|
||||
}).api(),
|
||||
|
||||
onAttached: new EventManager({
|
||||
context,
|
||||
name: "tabs.onAttached",
|
||||
register: fire => {
|
||||
let listener = (eventName, event) => {
|
||||
fire.async(event.tabId, { newWindowId: event.newWindowId, newPosition: event.newPosition });
|
||||
};
|
||||
|
||||
tabTracker.on("tab-attached", listener);
|
||||
return () => {
|
||||
tabTracker.off("tab-attached", listener);
|
||||
};
|
||||
},
|
||||
}).api(),
|
||||
|
||||
onDetached: new EventManager({
|
||||
context,
|
||||
name: "tabs.onDetached",
|
||||
register: fire => {
|
||||
let listener = (eventName, event) => {
|
||||
fire.async(event.tabId, { oldWindowId: event.oldWindowId, oldPosition: event.oldPosition });
|
||||
};
|
||||
|
||||
tabTracker.on("tab-detached", listener);
|
||||
return () => {
|
||||
tabTracker.off("tab-detached", listener);
|
||||
};
|
||||
},
|
||||
}).api(),
|
||||
|
||||
onRemoved: new EventManager({
|
||||
context,
|
||||
name: "tabs.onRemoved",
|
||||
register: fire => {
|
||||
let listener = (eventName, event) => {
|
||||
fire.async(event.tabId, { windowId: event.windowId, isWindowClosing: event.isWindowClosing });
|
||||
};
|
||||
|
||||
tabTracker.on("tab-removed", listener);
|
||||
return () => {
|
||||
tabTracker.off("tab-removed", listener);
|
||||
};
|
||||
}
|
||||
}).api(),
|
||||
|
||||
onMoved: new EventManager({
|
||||
context,
|
||||
name: "tabs.onMoved",
|
||||
register: fire => {
|
||||
let moveListener = event => {
|
||||
let nativeTab = event.originalTarget;
|
||||
let nativeTabInfo = event.detail.tabInfo;
|
||||
let tabmail = nativeTab.ownerDocument.getElementById("tabmail");
|
||||
|
||||
fire.async(tabTracker.getId(nativeTabInfo), {
|
||||
windowId: windowTracker.getId(nativeTab.ownerGlobal),
|
||||
fromIndex: event.detail.idx,
|
||||
toIndex: tabmail.tabInfo.indexOf(nativeTabInfo)
|
||||
});
|
||||
};
|
||||
|
||||
windowTracker.addListener("TabMove", moveListener);
|
||||
return () => {
|
||||
windowTracker.removeListener("TabMove", moveListener);
|
||||
};
|
||||
}
|
||||
}).api(),
|
||||
|
||||
onUpdated: new TabsUpdateFilterEventManager(context).api(),
|
||||
|
||||
async create(createProperties) {
|
||||
let window = createProperties.windowId === null
|
||||
? windowTracker.topNormalWindow
|
||||
: windowTracker.getWindow(createProperties.windowId, context);
|
||||
let tabmail = window.document.getElementById("tabmail");
|
||||
|
||||
let url;
|
||||
if (createProperties.url !== null) {
|
||||
url = context.uri.resolve(createProperties.url);
|
||||
|
||||
if (!context.checkLoadURL(url, { dontReportErrors: true })) {
|
||||
return Promise.reject({ message: `Illegal URL: ${url}` });
|
||||
}
|
||||
}
|
||||
|
||||
let currentTab = tabmail.selectedTab;
|
||||
|
||||
let active = true;
|
||||
if (createProperties.active !== null) {
|
||||
active = createProperties.active;
|
||||
}
|
||||
|
||||
tabListener.initTabReady();
|
||||
|
||||
let nativeTabInfo = tabmail.openTab("contentTab", {
|
||||
contentPage: url || "about:blank",
|
||||
background: !active
|
||||
});
|
||||
|
||||
if (createProperties.index !== null) {
|
||||
tabmail.moveTabTo(nativeTabInfo, createProperties.index);
|
||||
tabmail.updateCurrentTab();
|
||||
}
|
||||
|
||||
if (createProperties.url && createProperties.url !== "about:blank") {
|
||||
// Mark tabs as initializing, so operations like `executeScript` wait until the
|
||||
// requested URL is loaded
|
||||
tabListener.initializingTabs.add(nativeTabInfo);
|
||||
}
|
||||
|
||||
return tabManager.convert(nativeTabInfo, currentTab);
|
||||
},
|
||||
|
||||
async remove(tabs) {
|
||||
if (!Array.isArray(tabs)) {
|
||||
tabs = [tabs];
|
||||
}
|
||||
|
||||
for (let tabId of tabs) {
|
||||
let nativeTabInfo = tabTracker.getTab(tabId);
|
||||
let browser = getTabBrowser(nativeTabInfo);
|
||||
let tabmail = browser.ownerDocument.getElementById("tabmail");
|
||||
tabmail.closeTab(nativeTabInfo);
|
||||
}
|
||||
},
|
||||
|
||||
async update(tabId, updateProperties) {
|
||||
let nativeTabInfo = getTabOrActive(tabId);
|
||||
let browser = getTabBrowser(nativeTabInfo);
|
||||
let tabmail = browser.ownerDocument.getElementById("tabmail");
|
||||
|
||||
if (updateProperties.url !== null) {
|
||||
let url = context.uri.resolve(updateProperties.url);
|
||||
|
||||
if (!context.checkLoadURL(url, { dontReportErrors: true })) {
|
||||
return Promise.reject({ message: `Illegal URL: ${url}` });
|
||||
}
|
||||
|
||||
let options = {
|
||||
flags: updateProperties.loadReplace
|
||||
? Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY
|
||||
: Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
|
||||
triggeringPrincipal: context.principal,
|
||||
};
|
||||
browser.loadURI(url, options);
|
||||
}
|
||||
|
||||
if (updateProperties.active !== null) {
|
||||
if (updateProperties.active) {
|
||||
tabmail.selectedTab = nativeTabInfo;
|
||||
} else {
|
||||
// Not sure what to do here? Which tab should we select?
|
||||
}
|
||||
}
|
||||
|
||||
return tabManager.convert(nativeTabInfo);
|
||||
},
|
||||
|
||||
async reload(tabId, reloadProperties) {
|
||||
let nativeTabInfo = getTabOrActive(tabId);
|
||||
let browser = getTabBrowser(nativeTabInfo);
|
||||
|
||||
let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
|
||||
if (reloadProperties && reloadProperties.bypassCache) {
|
||||
flags |= Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
|
||||
}
|
||||
browser.reloadWithFlags(flags);
|
||||
},
|
||||
|
||||
async get(tabId) {
|
||||
return tabManager.get(tabId).convert();
|
||||
},
|
||||
|
||||
getCurrent() {
|
||||
let tabData;
|
||||
if (context.tabId) {
|
||||
tabData = tabManager.get(context.tabId).convert();
|
||||
}
|
||||
return Promise.resolve(tabData);
|
||||
},
|
||||
|
||||
async query(queryInfo) {
|
||||
if (!extension.hasPermission("tabs")) {
|
||||
if (queryInfo.url !== null || queryInfo.title !== null) {
|
||||
return Promise.reject({ message: 'The "tabs" permission is required to use the query API with the "url" or "title" parameters' });
|
||||
}
|
||||
}
|
||||
|
||||
queryInfo = Object.assign({}, queryInfo);
|
||||
|
||||
if (queryInfo.url !== null) {
|
||||
queryInfo.url = new MatchPatternSet([].concat(queryInfo.url));
|
||||
}
|
||||
if (queryInfo.title !== null) {
|
||||
queryInfo.title = new MatchGlob(queryInfo.title);
|
||||
}
|
||||
|
||||
// Make ext-tabs-base happy since it does a strict check
|
||||
queryInfo.screen = null;
|
||||
|
||||
return Array.from(tabManager.query(queryInfo, context), tab => tab.convert());
|
||||
},
|
||||
|
||||
async executeScript(tabId, details) {
|
||||
// TODO make this work
|
||||
let tab = await promiseTabWhenReady(tabId);
|
||||
|
||||
return tab.executeScript(context, details);
|
||||
},
|
||||
|
||||
async insertCSS(tabId, details) {
|
||||
// TODO make this work
|
||||
let tab = await promiseTabWhenReady(tabId);
|
||||
|
||||
return tab.insertCSS(context, details);
|
||||
},
|
||||
|
||||
async removeCSS(tabId, details) {
|
||||
// TODO make this work
|
||||
let tab = await promiseTabWhenReady(tabId);
|
||||
|
||||
return tab.removeCSS(context, details);
|
||||
},
|
||||
|
||||
async move(tabIds, moveProperties) {
|
||||
let tabsMoved = [];
|
||||
if (!Array.isArray(tabIds)) {
|
||||
tabIds = [tabIds];
|
||||
}
|
||||
|
||||
let destinationWindow = null;
|
||||
if (moveProperties.windowId !== null) {
|
||||
destinationWindow = windowTracker.getWindow(moveProperties.windowId);
|
||||
// Fail on an invalid window.
|
||||
if (!destinationWindow) {
|
||||
return Promise.reject({ message: `Invalid window ID: ${moveProperties.windowId}` });
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Indexes are maintained on a per window basis so that a call to
|
||||
move([tabA, tabB], {index: 0})
|
||||
-> tabA to 0, tabB to 1 if tabA and tabB are in the same window
|
||||
move([tabA, tabB], {index: 0})
|
||||
-> tabA to 0, tabB to 0 if tabA and tabB are in different windows
|
||||
*/
|
||||
let indexMap = new Map();
|
||||
let lastInsertion = new Map();
|
||||
|
||||
let tabs = tabIds.map(tabId => tabTracker.getTab(tabId));
|
||||
for (let nativeTabInfo of tabs) {
|
||||
// If the window is not specified, use the window from the tab.
|
||||
let browser = getTabBrowser(nativeTabInfo);
|
||||
|
||||
let srcwindow = browser.ownerGlobal;
|
||||
let tgtwindow = destinationWindow || browser.ownerGlobal;
|
||||
let tgttabmail = tgtwindow.document.getElementById("tabmail");
|
||||
let srctabmail = srcwindow.document.getElementById("tabmail");
|
||||
|
||||
// If we are not moving the tab to a different window, and the window
|
||||
// only has one tab, do nothing.
|
||||
if (srcwindow == tgtwindow && srctabmail.tabInfo.length === 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let insertionPoint = indexMap.get(tgtwindow) || moveProperties.index;
|
||||
// If the index is -1 it should go to the end of the tabs.
|
||||
if (insertionPoint == -1) {
|
||||
insertionPoint = tgttabmail.tabInfo.length;
|
||||
}
|
||||
|
||||
let tabPosition = srctabmail.tabInfo.indexOf(nativeTabInfo);
|
||||
|
||||
// If this is not the first tab to be inserted into this window and
|
||||
// the insertion point is the same as the last insertion and
|
||||
// the tab is further to the right than the current insertion point
|
||||
// then you need to bump up the insertion point. See bug 1323311.
|
||||
if (lastInsertion.has(tgtwindow) &&
|
||||
lastInsertion.get(tgtwindow) === insertionPoint &&
|
||||
tabPosition > insertionPoint) {
|
||||
insertionPoint++;
|
||||
indexMap.set(tgtwindow, insertionPoint);
|
||||
}
|
||||
|
||||
if (srcwindow == tgtwindow) {
|
||||
// If the window we are moving is the same, just move the tab.
|
||||
tgttabmail.moveTabTo(nativeTabInfo, insertionPoint);
|
||||
} else {
|
||||
// If the window we are moving the tab in is different, then move the tab
|
||||
// to the new window.
|
||||
srctabmail.replaceTabWithWindow(nativeTabInfo, tgtwindow, insertionPoint);
|
||||
nativeTabInfo = tgttabmail.tabInfo[insertionPoint] ||
|
||||
tgttabmail.tabInfo[tgttabmail.tabInfo.length - 1];
|
||||
}
|
||||
lastInsertion.set(tgtwindow, tabPosition);
|
||||
tabsMoved.push(nativeTabInfo);
|
||||
}
|
||||
|
||||
return tabsMoved.map(nativeTabInfo => tabManager.convert(nativeTabInfo));
|
||||
},
|
||||
|
||||
duplicate(tabId) {
|
||||
let nativeTabInfo = tabTracker.getTab(tabId);
|
||||
let browser = getTabBrowser(nativeTabInfo);
|
||||
let tabmail = browser.ownerDocument.getElementById("tabmail");
|
||||
|
||||
// This is our best approximation of duplicating tabs. It might produce unreliable results
|
||||
let state = tabmail.persistTab(nativeTabInfo);
|
||||
let mode = tabmail.tabModes[state.mode];
|
||||
state.state.duplicate = true;
|
||||
|
||||
if (mode.tabs.length && mode.tabs.length == mode.maxTabs) {
|
||||
throw new ExtensionError(`Maximum number of ${state.mode} tabs reached`);
|
||||
} else {
|
||||
tabmail.restoreTab(state);
|
||||
return tabManager.convert(mode.tabs[mode.tabs.length - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
|
@ -1,212 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
// The ext-* files are imported into the same scopes.
|
||||
/* import-globals-from ext-mail.js */
|
||||
this.windows = class extends ExtensionAPI {
|
||||
getAPI(context) {
|
||||
const { extension } = context;
|
||||
const { windowManager } = extension;
|
||||
|
||||
return {
|
||||
windows: {
|
||||
onCreated: new WindowEventManager(context, "windows.onCreated", "domwindowopened", (fire, window) => {
|
||||
fire.async(windowManager.convert(window));
|
||||
}).api(),
|
||||
|
||||
onRemoved: new WindowEventManager(context, "windows.onRemoved", "domwindowclosed", (fire, window) => {
|
||||
fire.async(windowTracker.getId(window));
|
||||
}).api(),
|
||||
|
||||
onFocusChanged: new EventManager({
|
||||
context,
|
||||
name: "windows.onFocusChanged",
|
||||
register: fire => {
|
||||
// Keep track of the last windowId used to fire an onFocusChanged event
|
||||
let lastOnFocusChangedWindowId;
|
||||
|
||||
let listener = event => {
|
||||
// Wait a tick to avoid firing a superfluous WINDOW_ID_NONE
|
||||
// event when switching focus between two Firefox windows.
|
||||
Promise.resolve().then(() => {
|
||||
let window = Services.focus.activeWindow;
|
||||
let windowId = window ? windowTracker.getId(window) : Window.WINDOW_ID_NONE;
|
||||
if (windowId !== lastOnFocusChangedWindowId) {
|
||||
fire.async(windowId);
|
||||
lastOnFocusChangedWindowId = windowId;
|
||||
}
|
||||
});
|
||||
};
|
||||
windowTracker.addListener("focus", listener);
|
||||
windowTracker.addListener("blur", listener);
|
||||
return () => {
|
||||
windowTracker.removeListener("focus", listener);
|
||||
windowTracker.removeListener("blur", listener);
|
||||
};
|
||||
}
|
||||
}).api(),
|
||||
|
||||
get: function(windowId, getInfo) {
|
||||
let window = windowTracker.getWindow(windowId, context);
|
||||
if (!window) {
|
||||
return Promise.reject({ message: `Invalid window ID: ${windowId}` });
|
||||
}
|
||||
return Promise.resolve(windowManager.convert(window, getInfo));
|
||||
},
|
||||
|
||||
getCurrent: function(getInfo) {
|
||||
let window = context.currentWindow || windowTracker.topWindow;
|
||||
return Promise.resolve(windowManager.convert(window, getInfo));
|
||||
},
|
||||
|
||||
getLastFocused: function(getInfo) {
|
||||
let window = windowTracker.topWindow;
|
||||
return Promise.resolve(windowManager.convert(window, getInfo));
|
||||
},
|
||||
|
||||
getAll: function(getInfo) {
|
||||
let doNotCheckTypes = getInfo === null || getInfo.windowTypes === null;
|
||||
|
||||
function typeFilter(win) {
|
||||
return doNotCheckTypes || getInfo.windowTypes.includes(win.type);
|
||||
}
|
||||
|
||||
let windows = Array.from(windowManager.getAll(), win => win.convert(getInfo)).filter(typeFilter);
|
||||
return Promise.resolve(windows);
|
||||
},
|
||||
|
||||
create: function(createData) {
|
||||
let needResize = (createData.left !== null || createData.top !== null ||
|
||||
createData.width !== null || createData.height !== null);
|
||||
|
||||
if (needResize) {
|
||||
if (createData.state !== null && createData.state != "normal") {
|
||||
return Promise.reject({ message: `"state": "${createData.state}" may not be combined with "left", "top", "width", or "height"` });
|
||||
}
|
||||
createData.state = "normal";
|
||||
}
|
||||
|
||||
let createWindowArgs = (urls) => {
|
||||
let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
|
||||
let actionData = {
|
||||
action: "open",
|
||||
tabs: urls.map(url => ({ tabType: "contentTab", tabParams: { contentPage: url } }))
|
||||
};
|
||||
actionData.wrappedJSObject = actionData;
|
||||
args.appendElement(null);
|
||||
args.appendElement(actionData);
|
||||
return args;
|
||||
};
|
||||
|
||||
let window;
|
||||
let wantNormalWindow = createData.type === null || createData.type == "normal";
|
||||
let features = ["chrome"];
|
||||
if (wantNormalWindow) {
|
||||
features.push("dialog=no", "all", "status", "toolbar");
|
||||
|
||||
if (createData.incognito) {
|
||||
// A private mode mail window isn't useful for Thunderbird
|
||||
return Promise.reject({ message: "`incognito` is currently not supported for normal windows" });
|
||||
}
|
||||
} else {
|
||||
// All other types create "popup"-type windows by default.
|
||||
features.push("dialog", "resizable", "minimizable", "centerscreen", "titlebar", "close");
|
||||
|
||||
if (createData.incognito) {
|
||||
features.push("private");
|
||||
}
|
||||
}
|
||||
|
||||
if (createData.tabId !== null) {
|
||||
if (createData.url !== null) {
|
||||
return Promise.reject({ message: "`tabId` may not be used in conjunction with `url`" });
|
||||
}
|
||||
|
||||
if (createData.allowScriptsToClose) {
|
||||
return Promise.reject({ message: "`tabId` may not be used in conjunction with `allowScriptsToClose`" });
|
||||
}
|
||||
|
||||
let nativeTabInfo = tabTracker.getTab(createData.tabId);
|
||||
let tabmail = getTabBrowser(nativeTabInfo).ownerDocument.getElementById("tabmail");
|
||||
let targetType = wantNormalWindow ? null : "popup";
|
||||
window = tabmail.replaceTabWithWindow(nativeTabInfo, targetType)[0];
|
||||
} else if (createData.url !== null) { // eslint-disable-line no-negated-condition
|
||||
let uris = Array.isArray(createData.url) ? createData.url : [createData.url];
|
||||
let args = createWindowArgs(uris);
|
||||
window = Services.ww.openWindow(null, "chrome://messenger/content/", "_blank", features.join(","), args);
|
||||
} else {
|
||||
let args = null;
|
||||
if (!wantNormalWindow) {
|
||||
args = createWindowArgs(["about:blank"]);
|
||||
}
|
||||
window = Services.ww.openWindow(null, "chrome://messenger/content/", "_blank", features.join(","), args);
|
||||
}
|
||||
|
||||
let win = windowManager.getWrapper(window);
|
||||
win.updateGeometry(createData);
|
||||
|
||||
// TODO: focused, type
|
||||
|
||||
return new Promise(resolve => {
|
||||
window.addEventListener("load", () => {
|
||||
resolve();
|
||||
}, { once: true });
|
||||
}).then(() => {
|
||||
if (["minimized", "fullscreen", "docked", "normal", "maximized"].includes(createData.state)) {
|
||||
win.state = createData.state;
|
||||
}
|
||||
return win.convert({ populate: true });
|
||||
});
|
||||
},
|
||||
|
||||
update: function(windowId, updateInfo) {
|
||||
if (updateInfo.state !== null && updateInfo.state != "normal") {
|
||||
if (updateInfo.left !== null || updateInfo.top !== null ||
|
||||
updateInfo.width !== null || updateInfo.height !== null) {
|
||||
return Promise.reject({ message: `"state": "${updateInfo.state}" may not be combined with "left", "top", "width", or "height"` });
|
||||
}
|
||||
}
|
||||
|
||||
let win = windowManager.get(windowId, context);
|
||||
if (updateInfo.focused) {
|
||||
Services.focus.activeWindow = win.window;
|
||||
}
|
||||
|
||||
if (updateInfo.state !== null) {
|
||||
win.state = updateInfo.state;
|
||||
}
|
||||
|
||||
if (updateInfo.drawAttention) {
|
||||
// Bug 1257497 - Firefox can't cancel attention actions.
|
||||
win.window.getAttention();
|
||||
}
|
||||
|
||||
win.updateGeometry(updateInfo);
|
||||
|
||||
if (updateInfo.titlePreface !== null) {
|
||||
win.setTitlePreface(updateInfo.titlePreface);
|
||||
win.window.gBrowser.updateTitlebar();
|
||||
}
|
||||
|
||||
// TODO: All the other properties, focused=false...
|
||||
|
||||
return Promise.resolve(win.convert());
|
||||
},
|
||||
|
||||
remove: function(windowId) {
|
||||
let window = windowTracker.getWindow(windowId, context);
|
||||
window.close();
|
||||
|
||||
return new Promise(resolve => {
|
||||
let listener = () => {
|
||||
windowTracker.removeListener("domwindowclosed", listener);
|
||||
resolve();
|
||||
};
|
||||
windowTracker.addListener("domwindowclosed", listener);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
|
@ -1,27 +0,0 @@
|
|||
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,685 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
[
|
||||
{
|
||||
"namespace": "manifest",
|
||||
"types": [
|
||||
{
|
||||
"$extend": "OptionalPermission",
|
||||
"choices": [{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"activeTab",
|
||||
"tabs",
|
||||
"tabHide"
|
||||
]
|
||||
}]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"namespace": "tabs",
|
||||
"description": "Use the <code>browser.tabs</code> API to interact with the browser's tab system. You can use this API to create, modify, and rearrange tabs in the browser.",
|
||||
"types": [
|
||||
{
|
||||
"id": "Tab",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {"type": "integer", "minimum": -1, "optional": true, "description": "The ID of the tab. Tab IDs are unique within a browser session. Under some circumstances a Tab may not be assigned an ID, for example when querying foreign tabs using the $(ref:sessions) API, in which case a session ID may be present. Tab ID can also be set to $(ref:tabs.TAB_ID_NONE) for apps and devtools windows."},
|
||||
"index": {"type": "integer", "minimum": -1, "description": "The zero-based index of the tab within its window."},
|
||||
"windowId": {"type": "integer", "optional": true, "minimum": 0, "description": "The ID of the window the tab is contained within."},
|
||||
"selected": {"type": "boolean", "description": "Whether the tab is selected.", "deprecated": "Please use $(ref:tabs.Tab.highlighted).", "unsupported": true},
|
||||
"highlighted": {"type": "boolean", "description": "Whether the tab is highlighted. Works as an alias of active"},
|
||||
"active": {"type": "boolean", "description": "Whether the tab is active in its window. (Does not necessarily mean the window is focused.)"},
|
||||
"lastAccessed": {"type": "integer", "optional": true, "description": "The last time the tab was accessed as the number of milliseconds since epoch."},
|
||||
"url": {"type": "string", "optional": true, "permissions": ["tabs"], "description": "The URL the tab is displaying. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission."},
|
||||
"title": {"type": "string", "optional": true, "permissions": ["tabs"], "description": "The title of the tab. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission."},
|
||||
"favIconUrl": {"type": "string", "optional": true, "permissions": ["tabs"], "description": "The URL of the tab's favicon. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission. It may also be an empty string if the tab is loading."},
|
||||
"status": {"type": "string", "optional": true, "description": "Either <em>loading</em> or <em>complete</em>."},
|
||||
"width": {"type": "integer", "optional": true, "description": "The width of the tab in pixels."},
|
||||
"height": {"type": "integer", "optional": true, "description": "The height of the tab in pixels."}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "TabStatus",
|
||||
"type": "string",
|
||||
"enum": ["loading", "complete"],
|
||||
"description": "Whether the tabs have completed loading."
|
||||
},
|
||||
{
|
||||
"id": "WindowType",
|
||||
"type": "string",
|
||||
"enum": ["normal", "popup", "panel", "app", "devtools"],
|
||||
"description": "The type of window."
|
||||
},
|
||||
{
|
||||
"id": "UpdatePropertyName",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"favIconUrl",
|
||||
"status",
|
||||
"title"
|
||||
],
|
||||
"description": "Event names supported in onUpdated."
|
||||
},
|
||||
{
|
||||
"id": "UpdateFilter",
|
||||
"type": "object",
|
||||
"description": "An object describing filters to apply to tabs.onUpdated events.",
|
||||
"properties": {
|
||||
"urls": {
|
||||
"type": "array",
|
||||
"description": "A list of URLs or URL patterns. Events that cannot match any of the URLs will be filtered out. Filtering with urls requires the <code>\"tabs\"</code> or <code>\"activeTab\"</code> permission.",
|
||||
"optional": true,
|
||||
"items": { "type": "string" },
|
||||
"minItems": 1
|
||||
},
|
||||
"properties": {
|
||||
"type": "array",
|
||||
"optional": true,
|
||||
"description": "A list of property names. Events that do not match any of the names will be filtered out.",
|
||||
"items": { "$ref": "UpdatePropertyName" },
|
||||
"minItems": 1
|
||||
},
|
||||
"tabId": { "type": "integer", "optional": true },
|
||||
"windowId": { "type": "integer", "optional": true }
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"TAB_ID_NONE": {
|
||||
"value": -1,
|
||||
"description": "An ID which represents the absence of a browser tab."
|
||||
}
|
||||
},
|
||||
"functions": [
|
||||
{
|
||||
"name": "get",
|
||||
"type": "function",
|
||||
"description": "Retrieves details about the specified tab.",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "tabId",
|
||||
"minimum": 0
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"parameters": [
|
||||
{"name": "tab", "$ref": "Tab"}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "getCurrent",
|
||||
"type": "function",
|
||||
"description": "Gets the tab that this script call is being made from. May be undefined if called from a non-tab context (for example: a background page or popup view).",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "tab",
|
||||
"$ref": "Tab",
|
||||
"optional": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "create",
|
||||
"type": "function",
|
||||
"description": "Creates a new tab.",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "object",
|
||||
"name": "createProperties",
|
||||
"properties": {
|
||||
"windowId": {
|
||||
"type": "integer",
|
||||
"minimum": -2,
|
||||
"optional": true,
|
||||
"description": "The window to create the new tab in. Defaults to the $(topic:current-window)[current window]."
|
||||
},
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"optional": true,
|
||||
"description": "The position the tab should take in the window. The provided value will be clamped to between zero and the number of tabs in the window."
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"description": "The URL to navigate the tab to initially. Fully-qualified URLs must include a scheme (i.e. 'http://www.google.com', not 'www.google.com'). Relative URLs will be relative to the current page within the extension. Defaults to the New Tab Page."
|
||||
},
|
||||
"active": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Whether the tab should become the active tab in the window. Does not affect whether the window is focused (see $(ref:windows.update)). Defaults to <var>true</var>."
|
||||
},
|
||||
"selected": {
|
||||
"deprecated": "Please use <em>active</em>.",
|
||||
"unsupported": true,
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Whether the tab should become the selected tab in the window. Defaults to <var>true</var>"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"optional": true,
|
||||
"parameters": [
|
||||
{
|
||||
"name": "tab",
|
||||
"$ref": "Tab",
|
||||
"optional": true,
|
||||
"description": "Details about the created tab. Will contain the ID of the new tab."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "duplicate",
|
||||
"type": "function",
|
||||
"description": "Duplicates a tab.",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "tabId",
|
||||
"minimum": 0,
|
||||
"description": "The ID of the tab which is to be duplicated."
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"optional": true,
|
||||
"parameters": [
|
||||
{
|
||||
"name": "tab",
|
||||
"optional": true,
|
||||
"description": "Details about the duplicated tab. The $(ref:tabs.Tab) object doesn't contain <code>url</code>, <code>title</code> and <code>favIconUrl</code> if the <code>\"tabs\"</code> permission has not been requested.",
|
||||
"$ref": "Tab"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "query",
|
||||
"type": "function",
|
||||
"description": "Gets all tabs that have the specified properties, or all tabs if no properties are specified.",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "object",
|
||||
"name": "queryInfo",
|
||||
"properties": {
|
||||
"active": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Whether the tabs are active in their windows."
|
||||
},
|
||||
"highlighted": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Whether the tabs are highlighted. Works as an alias of active."
|
||||
},
|
||||
"currentWindow": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Whether the tabs are in the $(topic:current-window)[current window]."
|
||||
},
|
||||
"lastFocusedWindow": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Whether the tabs are in the last focused window."
|
||||
},
|
||||
"status": {
|
||||
"$ref": "TabStatus",
|
||||
"optional": true,
|
||||
"description": "Whether the tabs have completed loading."
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"description": "Match page titles against a pattern."
|
||||
},
|
||||
"url": {
|
||||
"choices": [
|
||||
{"type": "string"},
|
||||
{"type": "array", "items": {"type": "string"}}
|
||||
],
|
||||
"optional": true,
|
||||
"description": "Match tabs against one or more $(topic:match_patterns)[URL patterns]. Note that fragment identifiers are not matched."
|
||||
},
|
||||
"windowId": {
|
||||
"type": "integer",
|
||||
"optional": true,
|
||||
"minimum": -2,
|
||||
"description": "The ID of the parent window, or $(ref:windows.WINDOW_ID_CURRENT) for the $(topic:current-window)[current window]."
|
||||
},
|
||||
"windowType": {
|
||||
"$ref": "WindowType",
|
||||
"optional": true,
|
||||
"description": "The type of window the tabs are in."
|
||||
},
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"optional": true,
|
||||
"minimum": 0,
|
||||
"description": "The position of the tabs within their windows."
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "result",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "Tab"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "update",
|
||||
"type": "function",
|
||||
"description": "Modifies the properties of a tab. Properties that are not specified in <var>updateProperties</var> are not modified.",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "tabId",
|
||||
"minimum": 0,
|
||||
"optional": true,
|
||||
"description": "Defaults to the selected tab of the $(topic:current-window)[current window]."
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"name": "updateProperties",
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"description": "A URL to navigate the tab to."
|
||||
},
|
||||
"active": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Whether the tab should be active. Does not affect whether the window is focused (see $(ref:windows.update))."
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"optional": true,
|
||||
"parameters": [
|
||||
{
|
||||
"name": "tab",
|
||||
"$ref": "Tab",
|
||||
"optional": true,
|
||||
"description": "Details about the updated tab. The $(ref:tabs.Tab) object doesn't contain <code>url</code>, <code>title</code> and <code>favIconUrl</code> if the <code>\"tabs\"</code> permission has not been requested."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "move",
|
||||
"type": "function",
|
||||
"description": "Moves one or more tabs to a new position within its window, or to a new window. Note that tabs can only be moved to and from normal (window.type === \"normal\") windows.",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "tabIds",
|
||||
"description": "The tab or list of tabs to move.",
|
||||
"choices": [
|
||||
{"type": "integer", "minimum": 0},
|
||||
{"type": "array", "items": {"type": "integer", "minimum": 0}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"name": "moveProperties",
|
||||
"properties": {
|
||||
"windowId": {
|
||||
"type": "integer",
|
||||
"minimum": -2,
|
||||
"optional": true,
|
||||
"description": "Defaults to the window the tab is currently in."
|
||||
},
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"minimum": -1,
|
||||
"description": "The position to move the window to. -1 will place the tab at the end of the window."
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"optional": true,
|
||||
"parameters": [
|
||||
{
|
||||
"name": "tabs",
|
||||
"description": "Details about the moved tabs.",
|
||||
"choices": [
|
||||
{"$ref": "Tab"},
|
||||
{"type": "array", "items": {"$ref": "Tab"}}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "reload",
|
||||
"type": "function",
|
||||
"description": "Reload a tab.",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "tabId",
|
||||
"minimum": 0,
|
||||
"optional": true,
|
||||
"description": "The ID of the tab to reload; defaults to the selected tab of the current window."
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"name": "reloadProperties",
|
||||
"optional": true,
|
||||
"properties": {
|
||||
"bypassCache": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Whether using any local cache. Default is false."
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"optional": true,
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "remove",
|
||||
"type": "function",
|
||||
"description": "Closes one or more tabs.",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "tabIds",
|
||||
"description": "The tab or list of tabs to close.",
|
||||
"choices": [
|
||||
{"type": "integer", "minimum": 0},
|
||||
{"type": "array", "items": {"type": "integer", "minimum": 0}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"optional": true,
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "executeScript",
|
||||
"type": "function",
|
||||
"description": "Injects JavaScript code into a page. For details, see the $(topic:content_scripts)[programmatic injection] section of the content scripts doc.",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "tabId",
|
||||
"minimum": 0,
|
||||
"optional": true,
|
||||
"description": "The ID of the tab in which to run the script; defaults to the active tab of the current window."
|
||||
},
|
||||
{
|
||||
"$ref": "extensionTypes.InjectDetails",
|
||||
"name": "details",
|
||||
"description": "Details of the script to run."
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"optional": true,
|
||||
"description": "Called after all the JavaScript has been executed.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "result",
|
||||
"optional": true,
|
||||
"type": "array",
|
||||
"items": {"type": "any"},
|
||||
"description": "The result of the script in every injected frame."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "insertCSS",
|
||||
"type": "function",
|
||||
"description": "Injects CSS into a page. For details, see the $(topic:content_scripts)[programmatic injection] section of the content scripts doc.",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "tabId",
|
||||
"minimum": 0,
|
||||
"optional": true,
|
||||
"description": "The ID of the tab in which to insert the CSS; defaults to the active tab of the current window."
|
||||
},
|
||||
{
|
||||
"$ref": "extensionTypes.InjectDetails",
|
||||
"name": "details",
|
||||
"description": "Details of the CSS text to insert."
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"optional": true,
|
||||
"description": "Called when all the CSS has been inserted.",
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "removeCSS",
|
||||
"type": "function",
|
||||
"description": "Removes injected CSS from a page. For details, see the $(topic:content_scripts)[programmatic injection] section of the content scripts doc.",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "tabId",
|
||||
"minimum": 0,
|
||||
"optional": true,
|
||||
"description": "The ID of the tab from which to remove the injected CSS; defaults to the active tab of the current window."
|
||||
},
|
||||
{
|
||||
"$ref": "extensionTypes.InjectDetails",
|
||||
"name": "details",
|
||||
"description": "Details of the CSS text to remove."
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"optional": true,
|
||||
"description": "Called when all the CSS has been removed.",
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"name": "onCreated",
|
||||
"type": "function",
|
||||
"description": "Fired when a tab is created. Note that the tab's URL may not be set at the time this event fired, but you can listen to onUpdated events to be notified when a URL is set.",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "Tab",
|
||||
"name": "tab",
|
||||
"description": "Details of the tab that was created."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "onUpdated",
|
||||
"type": "function",
|
||||
"description": "Fired when a tab is updated.",
|
||||
"parameters": [
|
||||
{"type": "integer", "name": "tabId", "minimum": 0},
|
||||
{
|
||||
"type": "object",
|
||||
"name": "changeInfo",
|
||||
"description": "Lists the changes to the state of the tab that was updated.",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"description": "The status of the tab. Can be either <em>loading</em> or <em>complete</em>."
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"description": "The tab's URL if it has changed."
|
||||
},
|
||||
"favIconUrl": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"description": "The tab's new favicon URL."
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"$ref": "Tab",
|
||||
"name": "tab",
|
||||
"description": "Gives the state of the tab that was updated."
|
||||
}
|
||||
],
|
||||
"extraParameters": [
|
||||
{
|
||||
"$ref": "UpdateFilter",
|
||||
"name": "filter",
|
||||
"optional": true,
|
||||
"description": "A set of filters that restricts the events that will be sent to this listener."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "onMoved",
|
||||
"type": "function",
|
||||
"description": "Fired when a tab is moved within a window. Only one move event is fired, representing the tab the user directly moved. Move events are not fired for the other tabs that must move in response. This event is not fired when a tab is moved between windows. For that, see $(ref:tabs.onDetached).",
|
||||
"parameters": [
|
||||
{"type": "integer", "name": "tabId", "minimum": 0},
|
||||
{
|
||||
"type": "object",
|
||||
"name": "moveInfo",
|
||||
"properties": {
|
||||
"windowId": {"type": "integer", "minimum": 0},
|
||||
"fromIndex": {"type": "integer", "minimum": 0},
|
||||
"toIndex": {"type": "integer", "minimum": 0}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "onActivated",
|
||||
"type": "function",
|
||||
"description": "Fires when the active tab in a window changes. Note that the tab's URL may not be set at the time this event fired, but you can listen to onUpdated events to be notified when a URL is set.",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "object",
|
||||
"name": "activeInfo",
|
||||
"properties": {
|
||||
"tabId": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "The ID of the tab that has become active."
|
||||
},
|
||||
"windowId": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "The ID of the window the active tab changed inside of."
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "onDetached",
|
||||
"type": "function",
|
||||
"description": "Fired when a tab is detached from a window, for example because it is being moved between windows.",
|
||||
"parameters": [
|
||||
{"type": "integer", "name": "tabId", "minimum": 0},
|
||||
{
|
||||
"type": "object",
|
||||
"name": "detachInfo",
|
||||
"properties": {
|
||||
"oldWindowId": {"type": "integer", "minimum": 0},
|
||||
"oldPosition": {"type": "integer", "minimum": 0}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "onAttached",
|
||||
"type": "function",
|
||||
"description": "Fired when a tab is attached to a window, for example because it was moved between windows.",
|
||||
"parameters": [
|
||||
{"type": "integer", "name": "tabId", "minimum": 0},
|
||||
{
|
||||
"type": "object",
|
||||
"name": "attachInfo",
|
||||
"properties": {
|
||||
"newWindowId": {"type": "integer", "minimum": 0},
|
||||
"newPosition": {"type": "integer", "minimum": 0}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "onRemoved",
|
||||
"type": "function",
|
||||
"description": "Fired when a tab is closed.",
|
||||
"parameters": [
|
||||
{"type": "integer", "name": "tabId", "minimum": 0},
|
||||
{
|
||||
"type": "object",
|
||||
"name": "removeInfo",
|
||||
"properties": {
|
||||
"windowId": {"type": "integer", "minimum": 0, "description": "The window whose tab is closed." },
|
||||
"isWindowClosing": {"type": "boolean", "description": "True when the tab is being closed because its window is being closed." }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -1,523 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
[
|
||||
{
|
||||
"namespace": "windows",
|
||||
"description": "Use the <code>browser.windows</code> API to interact with browser windows. You can use this API to create, modify, and rearrange windows in the browser.",
|
||||
"types": [
|
||||
{
|
||||
"id": "WindowType",
|
||||
"type": "string",
|
||||
"description": "The type of browser window this is. Under some circumstances a Window may not be assigned type property, for example when querying closed windows from the $(ref:sessions) API.",
|
||||
"enum": ["normal", "popup", "panel", "app", "devtools"]
|
||||
},
|
||||
{
|
||||
"id": "WindowState",
|
||||
"type": "string",
|
||||
"description": "The state of this browser window. Under some circumstances a Window may not be assigned state property, for example when querying closed windows from the $(ref:sessions) API.",
|
||||
"enum": ["normal", "minimized", "maximized", "fullscreen", "docked"]
|
||||
},
|
||||
{
|
||||
"id": "Window",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"optional": true,
|
||||
"minimum": 0,
|
||||
"description": "The ID of the window. Window IDs are unique within a browser session. Under some circumstances a Window may not be assigned an ID, for example when querying windows using the $(ref:sessions) API, in which case a session ID may be present."
|
||||
},
|
||||
"focused": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the window is currently the focused window."
|
||||
},
|
||||
"top": {
|
||||
"type": "integer",
|
||||
"optional": true,
|
||||
"description": "The offset of the window from the top edge of the screen in pixels. Under some circumstances a Window may not be assigned top property, for example when querying closed windows from the $(ref:sessions) API."
|
||||
},
|
||||
"left": {
|
||||
"type": "integer",
|
||||
"optional": true,
|
||||
"description": "The offset of the window from the left edge of the screen in pixels. Under some circumstances a Window may not be assigned left property, for example when querying closed windows from the $(ref:sessions) API."
|
||||
},
|
||||
"width": {
|
||||
"type": "integer",
|
||||
"optional": true,
|
||||
"description": "The width of the window, including the frame, in pixels. Under some circumstances a Window may not be assigned width property, for example when querying closed windows from the $(ref:sessions) API."
|
||||
},
|
||||
"height": {
|
||||
"type": "integer",
|
||||
"optional": true,
|
||||
"description": "The height of the window, including the frame, in pixels. Under some circumstances a Window may not be assigned height property, for example when querying closed windows from the $(ref:sessions) API."
|
||||
},
|
||||
"tabs": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "tabs.Tab" },
|
||||
"optional": true,
|
||||
"description": "Array of $(ref:tabs.Tab) objects representing the current tabs in the window."
|
||||
},
|
||||
"incognito": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the window is incognito."
|
||||
},
|
||||
"type": {
|
||||
"$ref": "WindowType",
|
||||
"optional": true,
|
||||
"description": "The type of browser window this is."
|
||||
},
|
||||
"state": {
|
||||
"$ref": "WindowState",
|
||||
"optional": true,
|
||||
"description": "The state of this browser window."
|
||||
},
|
||||
"alwaysOnTop": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the window is set to be always on top."
|
||||
},
|
||||
"sessionId": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"description": "The session ID used to uniquely identify a Window obtained from the $(ref:sessions) API."
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"description": "The title of the window. Read-only."
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CreateType",
|
||||
"type": "string",
|
||||
"description": "Specifies what type of browser window to create. The 'panel' and 'detached_panel' types create a popup unless the '--enable-panels' flag is set.",
|
||||
"enum": ["normal", "popup", "panel", "detached_panel"]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"WINDOW_ID_NONE": {
|
||||
"value": -1,
|
||||
"description": "The windowId value that represents the absence of a browser window."
|
||||
},
|
||||
"WINDOW_ID_CURRENT": {
|
||||
"value": -2,
|
||||
"description": "The windowId value that represents the $(topic:current-window)[current window]."
|
||||
}
|
||||
},
|
||||
"functions": [
|
||||
{
|
||||
"name": "get",
|
||||
"type": "function",
|
||||
"description": "Gets details about a window.",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "windowId",
|
||||
"minimum": -2
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"name": "getInfo",
|
||||
"optional": true,
|
||||
"description": "",
|
||||
"properties": {
|
||||
"populate": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "If true, the $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission."
|
||||
},
|
||||
"windowTypes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "WindowType"
|
||||
},
|
||||
"optional": true,
|
||||
"description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows."
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "window",
|
||||
"$ref": "Window"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "getCurrent",
|
||||
"type": "function",
|
||||
"description": "Gets the $(topic:current-window)[current window].",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "object",
|
||||
"name": "getInfo",
|
||||
"optional": true,
|
||||
"description": "",
|
||||
"properties": {
|
||||
"populate": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "If true, the $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission."
|
||||
},
|
||||
"windowTypes": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "WindowType" },
|
||||
"optional": true,
|
||||
"description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows."
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "window",
|
||||
"$ref": "Window"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "getLastFocused",
|
||||
"type": "function",
|
||||
"description": "Gets the window that was most recently focused — typically the window 'on top'.",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "object",
|
||||
"name": "getInfo",
|
||||
"optional": true,
|
||||
"description": "",
|
||||
"properties": {
|
||||
"populate": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "If true, the $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission."
|
||||
},
|
||||
"windowTypes": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "WindowType" },
|
||||
"optional": true,
|
||||
"description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows."
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "window",
|
||||
"$ref": "Window"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "getAll",
|
||||
"type": "function",
|
||||
"description": "Gets all windows.",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "object",
|
||||
"name": "getInfo",
|
||||
"optional": true,
|
||||
"description": "",
|
||||
"properties": {
|
||||
"populate": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "If true, each $(ref:windows.Window) object will have a <var>tabs</var> property that contains a list of the $(ref:tabs.Tab) objects for that window. The <code>Tab</code> objects only contain the <code>url</code>, <code>title</code> and <code>favIconUrl</code> properties if the extension's manifest file includes the <code>\"tabs\"</code> permission."
|
||||
},
|
||||
"windowTypes": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "WindowType" },
|
||||
"optional": true,
|
||||
"description": "If set, the $(ref:windows.Window) returned will be filtered based on its type. If unset the default filter is set to <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows."
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "windows",
|
||||
"type": "array",
|
||||
"items": { "$ref": "Window" }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "create",
|
||||
"type": "function",
|
||||
"description": "Creates (opens) a new browser with any optional sizing, position or default URL provided.",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "object",
|
||||
"name": "createData",
|
||||
"optional": true,
|
||||
"default": {},
|
||||
"properties": {
|
||||
"url": {
|
||||
"description": "A URL or array of URLs to open as tabs in the window. Fully-qualified URLs must include a scheme (i.e. 'http://www.google.com', not 'www.google.com'). Relative URLs will be relative to the current page within the extension. Defaults to the New Tab Page.",
|
||||
"optional": true,
|
||||
"choices": [
|
||||
{ "type": "string", "format": "relativeUrl" },
|
||||
{
|
||||
"type": "array",
|
||||
"items": { "type": "string", "format": "relativeUrl" }
|
||||
}
|
||||
]
|
||||
},
|
||||
"tabId": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"optional": true,
|
||||
"description": "The id of the tab for which you want to adopt to the new window."
|
||||
},
|
||||
"left": {
|
||||
"type": "integer",
|
||||
"optional": true,
|
||||
"description": "The number of pixels to position the new window from the left edge of the screen. If not specified, the new window is offset naturally from the last focused window. This value is ignored for panels."
|
||||
},
|
||||
"top": {
|
||||
"type": "integer",
|
||||
"optional": true,
|
||||
"description": "The number of pixels to position the new window from the top edge of the screen. If not specified, the new window is offset naturally from the last focused window. This value is ignored for panels."
|
||||
},
|
||||
"width": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"optional": true,
|
||||
"description": "The width in pixels of the new window, including the frame. If not specified defaults to a natural width."
|
||||
},
|
||||
"height": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"optional": true,
|
||||
"description": "The height in pixels of the new window, including the frame. If not specified defaults to a natural height."
|
||||
},
|
||||
"focused": {
|
||||
"unsupported": true,
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "If true, opens an active window. If false, opens an inactive window."
|
||||
},
|
||||
"incognito": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Whether the new window should be an incognito window."
|
||||
},
|
||||
"type": {
|
||||
"$ref": "CreateType",
|
||||
"optional": true,
|
||||
"description": "Specifies what type of browser window to create. The 'panel' and 'detached_panel' types create a popup unless the '--enable-panels' flag is set."
|
||||
},
|
||||
"state": {
|
||||
"$ref": "WindowState",
|
||||
"optional": true,
|
||||
"description": "The initial state of the window. The 'minimized', 'maximized' and 'fullscreen' states cannot be combined with 'left', 'top', 'width' or 'height'."
|
||||
},
|
||||
"allowScriptsToClose": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Allow scripts to close the window."
|
||||
},
|
||||
"titlePreface": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"description": "A string to add to the beginning of the window title."
|
||||
}
|
||||
},
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"optional": true,
|
||||
"parameters": [
|
||||
{
|
||||
"name": "window",
|
||||
"$ref": "Window",
|
||||
"description": "Contains details about the created window.",
|
||||
"optional": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "update",
|
||||
"type": "function",
|
||||
"description": "Updates the properties of a window. Specify only the properties that you want to change; unspecified properties will be left unchanged.",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "windowId",
|
||||
"minimum": -2
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"name": "updateInfo",
|
||||
"properties": {
|
||||
"left": {
|
||||
"type": "integer",
|
||||
"optional": true,
|
||||
"description": "The offset from the left edge of the screen to move the window to in pixels. This value is ignored for panels."
|
||||
},
|
||||
"top": {
|
||||
"type": "integer",
|
||||
"optional": true,
|
||||
"description": "The offset from the top edge of the screen to move the window to in pixels. This value is ignored for panels."
|
||||
},
|
||||
"width": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"optional": true,
|
||||
"description": "The width to resize the window to in pixels. This value is ignored for panels."
|
||||
},
|
||||
"height": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"optional": true,
|
||||
"description": "The height to resize the window to in pixels. This value is ignored for panels."
|
||||
},
|
||||
"focused": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "If true, brings the window to the front. If false, brings the next window in the z-order to the front."
|
||||
},
|
||||
"drawAttention": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "If true, causes the window to be displayed in a manner that draws the user's attention to the window, without changing the focused window. The effect lasts until the user changes focus to the window. This option has no effect if the window already has focus. Set to false to cancel a previous draw attention request."
|
||||
},
|
||||
"state": {
|
||||
"$ref": "WindowState",
|
||||
"optional": true,
|
||||
"description": "The new state of the window. The 'minimized', 'maximized' and 'fullscreen' states cannot be combined with 'left', 'top', 'width' or 'height'."
|
||||
},
|
||||
"titlePreface": {
|
||||
"type": "string",
|
||||
"optional": true,
|
||||
"description": "A string to add to the beginning of the window title."
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"optional": true,
|
||||
"parameters": [
|
||||
{
|
||||
"name": "window",
|
||||
"$ref": "Window"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "remove",
|
||||
"type": "function",
|
||||
"description": "Removes (closes) a window, and all the tabs inside it.",
|
||||
"async": "callback",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "windowId",
|
||||
"minimum": 0
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"name": "callback",
|
||||
"optional": true,
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"name": "onCreated",
|
||||
"type": "function",
|
||||
"description": "Fired when a window is created.",
|
||||
"filters": [
|
||||
{
|
||||
"name": "windowTypes",
|
||||
"type": "array",
|
||||
"items": { "$ref": "WindowType" },
|
||||
"description": "Conditions that the window's type being created must satisfy. By default it will satisfy <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows."
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "Window",
|
||||
"name": "window",
|
||||
"description": "Details of the window that was created."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "onRemoved",
|
||||
"type": "function",
|
||||
"description": "Fired when a window is removed (closed).",
|
||||
"filters": [
|
||||
{
|
||||
"name": "windowTypes",
|
||||
"type": "array",
|
||||
"items": { "$ref": "WindowType" },
|
||||
"description": "Conditions that the window's type being removed must satisfy. By default it will satisfy <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows."
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "windowId",
|
||||
"minimum": 0,
|
||||
"description": "ID of the removed window."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "onFocusChanged",
|
||||
"type": "function",
|
||||
"description": "Fired when the currently focused window changes. Will be $(ref:windows.WINDOW_ID_NONE) if all browser windows have lost focus. Note: On some Linux window managers, WINDOW_ID_NONE will always be sent immediately preceding a switch from one browser window to another.",
|
||||
"filters": [
|
||||
{
|
||||
"name": "windowTypes",
|
||||
"type": "array",
|
||||
"items": { "$ref": "WindowType" },
|
||||
"description": "Conditions that the window's type being removed must satisfy. By default it will satisfy <code>['app', 'normal', 'panel', 'popup']</code>, with <code>'app'</code> and <code>'panel'</code> window types limited to the extension's own windows."
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"type": "integer",
|
||||
"name": "windowId",
|
||||
"minimum": -1,
|
||||
"description": "ID of the newly focused window."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -10,7 +10,6 @@ DIRS += [
|
|||
'cloudfile',
|
||||
'devtools',
|
||||
'downloads',
|
||||
'extensions',
|
||||
'preferences',
|
||||
'addrbook',
|
||||
'migration',
|
||||
|
|
|
@ -575,7 +575,6 @@
|
|||
; [Extensions]
|
||||
@RESPATH@/components/extensions-toolkit.manifest
|
||||
@RESPATH@/components/extension-process-script.js
|
||||
@RESPATH@/components/extensions-mail.manifest
|
||||
|
||||
; [Personal Security Manager]
|
||||
;
|
||||
|
|
Загрузка…
Ссылка в новой задаче