зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1409262 - Notify when openerTabId changed via tabs.update() r=robwu,dao
Differential Revision: https://phabricator.services.mozilla.com/D164982
This commit is contained in:
Родитель
98bcd298b3
Коммит
cb5c94e122
|
@ -429,17 +429,29 @@ class TabTracker extends TabTrackerBase {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the opener of `tab` to the ID `openerTab`. Both tabs must be in the
|
||||
* same window, or this function will throw a type error.
|
||||
* Sets the opener of `tab` to the ID `openerTabId`. Both tabs must be in the
|
||||
* same window, or this function will throw an error. if `openerTabId` is `-1`
|
||||
* the opener tab is cleared.
|
||||
*
|
||||
* @param {Element} tab The tab for which to set the owner.
|
||||
* @param {Element} openerTab The opener of <tab>.
|
||||
* @param {Element} nativeTab The tab for which to set the owner.
|
||||
* @param {number} openerTabId The openerTabId of <tab>.
|
||||
*/
|
||||
setOpener(tab, openerTab) {
|
||||
if (tab.ownerDocument !== openerTab.ownerDocument) {
|
||||
throw new Error("Tab must be in the same window as its opener");
|
||||
setOpener(nativeTab, openerTabId) {
|
||||
let nativeOpenerTab = null;
|
||||
|
||||
if (openerTabId > -1) {
|
||||
nativeOpenerTab = tabTracker.getTab(openerTabId);
|
||||
if (nativeTab.ownerDocument !== nativeOpenerTab.ownerDocument) {
|
||||
throw new ExtensionError(
|
||||
"Opener tab must be in the same window as the tab being updated"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (nativeTab.openerTab !== nativeOpenerTab) {
|
||||
nativeTab.openerTab = nativeOpenerTab;
|
||||
this.emit("tab-openerTabId", { nativeTab, openerTabId });
|
||||
}
|
||||
tab.openerTab = openerTab;
|
||||
}
|
||||
|
||||
deferredForTabOpen(nativeTab) {
|
||||
|
|
|
@ -160,6 +160,7 @@ const allProperties = new Set([
|
|||
"hidden",
|
||||
"isArticle",
|
||||
"mutedInfo",
|
||||
"openerTabId",
|
||||
"pinned",
|
||||
"sharingState",
|
||||
"status",
|
||||
|
@ -503,6 +504,11 @@ this.tabs = class extends ExtensionAPIPersistent {
|
|||
}
|
||||
};
|
||||
|
||||
let openerTabIdChangeListener = (_, { nativeTab, openerTabId }) => {
|
||||
let tab = tabManager.getWrapper(nativeTab);
|
||||
fireForTab(tab, { openerTabId }, nativeTab);
|
||||
};
|
||||
|
||||
let listeners = new Map();
|
||||
if (filter.properties.has("status") || filter.properties.has("url")) {
|
||||
listeners.set("status", statusListener);
|
||||
|
@ -531,6 +537,10 @@ this.tabs = class extends ExtensionAPIPersistent {
|
|||
tabTracker.on("tab-isarticle", isArticleChangeListener);
|
||||
}
|
||||
|
||||
if (filter.properties.has("openerTabId")) {
|
||||
tabTracker.on("tab-openerTabId", openerTabIdChangeListener);
|
||||
}
|
||||
|
||||
return {
|
||||
unregister() {
|
||||
for (let [name, listener] of listeners) {
|
||||
|
@ -540,6 +550,10 @@ this.tabs = class extends ExtensionAPIPersistent {
|
|||
if (filter.properties.has("isArticle")) {
|
||||
tabTracker.off("tab-isarticle", isArticleChangeListener);
|
||||
}
|
||||
|
||||
if (filter.properties.has("openerTabId")) {
|
||||
tabTracker.off("tab-openerTabId", openerTabIdChangeListener);
|
||||
}
|
||||
},
|
||||
convert(_fire, _context) {
|
||||
fire = _fire;
|
||||
|
@ -939,14 +953,7 @@ this.tabs = class extends ExtensionAPIPersistent {
|
|||
}
|
||||
}
|
||||
if (updateProperties.openerTabId !== null) {
|
||||
let opener = tabTracker.getTab(updateProperties.openerTabId);
|
||||
if (opener.ownerDocument !== nativeTab.ownerDocument) {
|
||||
return Promise.reject({
|
||||
message:
|
||||
"Opener tab must be in the same window as the tab being updated",
|
||||
});
|
||||
}
|
||||
tabTracker.setOpener(nativeTab, opener);
|
||||
tabTracker.setOpener(nativeTab, updateProperties.openerTabId);
|
||||
}
|
||||
if (updateProperties.successorTabId !== null) {
|
||||
let successor = null;
|
||||
|
|
|
@ -109,7 +109,7 @@
|
|||
},
|
||||
"openerTabId": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"minimum": -1,
|
||||
"optional": true,
|
||||
"description": "The ID of the tab that opened this tab, if any. This property is only present if the opener tab still exists."
|
||||
},
|
||||
|
@ -623,7 +623,7 @@
|
|||
},
|
||||
"openerTabId": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"minimum": -1,
|
||||
"optional": true,
|
||||
"description": "The ID of the tab that opened this tab. If specified, the opener tab must be in the same window as the newly created tab."
|
||||
},
|
||||
|
@ -828,7 +828,7 @@
|
|||
},
|
||||
"openerTabId": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"minimum": -1,
|
||||
"optional": true,
|
||||
"description": "The ID of the tab that opened this tab. If specified, the opener tab must be in the same window as this tab."
|
||||
},
|
||||
|
@ -967,7 +967,7 @@
|
|||
},
|
||||
"openerTabId": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"minimum": -1,
|
||||
"optional": true,
|
||||
"description": "The ID of the tab that opened this tab. If specified, the opener tab must be in the same window as this tab."
|
||||
},
|
||||
|
|
|
@ -128,3 +128,114 @@ add_task(async function () {
|
|||
BrowserTestUtils.removeTab(tab1);
|
||||
BrowserTestUtils.removeTab(tab2);
|
||||
});
|
||||
|
||||
add_task(async function test_tabs_onUpdated_fired_on_openerTabId_change() {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
permissions: ["tabs"],
|
||||
},
|
||||
|
||||
async background() {
|
||||
let changedOpenerTabIds = [];
|
||||
let promise = new Promise(resolve => {
|
||||
browser.tabs.onUpdated.addListener((tabId, changeInfo, _tab) => {
|
||||
const { openerTabId } = changeInfo;
|
||||
if (openerTabId) {
|
||||
browser.test.assertDeepEq(
|
||||
{ openerTabId },
|
||||
changeInfo,
|
||||
`"openerTabId" is the only key in changeInfo for tab ${tabId}`
|
||||
);
|
||||
changedOpenerTabIds.push(openerTabId);
|
||||
if (openerTabId === -1) {
|
||||
// The last part of the test changes openerTabId back to -1.
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
let tab1 = await browser.tabs.create({});
|
||||
let tab2 = await browser.tabs.create({});
|
||||
|
||||
browser.test.assertDeepEq(
|
||||
[],
|
||||
changedOpenerTabIds,
|
||||
"No tabs.onUpdated fired with openerTabId at tab creation"
|
||||
);
|
||||
|
||||
// Not changed, should not emit event:
|
||||
await browser.tabs.update(tab1.id, { openerTabId: -1 });
|
||||
// Should emit event:
|
||||
await browser.tabs.update(tab1.id, { openerTabId: tab2.id });
|
||||
// Not changed, should not emit event:
|
||||
await browser.tabs.update(tab1.id, { openerTabId: tab2.id });
|
||||
// Should emit event:
|
||||
await browser.tabs.update(tab1.id, { openerTabId: -1 });
|
||||
await promise;
|
||||
|
||||
browser.test.assertDeepEq(
|
||||
[tab2.id, -1],
|
||||
changedOpenerTabIds,
|
||||
"Got expected tabs.onUpdated for openerTabId changes"
|
||||
);
|
||||
|
||||
await browser.tabs.remove(tab1.id);
|
||||
await browser.tabs.remove(tab2.id);
|
||||
|
||||
browser.test.notifyPass("tab-onUpdated-opener");
|
||||
} catch (e) {
|
||||
browser.test.fail(`${e} :: ${e.stack}`);
|
||||
browser.test.notifyFail("tab-onUpdated-opener");
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
await extension.awaitFinish("tab-onUpdated-opener");
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
add_task(async function test_errors_on_openerTabId_change() {
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
permissions: ["tabs"],
|
||||
},
|
||||
|
||||
async background() {
|
||||
let tab = await browser.tabs.create({});
|
||||
|
||||
await browser.test.assertRejects(
|
||||
browser.tabs.update(tab.id, { openerTabId: 123456789 }),
|
||||
"Invalid tab ID: 123456789",
|
||||
"Got error when openerTabId is invalid"
|
||||
);
|
||||
|
||||
let win = await browser.windows.create({ url: "about:blank" });
|
||||
|
||||
await browser.test.assertRejects(
|
||||
browser.tabs.update(tab.id, { openerTabId: win.tabs[0].id }),
|
||||
"Opener tab must be in the same window as the tab being updated",
|
||||
"Got error when openerTabId belongs to a different window"
|
||||
);
|
||||
|
||||
tab = await browser.tabs.get(tab.id);
|
||||
|
||||
browser.test.assertEq(
|
||||
undefined,
|
||||
tab.openerTabId,
|
||||
"Got initial tab.openerTabId after failing updates"
|
||||
);
|
||||
|
||||
await browser.windows.remove(win.id);
|
||||
await browser.tabs.remove(tab.id);
|
||||
|
||||
browser.test.notifyPass("tab-opener-with-wrong-window");
|
||||
},
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
await extension.awaitFinish("tab-opener-with-wrong-window");
|
||||
await extension.unload();
|
||||
});
|
||||
|
|
|
@ -2739,7 +2739,8 @@
|
|||
}
|
||||
let openerTab =
|
||||
(openerBrowser && this.getTabForBrowser(openerBrowser)) ||
|
||||
(relatedToCurrent && this.selectedTab);
|
||||
(relatedToCurrent && this.selectedTab) ||
|
||||
null;
|
||||
|
||||
// When overflowing, new tabs are scrolled into view smoothly, which
|
||||
// doesn't go well together with the width transition. So we skip the
|
||||
|
|
Загрузка…
Ссылка в новой задаче