Bug 1247493: [webext] Implement the `tabId` property of windows.create. r?billm
MozReview-Commit-ID: Jw4KvvUqkBh
--- a/browser/components/extensions/ext-windows.js
+++ b/browser/components/extensions/ext-windows.js
@@ -1,15 +1,17 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService");
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
+ "resource://gre/modules/PrivateBrowsingUtils.jsm");
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
EventManager,
} = ExtensionUtils;
extensions.registerSchemaAPI("windows", null, (extension, context) => {
return {
@@ -63,17 +65,35 @@ extensions.registerSchemaAPI("windows",
create: function(createData) {
function mkstr(s) {
let result = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
result.data = s;
return result;
}
let args = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray);
- if (createData.url !== null) {
+
+ if (createData.tabId !== null) {
+ if (createData.url !== null) {
+ return Promise.reject({ message: "`tabId` may not be used in conjunction with `url`" });
+ }
+ if (createData.incognito !== null) {
+ return Promise.reject({ message: "`tabId` may not be used in conjunction with `incognito`" });
+ }
+
+ let tab = TabManager.getTab(createData.tabId);
+ if (tab == null) {
+ return Promise.reject({ message: `Invalid tab ID: ${createData.tabId}` });
+ }
+ args.AppendElement(tab);
+
+ // Private browsing tabs can only be moved to private browsing
+ // windows.
+ createData.incognito = PrivateBrowsingUtils.isBrowserPrivate(tab.linkedBrowser);
+ } else if (createData.url !== null) {
if (Array.isArray(createData.url)) {
let array = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray);
for (let url of createData.url) {
array.AppendElement(mkstr(url));
}
args.AppendElement(array);
} else {
args.AppendElement(mkstr(createData.url));
--- a/browser/components/extensions/test/browser/browser.ini
+++ b/browser/components/extensions/test/browser/browser.ini
@@ -34,12 +34,13 @@ skip-if = !e10s
[browser_ext_tabs_query.js]
[browser_ext_tabs_getCurrent.js]
[browser_ext_tabs_create.js]
[browser_ext_tabs_update.js]
[browser_ext_tabs_onUpdated.js]
[browser_ext_tabs_sendMessage.js]
[browser_ext_tabs_move.js]
[browser_ext_tabs_move_window.js]
+[browser_ext_windows_create_tabId.js]
[browser_ext_windows_update.js]
[browser_ext_contentscript_connect.js]
[browser_ext_tab_runtimeConnect.js]
[browser_ext_webNavigation_getFrames.js]
--- a/browser/components/extensions/test/browser/browser_ext_tabs_events.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_events.js
@@ -42,21 +42,22 @@ add_task(function* testTabEvents() {
browser.test.assertEq(name, i in events && events[i].type,
`Got expected ${name} event`);
}
return events.splice(0);
});
}
browser.test.log("Create second browser window");
+ let windowId;
Promise.all([
browser.windows.getCurrent(),
browser.windows.create({}),
]).then(windows => {
- let windowId = windows[0].id;
+ windowId = windows[0].id;
let otherWindowId = windows[1].id;
let initialTab;
return expectEvents(["onCreated"]).then(([created]) => {
initialTab = created.tab;
browser.test.log("Create tab in window 1");
return browser.tabs.create({ windowId, index: 0, url: "about:blank" });
@@ -104,20 +105,53 @@ add_task(function* testTabEvents() {
browser.test.log("Close second window");
return browser.windows.remove(otherWindowId);
}).then(() => {
return expectEvents(["onRemoved"]);
}).then(([removed]) => {
browser.test.assertEq(initialTab.id, removed.tabId, "Expected removed tab ID");
browser.test.assertEq(otherWindowId, removed.windowId, "Expected removed tab window ID");
browser.test.assertEq(true, removed.isWindowClosing, "Expected isWindowClosing value");
- }).then(() => {
- browser.test.notifyPass("tabs-events");
});
});
+ }).then(() => {
+ browser.test.log("Create additional tab in window 1");
+ return browser.tabs.create({ windowId, url: "about:blank" });
+ }).then(tab => {
+ return expectEvents(["onCreated"]).then(() => {
+ browser.test.log("Create a new window, adopting the new tab");
+
+ // We have to explicitly wait for the event here, since its timing is
+ // not predictable.
+ let promiseAttached = new Promise(resolve => {
+ browser.tabs.onAttached.addListener(function listener(tabId) {
+ browser.tabs.onAttached.removeListener(listener);
+ resolve();
+ });
+ });
+
+ return Promise.all([
+ browser.windows.create({ tabId: tab.id }),
+ promiseAttached,
+ ]);
+ }).then(([window]) => {
+ return expectEvents(["onDetached", "onAttached"]).then(([detached, attached]) => {
+ browser.test.assertEq(tab.id, detached.tabId, "Expected onDetached tab ID");
+
+ browser.test.assertEq(tab.id, attached.tabId, "Expected onAttached tab ID");
+ browser.test.assertEq(0, attached.newPosition, "Expected onAttached new index");
+ browser.test.assertEq(window.id, attached.newWindowId,
+ "Expected onAttached new window id");
+
+ browser.test.log("Close the new window");
+ return browser.windows.remove(window.id);
+ });
+ });
+ }).then(() => {
+ browser.test.notifyPass("tabs-events");
}).catch(e => {
try {
browser.test.fail(`${e} :: ${e.stack}`);
} catch (ex) {
throw e;
}
browser.test.notifyFail("tabs-events");
});
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_windows_create_tabId.js
@@ -0,0 +1,108 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+add_task(function* testWindowCreate() {
+ function background() {
+ let promiseTabAttached = () => {
+ return new Promise(resolve => {
+ browser.tabs.onAttached.addListener(function listener() {
+ browser.tabs.onAttached.removeListener(listener);
+ resolve();
+ });
+ });
+ };
+
+ let windowId;
+ browser.windows.getCurrent().then(window => {
+ windowId = window.id;
+
+ browser.test.log("Create additional tab in window 1");
+ return browser.tabs.create({ windowId, url: "about:blank" });
+ }).then(tab => {
+ browser.test.log("Create a new window, adopting the new tab");
+
+ // Note that we want to check against actual boolean values for
+ // all of the `incognito` property tests.
+ browser.test.assertEq(false, tab.incognito, "Tab is not private");
+
+ return Promise.all([
+ promiseTabAttached(),
+ browser.windows.create({ tabId: tab.id }),
+ ]);
+ }).then(([, window]) => {
+ browser.test.assertEq(false, window.incognito, "New window is not private");
+
+ browser.test.log("Close the new window");
+ return browser.windows.remove(window.id);
+ }).then(() => {
+ browser.test.log("Create a new private window");
+
+ return browser.windows.create({ incognito: true });
+ }).then(privateWindow => {
+ browser.test.assertEq(true, privateWindow.incognito, "Private window is private");
+
+ browser.test.log("Create additional tab in private window");
+ return browser.tabs.create({ windowId: privateWindow.id }).then(privateTab => {
+ browser.test.assertEq(true, privateTab.incognito, "Private tab is private");
+
+ browser.test.log("Create a new window, adopting the new private tab");
+
+ return Promise.all([
+ promiseTabAttached(),
+ browser.windows.create({ tabId: privateTab.id }),
+ ]);
+ }).then(([, newWindow]) => {
+ browser.test.assertEq(true, newWindow.incognito, "New private window is private");
+
+ browser.test.log("Close the new private window");
+ return browser.windows.remove(newWindow.id);
+ }).then(() => {
+ browser.test.log("Close the private window");
+ return browser.windows.remove(privateWindow.id);
+ });
+ }).then(() => {
+ return browser.tabs.query({ windowId, active: true });
+ }).then(([tab]) => {
+ browser.test.log("Try to create a window with both a tab and a URL");
+
+ return browser.windows.create({ tabId: tab.id, url: "http://example.com/" }).then(
+ window => {
+ browser.test.fail("Create call should have failed");
+ },
+ error => {
+ browser.test.assertTrue(/`tabId` may not be used in conjunction with `url`/.test(error.message),
+ "Create call failed as expected");
+ }).then(() => {
+ browser.test.log("Try to create a window with both a tab and an incognito setting");
+
+ return browser.windows.create({ tabId: tab.id, incognito: false });
+ }).then(
+ window => {
+ browser.test.fail("Create call should have failed");
+ },
+ error => {
+ browser.test.assertTrue(/`tabId` may not be used in conjunction with `incognito`/.test(error.message),
+ "Create call failed as expected");
+ });
+ }).then(() => {
+ browser.test.notifyPass("window-create");
+ }).catch(e => {
+ browser.test.fail(`${e} :: ${e.stack}`);
+ browser.test.notifyFail("window-create");
+ });
+ }
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ "permissions": ["tabs"],
+ },
+
+ background,
+ });
+
+ yield extension.startup();
+ yield extension.awaitFinish("window-create");
+ yield extension.unload();
+});
+