Bug 1720662 - Implement a tab type property. r=john.bieling
Differential Revision: https://phabricator.services.mozilla.com/D120046 --HG-- extra : rebase_source : a4553df809fb0cd11ac4a41ca3eb7cc1219cdb9a
This commit is contained in:
Родитель
4d78cf5ad5
Коммит
564bad8f71
|
@ -313,6 +313,9 @@
|
|||
* that case, the tab type should probably only have a single mode unless
|
||||
* there are a number of similar modes that can gain from code sharing.
|
||||
*
|
||||
* If you're adding a new tab type, please update TabmailTab.type in
|
||||
* mail/components/extensions/parent/ext-mail.js.
|
||||
*
|
||||
* The tab type definition should include the following attributes:
|
||||
* * name: The name of the tab-type, mainly to aid in debugging.
|
||||
* * panelId or perTabPanel: If using a single tab panel, the id of the
|
||||
|
|
|
@ -807,7 +807,7 @@ this.compose = class extends ExtensionAPI {
|
|||
},
|
||||
async listAttachments(tabId) {
|
||||
let tab = tabManager.get(tabId);
|
||||
if (!tab.isComposeTab) {
|
||||
if (tab.type != "messageCompose") {
|
||||
throw new ExtensionError(`Invalid compose tab: ${tabId}`);
|
||||
}
|
||||
let bucket = tab.nativeTab.document.getElementById(
|
||||
|
@ -823,7 +823,7 @@ this.compose = class extends ExtensionAPI {
|
|||
},
|
||||
async addAttachment(tabId, data) {
|
||||
let tab = tabManager.get(tabId);
|
||||
if (!tab.isComposeTab) {
|
||||
if (tab.type != "messageCompose") {
|
||||
throw new ExtensionError(`Invalid compose tab: ${tabId}`);
|
||||
}
|
||||
|
||||
|
@ -840,7 +840,7 @@ this.compose = class extends ExtensionAPI {
|
|||
},
|
||||
async updateAttachment(tabId, attachmentId, data) {
|
||||
let tab = tabManager.get(tabId);
|
||||
if (!tab.isComposeTab) {
|
||||
if (tab.type != "messageCompose") {
|
||||
throw new ExtensionError(`Invalid compose tab: ${tabId}`);
|
||||
}
|
||||
if (!composeAttachmentTracker.hasAttachment(attachmentId)) {
|
||||
|
@ -868,7 +868,7 @@ this.compose = class extends ExtensionAPI {
|
|||
},
|
||||
async removeAttachment(tabId, attachmentId) {
|
||||
let tab = tabManager.get(tabId);
|
||||
if (!tab.isComposeTab) {
|
||||
if (tab.type != "messageCompose") {
|
||||
throw new ExtensionError(`Invalid compose tab: ${tabId}`);
|
||||
}
|
||||
if (!composeAttachmentTracker.hasAttachment(attachmentId)) {
|
||||
|
|
|
@ -35,8 +35,11 @@ XPCOMUtils.defineLazyPreferenceGetter(
|
|||
100
|
||||
);
|
||||
|
||||
const ADDRESS_BOOK_WINDOW_URI =
|
||||
"chrome://messenger/content/addressbook/addressbook.xhtml";
|
||||
const COMPOSE_WINDOW_URI =
|
||||
"chrome://messenger/content/messengercompose/messengercompose.xhtml";
|
||||
const MESSAGE_WINDOW_URI = "chrome://messenger/content/messageWindow.xhtml";
|
||||
const MESSAGE_PROTOCOLS = ["imap", "mailbox", "news", "nntp", "snews"];
|
||||
|
||||
(function() {
|
||||
|
@ -781,32 +784,36 @@ windowTracker = new WindowTracker();
|
|||
Object.assign(global, { tabTracker, windowTracker });
|
||||
|
||||
/**
|
||||
* Extension-specific wrapper around a Thunderbird tab.
|
||||
* Extension-specific wrapper around a Thunderbird tab. Note that for actual
|
||||
* tabs in the main window, some of these methods are overridden by the
|
||||
* TabmailTab subclass.
|
||||
*/
|
||||
class Tab extends TabBase {
|
||||
/** Returns true if this tab is a 3-pane tab. */
|
||||
get isMailTab() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns true if this tab is a compose window "tab". */
|
||||
get isComposeTab() {
|
||||
return (
|
||||
this.nativeTab.location &&
|
||||
this.nativeTab.location.href == COMPOSE_WINDOW_URI
|
||||
);
|
||||
/** What sort of tab is this? */
|
||||
get type() {
|
||||
switch (this.nativeTab.location?.href) {
|
||||
case ADDRESS_BOOK_WINDOW_URI:
|
||||
return "addressBook";
|
||||
case COMPOSE_WINDOW_URI:
|
||||
return "messageCompose";
|
||||
case MESSAGE_WINDOW_URI:
|
||||
return "messageDisplay";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Overrides the matches function to enable querying for 3-pane tabs. */
|
||||
matches(queryInfo, context) {
|
||||
let result = super.matches(queryInfo, context);
|
||||
return result && (!queryInfo.mailTab || this.isMailTab);
|
||||
return result && (!queryInfo.mailTab || this.type == "mail");
|
||||
}
|
||||
|
||||
/** Adds the mailTab property and removes some useless properties from a tab object. */
|
||||
convert(fallback) {
|
||||
let result = super.convert(fallback);
|
||||
result.mailTab = this.isMailTab;
|
||||
result.type = this.type;
|
||||
result.mailTab = result.type == "mail";
|
||||
|
||||
// These properties are not useful to Thunderbird extensions and are not returned.
|
||||
for (let key of [
|
||||
|
@ -837,7 +844,7 @@ class Tab extends TabBase {
|
|||
|
||||
/** Returns the XUL browser for the tab. */
|
||||
get browser() {
|
||||
if (this.isComposeTab) {
|
||||
if (this.type == "messageCompose") {
|
||||
return this.nativeTab.GetCurrentEditorElement();
|
||||
}
|
||||
if (this.nativeTab.getBrowser) {
|
||||
|
@ -847,7 +854,7 @@ class Tab extends TabBase {
|
|||
}
|
||||
|
||||
get innerWindowID() {
|
||||
if (this.isComposeTab) {
|
||||
if (this.type == "messageCompose") {
|
||||
return this.browser.contentWindow.windowUtils.currentInnerWindowID;
|
||||
}
|
||||
return super.innerWindowID;
|
||||
|
@ -870,7 +877,7 @@ class Tab extends TabBase {
|
|||
|
||||
/** Returns the current URL of this tab, without permission checks. */
|
||||
get _url() {
|
||||
if (this.isComposeTab) {
|
||||
if (this.type == "messageCompose") {
|
||||
return undefined;
|
||||
}
|
||||
return this.browser?.currentURI?.spec;
|
||||
|
@ -1006,6 +1013,47 @@ class TabmailTab extends Tab {
|
|||
super(extension, nativeTab, id);
|
||||
}
|
||||
|
||||
/** What sort of tab is this? */
|
||||
get type() {
|
||||
switch (this.nativeTab.mode.name) {
|
||||
case "folder":
|
||||
case "glodaList":
|
||||
return "mail";
|
||||
case "message":
|
||||
return "messageDisplay";
|
||||
case "contentTab": {
|
||||
let currentURI = this.nativeTab.browser.currentURI;
|
||||
if (currentURI?.schemeIs("about")) {
|
||||
switch (currentURI.filePath) {
|
||||
case "addressbook":
|
||||
return "addressBook";
|
||||
case "blank":
|
||||
return "content";
|
||||
default:
|
||||
return "special";
|
||||
}
|
||||
}
|
||||
if (currentURI?.schemeIs("chrome")) {
|
||||
return "special";
|
||||
}
|
||||
return "content";
|
||||
}
|
||||
case "calendar":
|
||||
case "calendarEvent":
|
||||
case "calendarTask":
|
||||
case "tasks":
|
||||
case "chat":
|
||||
return this.nativeTab.mode.name;
|
||||
case "accountProvisionerTab":
|
||||
case "glodaFacet":
|
||||
case "preferencesTab":
|
||||
return "special";
|
||||
default:
|
||||
// We should not get here, unless a new type is registered with tabmail.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the XUL browser for the tab. */
|
||||
get browser() {
|
||||
return getTabBrowser(this.nativeTab);
|
||||
|
@ -1021,11 +1069,6 @@ class TabmailTab extends Tab {
|
|||
return getTabTabmail(this.nativeTab);
|
||||
}
|
||||
|
||||
/** Returns true if this tab is a 3-pane tab. */
|
||||
get isMailTab() {
|
||||
return ["folder", "glodaList"].includes(this.nativeTab.mode.name);
|
||||
}
|
||||
|
||||
/** Returns the tab index. */
|
||||
get index() {
|
||||
return this.tabmail.tabInfo.indexOf(this.nativeTab);
|
||||
|
@ -1287,10 +1330,8 @@ class TabmailWindow extends Window {
|
|||
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);
|
||||
}
|
||||
// Only tabs that have a browser element.
|
||||
yield tabManager.getWrapper(nativeTabInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -155,7 +155,7 @@ this.mailTabs = class extends ExtensionAPI {
|
|||
tabId = tab.id;
|
||||
}
|
||||
|
||||
if (tab && tab.isMailTab) {
|
||||
if (tab && tab.type == "mail") {
|
||||
return tab;
|
||||
}
|
||||
throw new ExtensionError(`Invalid mail tab ID: ${tabId}`);
|
||||
|
|
|
@ -35,19 +35,98 @@
|
|||
"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. 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.)"},
|
||||
"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 <permission>tabs</permission> 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 <permission>tabs</permission> 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 <permission>tabs</permission> 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."},
|
||||
"mailTab": {"type": "boolean", "optional": true, "description": "Whether the tab is a 3-pane tab."}
|
||||
"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. 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.)"
|
||||
},
|
||||
"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 <permission>tabs</permission> 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 <permission>tabs</permission> 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 <permission>tabs</permission> 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."
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"addressBook",
|
||||
"calendar",
|
||||
"calendarEvent",
|
||||
"calendarTask",
|
||||
"chat",
|
||||
"content",
|
||||
"mail",
|
||||
"messageCompose",
|
||||
"messageDisplay",
|
||||
"special",
|
||||
"tasks"
|
||||
],
|
||||
"optional": true
|
||||
},
|
||||
"mailTab": {
|
||||
"type": "boolean",
|
||||
"optional": true,
|
||||
"description": "Whether the tab is a 3-pane tab."
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -115,6 +115,7 @@ add_task(async () => {
|
|||
browser.test.assertEq(1, initialTabs.length);
|
||||
browser.test.assertEq(0, initialTabs[0].index);
|
||||
browser.test.assertTrue(initialTabs[0].mailTab);
|
||||
browser.test.assertEq("mail", initialTabs[0].type);
|
||||
let [{ id: initialTab, windowId: initialWindow }] = initialTabs;
|
||||
|
||||
browser.test.log("Add a first content tab and wait for it to load.");
|
||||
|
@ -125,9 +126,14 @@ add_task(async () => {
|
|||
windowId: initialWindow,
|
||||
active: true,
|
||||
mailTab: false,
|
||||
type: "content",
|
||||
});
|
||||
browser.test.assertTrue(contentTab1 != initialTab);
|
||||
await listener.pageLoad(contentTab1);
|
||||
browser.test.assertEq(
|
||||
"content",
|
||||
(await browser.tabs.get(contentTab1)).type
|
||||
);
|
||||
|
||||
browser.test.log("Add a second content tab and wait for it to load.");
|
||||
|
||||
|
@ -137,9 +143,14 @@ add_task(async () => {
|
|||
windowId: initialWindow,
|
||||
active: true,
|
||||
mailTab: false,
|
||||
type: "content",
|
||||
});
|
||||
browser.test.assertTrue(![initialTab, contentTab1].includes(contentTab2));
|
||||
await listener.pageLoad(contentTab2);
|
||||
browser.test.assertEq(
|
||||
"content",
|
||||
(await browser.tabs.get(contentTab2)).type
|
||||
);
|
||||
|
||||
browser.test.log("Add the calendar tab.");
|
||||
|
||||
|
@ -149,6 +160,7 @@ add_task(async () => {
|
|||
windowId: initialWindow,
|
||||
active: true,
|
||||
mailTab: false,
|
||||
type: "calendar",
|
||||
});
|
||||
browser.test.assertTrue(
|
||||
![initialTab, contentTab1, contentTab2].includes(calendarTab)
|
||||
|
@ -162,6 +174,7 @@ add_task(async () => {
|
|||
windowId: initialWindow,
|
||||
active: true,
|
||||
mailTab: false,
|
||||
type: "tasks",
|
||||
});
|
||||
browser.test.assertTrue(
|
||||
![initialTab, contentTab1, contentTab2, calendarTab].includes(taskTab)
|
||||
|
@ -175,6 +188,7 @@ add_task(async () => {
|
|||
windowId: initialWindow,
|
||||
active: true,
|
||||
mailTab: true,
|
||||
type: "mail",
|
||||
});
|
||||
browser.test.assertTrue(
|
||||
![initialTab, contentTab1, contentTab2, calendarTab, taskTab].includes(
|
||||
|
@ -199,6 +213,7 @@ add_task(async () => {
|
|||
windowId: initialWindow,
|
||||
active: true,
|
||||
mailTab: false,
|
||||
type: "messageDisplay",
|
||||
});
|
||||
browser.test.assertTrue(
|
||||
![
|
||||
|
@ -222,6 +237,7 @@ add_task(async () => {
|
|||
windowId: initialWindow,
|
||||
active: false,
|
||||
mailTab: false,
|
||||
type: "messageDisplay",
|
||||
});
|
||||
browser.test.assertTrue(
|
||||
![
|
||||
|
|
|
@ -21,6 +21,7 @@ add_task(async () => {
|
|||
});
|
||||
browser.test.assertEq("addressBook", windowDetail.type);
|
||||
browser.test.assertEq(1, windowDetail.tabs.length);
|
||||
browser.test.assertEq("addressBook", windowDetail.tabs[0].type);
|
||||
// These three properties should not be present, but not fail either.
|
||||
browser.test.assertEq(undefined, windowDetail.tabs[0].favIconUrl);
|
||||
browser.test.assertEq(undefined, windowDetail.tabs[0].title);
|
||||
|
@ -42,6 +43,7 @@ add_task(async () => {
|
|||
});
|
||||
browser.test.assertEq("messageCompose", windowDetail.type);
|
||||
browser.test.assertEq(1, windowDetail.tabs.length);
|
||||
browser.test.assertEq("messageCompose", windowDetail.tabs[0].type);
|
||||
// These three properties should not be present, but not fail either.
|
||||
browser.test.assertEq(undefined, windowDetail.tabs[0].favIconUrl);
|
||||
browser.test.assertEq(undefined, windowDetail.tabs[0].title);
|
||||
|
@ -63,6 +65,7 @@ add_task(async () => {
|
|||
});
|
||||
browser.test.assertEq("messageDisplay", windowDetail.type);
|
||||
browser.test.assertEq(1, windowDetail.tabs.length);
|
||||
browser.test.assertEq("messageDisplay", windowDetail.tabs[0].type);
|
||||
browser.test.assertEq("about:blank", windowDetail.tabs[0].url);
|
||||
// These properties should not be present, but not fail either.
|
||||
browser.test.assertEq(undefined, windowDetail.tabs[0].favIconUrl);
|
||||
|
|
Загрузка…
Ссылка в новой задаче