Bug 1300584 - Implements devtools.inspectedWindow.reload. draft
authorLuca Greco <lgreco@mozilla.com>
Sat, 28 Jan 2017 06:26:53 +0100
changeset 467666 fbe014c32e6a51d6b16e47e5af4c291ce58a651e
parent 467665 aeb4354b2c6c7b209de008ce67a107306782837d
child 467667 65cb8fc48a1f9704fb98e101e49f352742a6ee3e
push id43243
push userluca.greco@alcacoop.it
push dateSat, 28 Jan 2017 18:21:35 +0000
bugs1300584
milestone54.0a1
Bug 1300584 - Implements devtools.inspectedWindow.reload. MozReview-Commit-ID: J4ttcS7efsO
browser/components/extensions/ext-devtools-inspectedWindow.js
browser/components/extensions/schemas/devtools_inspected_window.json
browser/components/extensions/test/browser/browser-common.ini
browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow_reload.js
browser/components/extensions/test/browser/file_inspectedwindow_reload_target.sjs
--- 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