Bug 1288276 - Close proxy context upon page reload, with tests draft
authorRob Wu <rob@robwu.nl>
Sun, 07 Aug 2016 19:09:27 -0700
changeset 398835 769cdef2bdcb0f4e680347bd2360f6a104399dcf
parent 398182 ec7f8b9830e8f7d32330cf873d0a96af8057fe26
child 398856 4a95f4030ddf9ffb5c60cf41b9a61ce007547581
push id25651
push userbmo:rob@robwu.nl
push dateTue, 09 Aug 2016 21:31:59 +0000
bugs1288276
milestone51.0a1
Bug 1288276 - Close proxy context upon page reload, with tests MozReview-Commit-ID: HB65DfQTGXd
toolkit/components/extensions/ExtensionUtils.jsm
toolkit/components/extensions/test/mochitest/file_teardown_test.js
toolkit/components/extensions/test/mochitest/mochitest.ini
toolkit/components/extensions/test/mochitest/test_ext_contentscript_teardown.html
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -1404,17 +1404,17 @@ class ChildAPIManager {
           deferred.resolve(new SpreadArgs(data.args));
         }
         this.callPromises.delete(data.callId);
         break;
     }
   }
 
   close() {
-    this.messageManager.sendAsyncMessage("Extension:CloseProxyContext", {childId: this.id});
+    this.messageManager.sendAsyncMessage("API:CloseProxyContext", {childId: this.id});
   }
 
   get cloneScope() {
     return this.context.cloneScope;
   }
 
   callFunction(path, name, args) {
     throw new Error("Not implemented");
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/file_teardown_test.js
@@ -0,0 +1,24 @@
+"use strict";
+
+/* globals addMessageListener */
+let {Management} = Components.utils.import("resource://gre/modules/Extension.jsm", {});
+let events = [];
+function record(type, extensionContext) {
+  let eventType = type == "page-load" ? "load" : "unload";
+  let url = extensionContext.uri.spec;
+  let {extensionId} = extensionContext;
+  events.push({eventType, url, extensionId});
+}
+
+Management.on("page-load", record);
+Management.on("page-unload", record);
+addMessageListener("cleanup", () => {
+  Management.off("page-load", record);
+  Management.off("page-unload", record);
+});
+
+addMessageListener("get-context-events", extensionId => {
+  sendAsyncMessage("context-events", events);
+  events = [];
+});
+sendAsyncMessage("chromescript-startup");
--- a/toolkit/components/extensions/test/mochitest/mochitest.ini
+++ b/toolkit/components/extensions/test/mochitest/mochitest.ini
@@ -29,29 +29,32 @@ support-files =
   file_script_bad.js
   file_script_redirect.js
   file_script_xhr.js
   file_sample.html
   redirection.sjs
   file_privilege_escalation.html
   file_ext_test_api_injection.js
   file_permission_xhr.html
+  file_teardown_test.js
 
 [test_ext_inIncognitoContext_window.html]
 skip-if = os == 'android' # Android does not currently support windows.
 [test_ext_geturl.html]
 [test_ext_background_canvas.html]
 [test_ext_content_security_policy.html]
 [test_ext_contentscript.html]
 skip-if = buildapp == 'b2g' # runat != document_idle is not supported.
 [test_ext_contentscript_api_injection.html]
 [test_ext_contentscript_create_iframe.html]
 [test_ext_contentscript_devtools_metadata.html]
 [test_ext_contentscript_exporthelpers.html]
 [test_ext_contentscript_css.html]
+[test_ext_contentscript_teardown.html]
+skip-if = (os == 'android') # Android does not support tabs API. Bug 1260250
 [test_ext_exclude_include_globs.html]
 [test_ext_i18n_css.html]
 [test_ext_generate.html]
 [test_ext_notifications.html]
 [test_ext_permission_xhr.html]
 skip-if = buildapp == 'b2g' # JavaScript error: jar:remoteopenfile:///data/local/tmp/generated-extension.xpi!/content.js, line 46: NS_ERROR_ILLEGAL_VALUE:
 [test_ext_runtime_connect.html]
 skip-if = (os == 'android' || buildapp == 'b2g') # port.sender.tab is undefined on b2g. Bug 1258975 on android.
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_teardown.html
@@ -0,0 +1,96 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for content script teardown</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
+  <script type="text/javascript" src="head.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+
+<script>
+"use strict";
+
+add_task(function* test_contentscript_reload_and_unload() {
+  function contentScript() {
+    browser.test.sendMessage("contentscript-run");
+  }
+  function backgroundScript() {
+    let removedTabs = 0;
+    browser.tabs.onRemoved.addListener(() => {
+      browser.test.assertEq(1, ++removedTabs,
+          "Expected only one tab to be removed during the test");
+      browser.test.sendMessage("tab-closed");
+    });
+  }
+
+  let extensionData = {
+    background: `(${backgroundScript})();`,
+    manifest: {
+      content_scripts: [{
+        "matches": ["http://mochi.test/*/file_sample.html"],
+        "js": ["contentscript.js"],
+      }],
+    },
+
+    files: {
+      "contentscript.js": `(${contentScript})();`,
+    },
+  };
+
+  let extension = ExtensionTestUtils.loadExtension(extensionData);
+  yield extension.startup();
+
+  let chromeScript = SpecialPowers.loadChromeScript(
+      SimpleTest.getTestFileURL("file_teardown_test.js"));
+  yield chromeScript.promiseOneMessage("chromescript-startup");
+  function* getContextEvents() {
+    chromeScript.sendAsyncMessage("get-context-events");
+    let contextEvents = yield chromeScript.promiseOneMessage("context-events");
+    return contextEvents.filter(event => event.extensionId == extension.id);
+  }
+
+  let win = window.open("file_sample.html");
+  yield extension.awaitMessage("contentscript-run");
+  let tabUrl = win.location.href;
+
+  let contextEvents = yield* getContextEvents();
+  is(contextEvents.length, 1,
+      "ExtensionContext state change after loading a content script");
+  is(contextEvents[0].eventType, "load",
+      "Create ExtensionContext for content script");
+  is(contextEvents[0].url, tabUrl, "ExtensionContext URL = page");
+
+  let promiseReload = extension.awaitMessage("contentscript-run");
+  win.location.reload();
+  yield promiseReload;
+  contextEvents = yield* getContextEvents();
+  is(contextEvents.length, 2,
+      "ExtensionContext state changes after reloading a content script");
+  is(contextEvents[0].eventType, "unload", "Unload old ExtensionContext");
+  is(contextEvents[0].url, tabUrl, "ExtensionContext URL = page");
+  is(contextEvents[1].eventType, "load",
+      "Create new ExtensionContext for content script");
+  is(contextEvents[1].url, tabUrl, "ExtensionContext URL = page");
+
+  let tabClosePromise = extension.awaitMessage("tab-closed");
+  win.close();
+  yield tabClosePromise;
+
+  contextEvents = yield* getContextEvents();
+  is(contextEvents.length, 1,
+      "ExtensionContext state change after unloading a content script");
+  is(contextEvents[0].eventType, "unload",
+      "Unload ExtensionContext after closing the tab with the content script");
+  is(contextEvents[0].url, tabUrl, "ExtensionContext URL = page");
+
+  chromeScript.sendAsyncMessage("cleanup");
+  chromeScript.destroy();
+  yield extension.unload();
+});
+</script>
+
+</body>
+</html>