bug 1238312 implement tabs.duplicate r?kmag
MozReview-Commit-ID: Fzd6BuAEgSl
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -641,12 +641,31 @@ extensions.registerSchemaAPI("tabs", nul
// If the window we are moving is the same, just move the tab.
gBrowser.moveTabTo(tab, insertionPoint);
}
tabsMoved.push(tab);
}
return Promise.resolve(tabsMoved.map(tab => TabManager.convert(extension, tab)));
},
+
+ duplicate: function(tabId) {
+ let tab = TabManager.getTab(tabId);
+ if (!tab) {
+ return Promise.reject({message: `Invalid tab ID: ${tabId}`});
+ }
+
+ let gBrowser = tab.ownerDocument.defaultView.gBrowser;
+ let newTab = gBrowser.duplicateTab(tab);
+ gBrowser.moveTabTo(newTab, tab._tPos + 1);
+ gBrowser.selectTabAtIndex(newTab._tPos);
+
+ return new Promise(resolve => {
+ newTab.addEventListener("SSTabRestored", function listener() {
+ newTab.removeEventListener("SSTabRestored", listener);
+ return resolve(TabManager.convert(extension, newTab));
+ });
+ });
+ },
},
};
return self;
});
--- a/browser/components/extensions/test/browser/browser.ini
+++ b/browser/components/extensions/test/browser/browser.ini
@@ -26,16 +26,17 @@ support-files =
[browser_ext_tabs_captureVisibleTab.js]
[browser_ext_tabs_executeScript.js]
[browser_ext_tabs_executeScript_good.js]
[browser_ext_tabs_executeScript_bad.js]
[browser_ext_tabs_insertCSS.js]
[browser_ext_tabs_query.js]
[browser_ext_tabs_getCurrent.js]
[browser_ext_tabs_create.js]
+[browser_ext_tabs_duplicate.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_update.js]
[browser_ext_contentscript_connect.js]
[browser_ext_tab_runtimeConnect.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_duplicate.js
@@ -0,0 +1,44 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+add_task(function* testDuplicateTab() {
+ yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.net/");
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ "permissions": ["tabs"],
+ },
+
+ background: function() {
+ browser.tabs.query({
+ lastFocusedWindow: true,
+ }, function(tabs) {
+ let source = tabs[1];
+ // By moving it 0, we check that the new tab is created next
+ // to the existing one.
+ browser.tabs.move(source.id, {index: 0}, () => {
+ browser.tabs.duplicate(source.id, (tab) => {
+ browser.test.assertEq("http://example.net/", tab.url);
+ // Should be the second tab, next to the one duplicated.
+ browser.test.assertEq(1, tab.index);
+ // Should be selected by default.
+ browser.test.assertTrue(tab.selected);
+ browser.test.notifyPass("tabs.duplicate");
+ });
+ });
+ });
+ },
+ });
+
+ yield extension.startup();
+ yield extension.awaitFinish("tabs.duplicate");
+ yield extension.unload();
+
+ while (window.gBrowser.tabs.length > 1) {
+ let tab = window.gBrowser.tabs[0];
+ if (tab.linkedBrowser.currentURI.spec === "http://example.net/") {
+ yield BrowserTestUtils.removeTab(tab);
+ }
+ }
+});
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -199,17 +199,17 @@ class BaseContext {
/**
* Wraps the given promise so it can be safely returned to extension
* code in this context.
*
* If `callback` is provided, however, it is used as a completion
* function for the promise, and no promise is returned. In this case,
* the callback is called when the promise resolves or rejects. In the
* latter case, `lastError` is set to the rejection value, and the
- * callback funciton must check `browser.runtime.lastError` or
+ * callback function must check `browser.runtime.lastError` or
* `extension.runtime.lastError` in order to prevent it being reported
* to the console.
*
* @param {Promise} promise The promise with which to wrap the
* callback. May resolve to a `SpreadArgs` instance, in which case
* each element will be used as a separate argument.
*
* Unless the promise object belongs to the cloneScope global, its