Bug 1300584 - Implements devtools.inspectedWindow.reload.
MozReview-Commit-ID: J4ttcS7efsO
--- a/browser/components/extensions/ext-devtools-inspectedWindow.js
+++ b/browser/components/extensions/ext-devtools-inspectedWindow.js
@@ -43,12 +43,22 @@ extensions.registerSchemaAPI("devtools.i
const front = await waitForInspectedWindowFront;
return front.eval(callerInfo, expression, options || {}).then(evalResult => {
// TODO(rpl): check for additional undocumented behaviors on chrome
// (e.g. if we should also print error to the console or set lastError?).
return new SpreadArgs([evalResult.value, evalResult.exceptionInfo]);
});
},
+ async reload(options) {
+ const {ignoreCache, userAgent, injectedScript} = options || {};
+
+ if (!waitForInspectedWindowFront) {
+ waitForInspectedWindowFront = getInspectedWindowFront();
+ }
+
+ const front = await waitForInspectedWindowFront;
+ front.reload(callerInfo, {ignoreCache, userAgent, injectedScript});
+ },
},
},
};
});
--- a/browser/components/extensions/schemas/devtools_inspected_window.json
+++ b/browser/components/extensions/schemas/devtools_inspected_window.json
@@ -173,17 +173,16 @@
}
}
]
}
]
},
{
"name": "reload",
- "unsupported": true,
"type": "function",
"description": "Reloads the inspected page.",
"parameters": [
{
"type": "object",
"name": "reloadOptions",
"optional": true,
"properties": {
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -12,16 +12,17 @@ support-files =
file_popup_api_injection_b.html
file_iframe_document.html
file_iframe_document.sjs
file_bypass_cache.sjs
file_language_fr_en.html
file_language_ja.html
file_language_tlh.html
file_dummy.html
+ file_inspectedwindow_reload_target.sjs
file_serviceWorker.html
serviceWorker.js
searchSuggestionEngine.xml
searchSuggestionEngine.sjs
../../../../../toolkit/components/extensions/test/mochitest/head_webrequest.js
[browser_ext_browserAction_context.js]
[browser_ext_browserAction_disabled.js]
@@ -45,16 +46,17 @@ support-files =
[browser_ext_contextMenus_chrome.js]
[browser_ext_contextMenus_icons.js]
[browser_ext_contextMenus_onclick.js]
[browser_ext_contextMenus_radioGroups.js]
[browser_ext_contextMenus_uninstall.js]
[browser_ext_contextMenus_urlPatterns.js]
[browser_ext_currentWindow.js]
[browser_ext_devtools_inspectedWindow.js]
+[browser_ext_devtools_inspectedWindow_reload.js]
[browser_ext_devtools_page.js]
[browser_ext_getViews.js]
[browser_ext_incognito_views.js]
[browser_ext_incognito_popup.js]
[browser_ext_lastError.js]
[browser_ext_omnibox.js]
[browser_ext_optionsPage_privileges.js]
[browser_ext_pageAction_context.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow_reload.js
@@ -0,0 +1,336 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+// Like most of the mochitest-browser devtools test,
+// on debug test slave, it takes about 50s to run the test.
+requestLongerTimeout(4);
+
+XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
+ "resource://devtools/client/framework/gDevTools.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "devtools",
+ "resource://devtools/shared/Loader.jsm");
+
+// Small helper which provides the common steps to the following reload test cases.
+function* runReloadTestCase({urlParams, background, devtoolsPage, testCase}) {
+ const BASE = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/";
+ const TEST_TARGET_URL = `${BASE}file_inspectedwindow_reload_target.sjs?${urlParams}`;
+ let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_TARGET_URL);
+
+ let extension = ExtensionTestUtils.loadExtension({
+ background,
+ manifest: {
+ devtools_page: "devtools_page.html",
+ permissions: ["webNavigation", "<all_urls>"],
+ },
+ files: {
+ "devtools_page.html": `<!DOCTYPE html>
+ <html>
+ <head>
+ <meta charset="utf-8">
+ <script type="text/javascript" src="devtools_page.js"></script>
+ </head>
+ <body>
+ </body>
+ </html>`,
+ "devtools_page.js": devtoolsPage,
+ },
+ });
+
+ yield extension.startup();
+
+ let target = devtools.TargetFactory.forTab(tab);
+
+ yield gDevTools.showToolbox(target, "webconsole");
+ info("developer toolbox opened");
+
+ // Wait the test extension to be ready.
+ yield extension.awaitMessage("devtools_inspected_window_reload.ready");
+
+ info("devtools page ready");
+
+ // Run the test case.
+ yield testCase(extension);
+
+ yield gDevTools.closeToolbox(target);
+
+ yield target.destroy();
+
+ yield BrowserTestUtils.removeTab(tab);
+
+ yield extension.unload();
+}
+
+add_task(function* test_devtools_inspectedWindow_reload_ignore_cache() {
+ function background() {
+ // Wait until the devtools page is ready to run the test.
+ browser.runtime.onMessage.addListener(async (msg) => {
+ if (msg !== "devtools_page.ready") {
+ browser.test.fail(`Unexpected message received: ${msg}`);
+ return;
+ }
+
+ const tabs = await browser.tabs.query({active: true});
+ const activeTabId = tabs[0].id;
+ let reloads = 0;
+
+ browser.webNavigation.onCompleted.addListener(async (details) => {
+ if (details.tabId == activeTabId && details.frameId == 0) {
+ reloads++;
+
+ // This test expects two `devtools.inspectedWindow.reload` calls:
+ // the first one without any options and the second one with
+ // `ignoreCache=true`.
+ let expectedContent;
+ let enabled;
+
+ switch (reloads) {
+ case 1:
+ enabled = false;
+ expectedContent = "empty cache headers";
+ break;
+ case 2:
+ enabled = true;
+ expectedContent = "no-cache:no-cache";
+ break;
+ }
+
+ if (!expectedContent) {
+ browser.test.fail(`Unexpected number of tab reloads: ${reloads}`);
+ } else {
+ try {
+ const code = `document.body.textContent`;
+ const [text] = await browser.tabs.executeScript(activeTabId, {code});
+
+ browser.test.assertEq(text, expectedContent,
+ `Got the expected cache headers with ignoreCache=${enabled}`);
+ } catch (err) {
+ browser.test.fail(`Error: ${err.message} - ${err.stack}`);
+ }
+ }
+
+ browser.test.sendMessage("devtools_inspectedWindow_reload_checkIgnoreCache.done");
+ }
+ });
+
+ browser.test.sendMessage("devtools_inspected_window_reload.ready");
+ });
+ }
+
+ async function devtoolsPage() {
+ browser.test.onMessage.addListener(msg => {
+ switch (msg) {
+ case "no-ignore-cache":
+ browser.devtools.inspectedWindow.reload();
+ break;
+ case "ignore-cache":
+ browser.devtools.inspectedWindow.reload({ignoreCache: true});
+ break;
+ default:
+ browser.test.fail(`Unexpected test message received: ${msg}`);
+ }
+ });
+
+ browser.runtime.sendMessage("devtools_page.ready");
+ }
+
+ yield runReloadTestCase({
+ urlParams: "test=cache",
+ background, devtoolsPage,
+ testCase: function* (extension) {
+ for (const testMessage of ["no-ignore-cache", "ignore-cache"]) {
+ extension.sendMessage(testMessage);
+ yield extension.awaitMessage("devtools_inspectedWindow_reload_checkIgnoreCache.done");
+ }
+ },
+ });
+});
+
+add_task(function* test_devtools_inspectedWindow_reload_custom_user_agent() {
+ function background() {
+ browser.runtime.onMessage.addListener(async (msg) => {
+ if (msg !== "devtools_page.ready") {
+ browser.test.fail(`Unexpected message received: ${msg}`);
+ return;
+ }
+
+ const tabs = await browser.tabs.query({active: true});
+ const activeTabId = tabs[0].id;
+ let reloads = 0;
+
+ browser.webNavigation.onCompleted.addListener(async (details) => {
+ if (details.tabId == activeTabId && details.frameId == 0) {
+ reloads++;
+
+ let expectedContent;
+ let enabled;
+
+ switch (reloads) {
+ case 1:
+ enabled = false;
+ expectedContent = window.navigator.userAgent;
+ break;
+ case 2:
+ enabled = true;
+ expectedContent = "CustomizedUserAgent";
+ break;
+ }
+
+ if (!expectedContent) {
+ browser.test.fail(`Unexpected number of tab reloads: ${reloads}`);
+ } else {
+ const code = `document.body.textContent`;
+ try {
+ const [text] = await browser.tabs.executeScript(activeTabId, {code});
+ browser.test.assertEq(expectedContent, text,
+ `Got the expected userAgent with userAgent=${enabled}`);
+ } catch (err) {
+ browser.test.fail(`Error: ${err.message} - ${err.stack}`);
+ }
+ }
+
+ browser.test.sendMessage("devtools_inspectedWindow_reload_checkUserAgent.done");
+ }
+ });
+
+ browser.test.sendMessage("devtools_inspected_window_reload.ready");
+ });
+ }
+
+ function devtoolsPage() {
+ browser.test.onMessage.addListener(msg => {
+ switch (msg) {
+ case "no-custom-user-agent":
+ browser.devtools.inspectedWindow.reload({});
+ break;
+ case "custom-user-agent":
+ browser.devtools.inspectedWindow.reload({userAgent: "CustomizedUserAgent"});
+ break;
+ default:
+ browser.test.fail(`Unexpected test message received: ${msg}`);
+ }
+ });
+
+ browser.runtime.sendMessage("devtools_page.ready");
+ }
+
+ yield runReloadTestCase({
+ urlParams: "test=user-agent",
+ background, devtoolsPage,
+ testCase: function* (extension) {
+ extension.sendMessage("no-custom-user-agent");
+
+ yield extension.awaitMessage("devtools_inspectedWindow_reload_checkUserAgent.done");
+
+ extension.sendMessage("custom-user-agent");
+
+ yield extension.awaitMessage("devtools_inspectedWindow_reload_checkUserAgent.done");
+ },
+ });
+});
+
+add_task(function* test_devtools_inspectedWindow_reload_injected_script() {
+ function background() {
+ function getIframesTextContent() {
+ let docs = [];
+ for (let iframe, doc = document; doc; doc = iframe && iframe.contentDocument) {
+ docs.push(doc);
+ iframe = doc.querySelector("iframe");
+ }
+
+ return docs.map(doc => doc.querySelector("pre").textContent);
+ }
+
+ browser.runtime.onMessage.addListener(async (msg) => {
+ if (msg !== "devtools_page.ready") {
+ browser.test.fail(`Unexpected message received: ${msg}`);
+ return;
+ }
+
+ const tabs = await browser.tabs.query({active: true});
+ const activeTabId = tabs[0].id;
+ let reloads = 0;
+
+ browser.webNavigation.onCompleted.addListener(async (details) => {
+ if (details.tabId == activeTabId && details.frameId == 0) {
+ reloads++;
+
+ let expectedContent;
+ let enabled;
+
+ switch (reloads) {
+ case 1:
+ enabled = false;
+ expectedContent = "injected script NOT executed";
+ break;
+ case 2:
+ enabled = true;
+ expectedContent = "injected script executed first";
+ break;
+ default:
+ browser.test.fail(`Unexpected number of tab reloads: ${reloads}`);
+ }
+
+ if (!expectedContent) {
+ browser.test.fail(`Unexpected number of tab reloads: ${reloads}`);
+ } else {
+ let expectedResults = (new Array(4)).fill(expectedContent);
+ let code = `(${getIframesTextContent})()`;
+
+ try {
+ let [results] = await browser.tabs.executeScript(activeTabId, {code});
+
+ browser.test.assertEq(JSON.stringify(expectedResults), JSON.stringify(results),
+ `Got the expected result with injectScript=${enabled}`);
+ } catch (err) {
+ browser.test.fail(`Error: ${err.message} - ${err.stack}`);
+ }
+ }
+
+ browser.test.sendMessage(`devtools_inspectedWindow_reload_injectedScript.done`);
+ }
+ });
+
+ browser.test.sendMessage("devtools_inspected_window_reload.ready");
+ });
+ }
+
+ function devtoolsPage() {
+ function injectedScript() {
+ if (!window.pageScriptExecutedFirst) {
+ window.addEventListener("DOMContentLoaded", function listener() {
+ document.querySelector("pre").textContent = "injected script executed first";
+ }, {once: true});
+ }
+ }
+
+ browser.test.onMessage.addListener(msg => {
+ switch (msg) {
+ case "no-injected-script":
+ browser.devtools.inspectedWindow.reload({});
+ break;
+ case "injected-script":
+ browser.devtools.inspectedWindow.reload({injectedScript: `new ${injectedScript}`});
+ break;
+ default:
+ browser.test.fail(`Unexpected test message received: ${msg}`);
+ }
+ });
+
+ browser.runtime.sendMessage("devtools_page.ready");
+ }
+
+ yield runReloadTestCase({
+ urlParams: "test=injected-script&frames=3",
+ background, devtoolsPage,
+ testCase: function* (extension) {
+ extension.sendMessage("no-injected-script");
+
+ yield extension.awaitMessage("devtools_inspectedWindow_reload_injectedScript.done");
+
+ extension.sendMessage("injected-script");
+
+ yield extension.awaitMessage("devtools_inspectedWindow_reload_injectedScript.done");
+ },
+ });
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/file_inspectedwindow_reload_target.sjs
@@ -0,0 +1,74 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80 ft=javascript: */
+"use strict";
+
+Components.utils.importGlobalProperties(["URLSearchParams"]);
+
+function handleRequest(request, response) {
+ let params = new URLSearchParams(request.queryString);
+
+ switch(params.get("test")) {
+ case "cache":
+ handleCacheTestRequest(request, response);
+ break;
+
+ case "user-agent":
+ handleUserAgentTestRequest(request, response);
+ break;
+
+ case "injected-script":
+ handleInjectedScriptTestRequest(request, response, params);
+ break;
+ }
+}
+
+function handleCacheTestRequest(request, response) {
+ response.setHeader("Content-Type", "text/plain; charset=UTF-8", false);
+
+ if (request.hasHeader("pragma") && request.hasHeader("cache-control")) {
+ response.write(`${request.getHeader("pragma")}:${request.getHeader("cache-control")}`);
+ } else {
+ response.write("empty cache headers");
+ }
+}
+
+function handleUserAgentTestRequest(request, response) {
+ response.setHeader("Content-Type", "text/plain; charset=UTF-8", false);
+
+ if (request.hasHeader("user-agent")) {
+ response.write(request.getHeader("user-agent"));
+ } else {
+ response.write("no user agent header");
+ }
+}
+
+function handleInjectedScriptTestRequest(request, response, params) {
+ response.setHeader("Content-Type", "text/html; charset=UTF-8", false);
+
+ let content = "";
+ const frames = parseInt(params.get("frames"));
+ if (frames > 0) {
+ // Output an iframe in seamless mode, so that there is an higher chance that in case
+ // of test failures we get a screenshot where the nested iframes are all visible.
+ content = `<iframe seamless src="?test=injected-script&frames=${frames - 1}"></iframe>`;
+ }
+
+ response.write(`<!DOCTYPE html>
+ <html>
+ <head>
+ <meta charset="utf-8">
+ <style>
+ iframe { width: 100%; height: ${frames * 150}px; }
+ </style>
+ </head>
+ <body>
+ <h1>IFRAME ${frames}</h1>
+ <pre>injected script NOT executed</pre>
+ <script type="text/javascript">
+ window.pageScriptExecutedFirst = true;
+ </script>
+ ${content}
+ </body>
+ </html>
+ `);
+}
\ No newline at end of file