Bug 1297551 - Avoid cancelling content timeout callback; r?automatedtester
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
--- 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"))