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:
Geoff Lankow 2021-07-16 15:08:26 +12:00
Родитель 4d78cf5ad5
Коммит 564bad8f71
7 изменённых файлов: 186 добавлений и 44 удалений

Просмотреть файл

@ -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);