Bug 1297551 - Avoid cancelling content timeout callback; r=automatedtester a=test-only
authorAndreas Tolfsen <ato@mozilla.com>
Fri, 25 Nov 2016 17:01:16 +0000
changeset 352688 7a51c6107a4d2e01c7ce6512c8e8eb7d2bdad4f2
parent 352687 786488b0735ed48d429723c2aba4e543c1b69b37
child 352689 bfd07f94e1c9decc396bfc8166d56c5bbb524b72
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-esr52@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersautomatedtester, test-only
bugs1297551
milestone52.0a2
Bug 1297551 - Avoid cancelling content timeout callback; r=automatedtester a=test-only We need to avoid passing the timeout ID returned from `setTimeout` in the content frame script that we use to register the script timeout handler to the sandbox's `clearTimeout` function as this might interfere with any `window.setTimeout` calls being made in the injected script. MozReview-Commit-ID: 26PY8JDkf9A
testing/marionette/evaluate.js
testing/marionette/harness/marionette/tests/unit/test_execute_script.py
--- a/testing/marionette/evaluate.js
+++ b/testing/marionette/evaluate.js
@@ -94,17 +94,17 @@ this.evaluate = {};
  *     it can be sent to the client.
  *
  * @throws JavaScriptError
  *   If an Error was thrown whilst evaluating the script.
  * @throws ScriptTimeoutError
  *   If the script was interrupted due to script timeout.
  */
 evaluate.sandbox = function(sb, script, args = [], opts = {}) {
-  let timeoutId, timeoutHandler, unloadHandler;
+  let scriptTimeoutID, timeoutHandler, unloadHandler;
 
   let promise = new Promise((resolve, reject) => {
     let src = "";
     sb[COMPLETE] = resolve;
     timeoutHandler = () => reject(new ScriptTimeoutError("Timed out"));
     unloadHandler = () => reject(
         new JavaScriptError("Document was unloaded during execution"));
 
@@ -139,17 +139,17 @@ evaluate.sandbox = function(sb, script, 
     if (opts.debug) {
       sb.window.onerror = (msg, url, line) => {
         let err = new JavaScriptError(`${msg} at: ${url} line: ${line}`);
         reject(err);
       };
     }
 
     // timeout and unload handlers
-    timeoutId = setTimeout(
+    scriptTimeoutID = setTimeout(
         timeoutHandler, opts.timeout || DEFAULT_TIMEOUT);
     sb.window.addEventListener("unload", unloadHandler);
 
     let res;
     try {
       res = Cu.evalInSandbox(
           src, sb, "1.8", opts.filename || "dummy file", 0);
     } catch (e) {
@@ -163,17 +163,17 @@ evaluate.sandbox = function(sb, script, 
     }
 
     if (!opts.async) {
       resolve(res);
     }
   });
 
   return promise.then(res => {
-    sb.window.clearTimeout(timeoutId);
+    clearTimeout(scriptTimeoutID);
     sb.window.removeEventListener("unload", unloadHandler);
     return res;
   });
 };
 
 this.sandbox = {};
 
 /**
--- a/testing/marionette/harness/marionette/tests/unit/test_execute_script.py
+++ b/testing/marionette/harness/marionette/tests/unit/test_execute_script.py
@@ -1,13 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import os
+import time
 import urllib
 
 from marionette import MarionetteTestCase, WindowManagerMixin
 from marionette_driver import By, errors
 from marionette_driver.marionette import HTMLElement
 
 
 def inline(doc):
@@ -232,16 +233,39 @@ class TestExecuteContent(MarionetteTestC
         # self.assertTrue(send("return typeof Components == 'undefined'"))
         self.assertTrue(
             send("return typeof window.wrappedJSObject == 'undefined'"))
 
     def test_no_callback(self):
         self.assertTrue(self.marionette.execute_script(
             "return typeof arguments[0] == 'undefined'"))
 
+    def test_window_set_timeout_is_not_cancelled(self):
+        self.marionette.navigate(inline("""
+            <script>
+            window.contentTimeoutTriggered = 0;
+            window.contentTimeoutID = setTimeout(
+                () => window.contentTimeoutTriggered++, 1000);
+            </script>"""))
+
+        # first execute script call should not cancel event
+        self.assertEqual(0, self.marionette.execute_script(
+            "return window.contentTimeoutTriggered", sandbox=None))
+
+        # test that event was not cancelled
+        time.sleep(1)
+        self.assertEqual(1, self.marionette.execute_script(
+            "return window.contentTimeoutTriggered", sandbox=None))
+
+        # ../../../../evaluate.js:/scriptTimeoutID/
+        # sets the script timeout handler using the content frame script
+        # so the in-content setTimeout should always return 2
+        self.assertEqual(2, self.marionette.execute_script(
+            "return window.contentTimeoutID", sandbox=None))
+
 
 class TestExecuteChrome(WindowManagerMixin, TestExecuteContent):
 
     def setUp(self):
         super(TestExecuteChrome, self).setUp()
 
         self.marionette.set_context("chrome")
 
@@ -291,16 +315,19 @@ class TestExecuteChrome(WindowManagerMix
         pass
 
     def test_return_web_element_array(self):
         pass
 
     def test_return_web_element_nodelist(self):
         pass
 
+    def test_window_set_timeout_is_not_cancelled(self):
+        pass
+
 
 class TestElementCollections(MarionetteTestCase):
     def assertSequenceIsInstance(self, seq, typ):
         for item in seq:
             self.assertIsInstance(item, typ)
 
     def test_array(self):
         self.marionette.navigate(inline("<p>foo <p>bar"))