Bug 1298215 - Add an extension API browser.downloads.showDownloads() to show about:downloads in a new tab; r?mixedpuppy
MozReview-Commit-ID: E2amcLwuZkL
--- a/toolkit/components/extensions/parent/.eslintrc.js
+++ b/toolkit/components/extensions/parent/.eslintrc.js
@@ -3,16 +3,17 @@
module.exports = {
"globals": {
"CONTAINER_STORE": true,
"DEFAULT_STORE": true,
"EventEmitter": true,
"EventManager": true,
"InputEventManager": true,
"PRIVATE_STORE": true,
+ "Tab": true,
"TabBase": true,
"TabManagerBase": true,
"TabTrackerBase": true,
"WindowBase": true,
"WindowManagerBase": true,
"WindowTrackerBase": true,
"browserPaintedPromise": true,
"browserStartupPromise": true,
--- a/toolkit/components/extensions/parent/ext-downloads.js
+++ b/toolkit/components/extensions/parent/ext-downloads.js
@@ -829,12 +829,16 @@ this.downloads = class extends Extension
registerPromise.then(() => {
DownloadMap.off("erase", handler);
});
};
},
}).api(),
onDeterminingFilename: ignoreEvent(context, "downloads.onDeterminingFilename"),
+
+ showDownloads(options) {
+ Tab.openActiveInternalUrl(context, "about:downloads", (options || {}).windowId, "downloads");
+ },
},
};
}
};
--- a/toolkit/components/extensions/parent/ext-tabs-base.js
+++ b/toolkit/components/extensions/parent/ext-tabs-base.js
@@ -747,16 +747,66 @@ class TabBase {
* The InjectDetails object, specifying what to remove, and from where.
*
* @returns {Promise}
* Resolves when the operation has completed.
*/
removeCSS(context, details) {
return this._execute(context, details, "css", "removeCSS").then(() => {});
}
+
+ /**
+ * Opens an active tab to an internal link, like about:downloads.
+ *
+ * @param {BaseContext} context
+ * The context through which to send the message.
+ * @param {string} url
+ * The internal url to open.
+ * @param {integer} [windowId = null]
+ * The windowId to open the new tab in.
+ * @param {string} requiredPermission
+ * Optional. The permission the extension must have to access the URL.
+ *
+ * @returns {Promise}
+ * @static
+ */
+ static openActiveInternalUrl(context, url, windowId = null, requiredPermission = null) {
+ return new Promise((resolve, reject) => {
+ let {extension} = context;
+ if (requiredPermission && !extension.hasPermission(requiredPermission)) {
+ return Promise.reject({message: `No permission for ${requiredPermission}`});
+ }
+
+ let window = windowId !== null ?
+ global.windowTracker.getWindow(windowId, context) :
+ global.windowTracker.topWindow;
+
+ let browser = window.gBrowser || window.BrowserApp;
+ if (!browser) {
+ let obs = (finishedWindow, topic, data) => {
+ if (finishedWindow != window) {
+ return;
+ }
+ Services.obs.removeObserver(obs, "browser-delayed-startup-finished");
+ resolve(window.gBrowser || window.BrowserApp);
+ };
+ Services.obs.addObserver(obs, "browser-delayed-startup-finished");
+ } else {
+ resolve(browser);
+ }
+ }).then(browser => {
+ let options = {};
+
+ // Make sure things like about:blank and data: URIs never inherit,
+ // and instead always get a NullPrincipal.
+ options.disallowInheritPrincipal = true;
+
+ browser.selectedTab = browser.addTab(url, options);
+ });
+ }
}
defineLazyGetter(TabBase.prototype, "incognito", function() { return this._incognito; });
// Note: These must match the values in windows.json.
const WINDOW_ID_NONE = -1;
const WINDOW_ID_CURRENT = -2;
--- a/toolkit/components/extensions/schemas/downloads.json
+++ b/toolkit/components/extensions/schemas/downloads.json
@@ -684,16 +684,35 @@
"type": "function",
"unsupported": true,
"parameters": [
{
"name": "enabled",
"type": "boolean"
}
]
+ },
+ {
+ "name": "showDownloads",
+ "type": "function",
+ "description": "Open the download manager and give it focus.",
+ "parameters": [
+ {
+ "name": "options",
+ "optional": true,
+ "properties": {
+ "windowId": {
+ "description": "The window to open a new tab in.",
+ "optional": true,
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ }
+ ]
}
],
"events": [
{
"description": "This event fires with the <a href='#type-DownloadItem'>DownloadItem</a> object when a download begins.",
"name": "onCreated",
"parameters": [
{
--- a/toolkit/components/extensions/test/mochitest/chrome.ini
+++ b/toolkit/components/extensions/test/mochitest/chrome.ini
@@ -16,16 +16,17 @@ tags = webextensions in-process-webexten
# NO NEW TESTS. mochitest-chrome does not run under e10s, avoid adding new
# tests here unless absolutely necessary.
[test_chrome_ext_contentscript_data_uri.html]
[test_chrome_ext_contentscript_telemetry.html]
[test_chrome_ext_contentscript_unrecognizedprop_warning.html]
[test_chrome_ext_downloads_open.html]
[test_chrome_ext_downloads_saveAs.html]
+[test_chrome_ext_downloads_showDownloads.html]
[test_chrome_ext_downloads_uniquify.html]
[test_chrome_ext_permissions.html]
skip-if = os == 'android' # Bug 1350559
[test_chrome_ext_trackingprotection.html]
[test_chrome_ext_webnavigation_resolved_urls.html]
[test_chrome_ext_webrequest_background_events.html]
[test_chrome_ext_webrequest_host_permissions.html]
[test_chrome_ext_webrequest_mozextension.html]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/test_chrome_ext_downloads_showDownloads.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+
+<html>
+<head>
+<meta charset="utf-8">
+ <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/ExtensionTestUtils.js"></script>
+ <link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+<script>
+"use strict";
+
+let extensionData = {
+ manifest: {
+ permissions: ["downloads", "webNavigation"],
+ },
+ background() {
+ let tabId;
+ browser.webNavigation.onCompleted.addListener(msg => {
+ if (msg.url === "about:downloads") {
+ browser.test.sendMessage("shown");
+ tabId = msg.tabId;
+ }
+ });
+
+ browser.test.onMessage.addListener(msg => {
+ if (msg === "showDownloads") {
+ browser.downloads.showDownloads();
+ } else if (msg === "close") {
+ browser.tabs.remove(tabId);
+ browser.test.sendMessage("closed");
+ }
+ });
+ },
+};
+
+add_task(async function test_webRequest_main_frame() {
+ let extension = ExtensionTestUtils.loadExtension(extensionData);
+ await extension.startup();
+
+ extension.sendMessage("showDownloads");
+ await extension.awaitMessage("shown");
+ ok(true, "about:downloads shown");
+ extension.sendMessage("close");
+ await extension.awaitMessage("closed");
+
+ await extension.unload();
+});
+</script>
+</head>
+<body>
+<div id="test">Sample text</div>
+
+</body>
+</html>