Bug 1300584 - Implements devtools.inspectedWindow.reload. r=kmag
authorLuca Greco <lgreco@mozilla.com>
Sat, 28 Jan 2017 06:26:53 +0100
changeset 380588 0a699117d21406e40b457572e8884ab80804298d
parent 380587 c20c6b11a1dae5bc59a7f2a3c4ab286778f41b41
child 380589 964e4881f2fe48da51856c73473fd35d239a907b
push id1468
push userasasaki@mozilla.com
push dateMon, 05 Jun 2017 19:31:07 +0000
treeherdermozilla-release@0641fc6ee9d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskmag
bugs1300584
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1300584 - Implements devtools.inspectedWindow.reload. r=kmag 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