Bug 1370850 - Serialise undefined script evaluation return value to null; r=maja_zf

When a call through the content frame proxy is interrupted by the
dialogueObserver, the synchronous promise that is meant to wait for a
response from the frame script is resolved immediately with an undefined
return value.

When an undefined value is assigned to the response body, it gets dropped
during JSON serialisation.  To ensure the "value" field expected from
the Execute Script and Execute Async Script commands is populated,
we need to assign a null value to resp.body.value.

We can treat undefined as null by calling evaluate.toJSON again on the
return value from the proxied frame script call.  This effectively means
we serialise it twice, since it first needs to be serialised to cross
the IPC border, though the second computation only looks at primitives
and no known web element store is required.

It would be nicer if the content frame script itself would be able to
return early with null by installing a user prompt notification event,
but this is not possible because the tabmodal dialogue that appears
blocks script execution.  This means we need to rely on the
dialogueObserver in testing/marionette/proxy.js to take care of the
dialogue for us.

MozReview-Commit-ID: D14TA2TYYXI

--HG--
extra : rebase_source : 3b2405111b0f027b1fd6281d075ab6dbb2259591
This commit is contained in:
Andreas Tolfsen 2017-06-07 12:46:14 +01:00
parent d382c7e1f3
commit 239a225f5a
2 changed files with 32 additions and 5 deletions

View File

@ -871,11 +871,13 @@ GeckoDriver.prototype.execute_ = function (script, args, timeout, opts = {}) {
case Context.CONTENT:
// evaluate in content with lasting side-effects
if (!opts.sandboxName) {
return this.listener.execute(script, args, timeout, opts);
return this.listener.execute(script, args, timeout, opts)
.then(evaluate.toJSON);
// evaluate in content with sandbox
} else {
return this.listener.executeInSandbox(script, args, timeout, opts);
return this.listener.executeInSandbox(script, args, timeout, opts)
.then(evaluate.toJSON);
}
case Context.CHROME:
@ -887,8 +889,8 @@ GeckoDriver.prototype.execute_ = function (script, args, timeout, opts = {}) {
opts.timeout = timeout;
let wargs = evaluate.fromJSON(args, this.curBrowser.seenEls, sb.window);
let evaluatePromise = evaluate.sandbox(sb, script, wargs, opts);
return evaluatePromise.then(res => evaluate.toJSON(res, this.curBrowser.seenEls));
return evaluate.sandbox(sb, script, wargs, opts)
.then(res => evaluate.toJSON(res, this.curBrowser.seenEls));
}
};

View File

@ -6,7 +6,7 @@ import os
import urllib
from marionette_driver import By, errors
from marionette_driver.marionette import HTMLElement
from marionette_driver.marionette import Alert, HTMLElement
from marionette_driver.wait import Wait
from marionette_harness import MarionetteTestCase, skip_if_mobile, WindowManagerMixin
@ -31,6 +31,23 @@ globals = set([
class TestExecuteContent(MarionetteTestCase):
def alert_present(self):
try:
Alert(self.marionette).text
return True
except errors.NoAlertPresentException:
return False
def wait_for_alert_closed(self, timeout=None):
Wait(self.marionette, timeout=timeout).until(
lambda _: not self.alert_present())
def tearDown(self):
if self.alert_present():
alert = self.marionette.switch_to_alert()
alert.dismiss()
self.wait_for_alert_closed()
def assert_is_defined(self, property, sandbox="default"):
self.assertTrue(self.marionette.execute_script(
"return typeof arguments[0] != 'undefined'", [property], sandbox=sandbox),
@ -340,6 +357,11 @@ class TestExecuteContent(MarionetteTestCase):
sandbox=None)
self.assert_is_web_element(el)
def test_return_value_on_alert(self):
res = self.marionette._send_message("executeScript", {"script": "alert()"})
self.assertIn("value", res)
self.assertIsNone(res)
class TestExecuteChrome(WindowManagerMixin, TestExecuteContent):
@ -408,6 +430,9 @@ class TestExecuteChrome(WindowManagerMixin, TestExecuteContent):
def test_access_chrome_objects_in_event_listeners(self):
pass
def test_return_value_on_alert(self):
pass
class TestElementCollections(MarionetteTestCase):