import os from six.moves.urllib.parse import quote from marionette_driver import By, errors from marionette_driver.marionette import Alert, WebElement from marionette_driver.wait import Wait from marionette_harness import MarionetteTestCase, WindowManagerMixin def inline(doc): return "data:text/html;charset=utf-8,{}".format(quote(doc)) elements = inline("

foo

bar

") shadow_dom = """ """ globals = set( [ "atob", "Audio", "btoa", "document", "navigator", "URL", "window", ] ) 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 ), "property {} is undefined".format(property), ) def assert_is_web_element(self, element): self.assertIsInstance(element, WebElement) def test_return_number(self): self.assertEqual(1, self.marionette.execute_script("return 1")) self.assertEqual(1.5, self.marionette.execute_script("return 1.5")) def test_return_boolean(self): self.assertTrue(self.marionette.execute_script("return true")) def test_return_string(self): self.assertEqual("foo", self.marionette.execute_script("return 'foo'")) def test_return_array(self): self.assertEqual([1, 2], self.marionette.execute_script("return [1, 2]")) self.assertEqual( [1.25, 1.75], self.marionette.execute_script("return [1.25, 1.75]") ) self.assertEqual( [True, False], self.marionette.execute_script("return [true, false]") ) self.assertEqual( ["foo", "bar"], self.marionette.execute_script("return ['foo', 'bar']") ) self.assertEqual( [1, 1.5, True, "foo"], self.marionette.execute_script("return [1, 1.5, true, 'foo']"), ) self.assertEqual([1, [2]], self.marionette.execute_script("return [1, [2]]")) def test_return_object(self): self.assertEqual({"foo": 1}, self.marionette.execute_script("return {foo: 1}")) self.assertEqual( {"foo": 1.5}, self.marionette.execute_script("return {foo: 1.5}") ) self.assertEqual( {"foo": True}, self.marionette.execute_script("return {foo: true}") ) self.assertEqual( {"foo": "bar"}, self.marionette.execute_script("return {foo: 'bar'}") ) self.assertEqual( {"foo": [1, 2]}, self.marionette.execute_script("return {foo: [1, 2]}") ) self.assertEqual( {"foo": {"bar": [1, 2]}}, self.marionette.execute_script("return {foo: {bar: [1, 2]}}"), ) def test_no_return_value(self): self.assertIsNone(self.marionette.execute_script("true")) def test_argument_null(self): self.assertIsNone( self.marionette.execute_script( "return arguments[0]", script_args=(None,), sandbox="default" ) ) self.assertIsNone( self.marionette.execute_script( "return arguments[0]", script_args=(None,), sandbox="system" ) ) self.assertIsNone( self.marionette.execute_script( "return arguments[0]", script_args=(None,), sandbox=None ) ) def test_argument_number(self): self.assertEqual(1, self.marionette.execute_script("return arguments[0]", (1,))) self.assertEqual( 1.5, self.marionette.execute_script("return arguments[0]", (1.5,)) ) def test_argument_boolean(self): self.assertTrue(self.marionette.execute_script("return arguments[0]", (True,))) def test_argument_string(self): self.assertEqual( "foo", self.marionette.execute_script("return arguments[0]", ("foo",)) ) def test_argument_array(self): self.assertEqual( [1, 2], self.marionette.execute_script("return arguments[0]", ([1, 2],)) ) def test_argument_object(self): self.assertEqual( {"foo": 1}, self.marionette.execute_script("return arguments[0]", ({"foo": 1},)), ) def test_argument_shadow_root(self): self.marionette.navigate(inline(shadow_dom % "open")) elem = self.marionette.find_element(By.TAG_NAME, "custom-checkbox-element") shadow_root = elem.shadow_root nodeType = self.marionette.execute_script( "return arguments[0].nodeType", script_args=(shadow_root,) ) self.assertEqual(nodeType, 11) def test_argument_web_element(self): self.marionette.navigate(elements) elem = self.marionette.find_element(By.TAG_NAME, "p") nodeType = self.marionette.execute_script( "return arguments[0].nodeType", script_args=(elem,) ) self.assertEqual(nodeType, 1) def test_default_sandbox_globals(self): for property in globals: self.assert_is_defined(property, sandbox="default") self.assert_is_defined("Components") self.assert_is_defined("window.wrappedJSObject") def test_system_globals(self): for property in globals: self.assert_is_defined(property, sandbox="system") self.assert_is_defined("Components", sandbox="system") self.assert_is_defined("window.wrappedJSObject", sandbox="system") def test_mutable_sandbox_globals(self): for property in globals: self.assert_is_defined(property, sandbox=None) # Components is there, but will be removed soon self.assert_is_defined("Components", sandbox=None) # wrappedJSObject is always there in sandboxes self.assert_is_defined("window.wrappedJSObject", sandbox=None) def test_exception(self): self.assertRaises( errors.JavascriptException, self.marionette.execute_script, "return foo" ) def test_stacktrace(self): with self.assertRaises(errors.JavascriptException) as cm: self.marionette.execute_script("return b") # by default execute_script pass the name of the python file self.assertIn( os.path.relpath(__file__.replace(".pyc", ".py")), cm.exception.stacktrace ) self.assertIn("b is not defined", str(cm.exception)) def test_permission(self): for sandbox in ["default", None]: with self.assertRaises(errors.JavascriptException): self.marionette.execute_script( "Components.classes['@mozilla.org/preferences-service;1']" ) def test_return_web_element(self): self.marionette.navigate(elements) expected = self.marionette.find_element(By.TAG_NAME, "p") actual = self.marionette.execute_script("return document.querySelector('p')") self.assertEqual(expected, actual) def test_return_web_element_array(self): self.marionette.navigate(elements) expected = self.marionette.find_elements(By.TAG_NAME, "p") actual = self.marionette.execute_script( """ let els = document.querySelectorAll('p') return [els[0], els[1]]""" ) self.assertEqual(expected, actual) def test_return_web_element_nested_array(self): self.marionette.navigate(elements) expected = self.marionette.find_elements(By.TAG_NAME, "p") actual = self.marionette.execute_script( """ let els = document.querySelectorAll('p') return { els: [els[0], els[1]] }""" ) self.assertEqual(expected, actual["els"]) def test_return_web_element_nested_dict(self): self.marionette.navigate(elements) expected = self.marionette.find_element(By.TAG_NAME, "p") actual = self.marionette.execute_script( """ let el = document.querySelector('p') return { path: { to: { el } } }""" ) self.assertEqual(expected, actual["path"]["to"]["el"]) # Bug 938228 identifies a problem with unmarshaling NodeList # objects from the DOM. document.querySelectorAll returns this # construct. def test_return_web_element_nodelist(self): self.marionette.navigate(elements) expected = self.marionette.find_elements(By.TAG_NAME, "p") actual = self.marionette.execute_script("return document.querySelectorAll('p')") self.assertEqual(expected, actual) def test_sandbox_reuse(self): # Sandboxes between `execute_script()` invocations are shared. self.marionette.execute_script("this.foobar = [23, 42];") self.assertEqual( self.marionette.execute_script("return this.foobar;", new_sandbox=False), [23, 42], ) def test_sandbox_refresh_arguments(self): self.marionette.execute_script( "this.foobar = [arguments[0], arguments[1]]", [23, 42] ) self.assertEqual( self.marionette.execute_script("return this.foobar", new_sandbox=False), [23, 42], ) def test_mutable_sandbox_wrappedjsobject(self): self.assert_is_defined("window.wrappedJSObject") with self.assertRaises(errors.JavascriptException): self.marionette.execute_script( "window.wrappedJSObject.foo = 1", sandbox=None ) def test_default_sandbox_wrappedjsobject(self): self.assert_is_defined("window.wrappedJSObject", sandbox="default") try: self.marionette.execute_script( "window.wrappedJSObject.foo = 4", sandbox="default" ) self.assertEqual( self.marionette.execute_script( "return window.wrappedJSObject.foo", sandbox="default" ), 4, ) finally: self.marionette.execute_script( "delete window.wrappedJSObject.foo", sandbox="default" ) def test_system_sandbox_wrappedjsobject(self): self.assert_is_defined("window.wrappedJSObject", sandbox="system") self.marionette.execute_script( "window.wrappedJSObject.foo = 4", sandbox="system" ) self.assertEqual( self.marionette.execute_script( "return window.wrappedJSObject.foo", sandbox="system" ), 4, ) def test_system_dead_object(self): self.assert_is_defined("window.wrappedJSObject", sandbox="system") self.marionette.execute_script( "window.wrappedJSObject.foo = function() { return 'yo' }", sandbox="system" ) self.marionette.execute_script( "dump(window.wrappedJSObject.foo)", sandbox="system" ) self.marionette.execute_script( "window.wrappedJSObject.foo = function() { return 'yolo' }", sandbox="system", ) typ = self.marionette.execute_script( "return typeof window.wrappedJSObject.foo", sandbox="system" ) self.assertEqual("function", typ) obj = self.marionette.execute_script( "return window.wrappedJSObject.foo.toString()", sandbox="system" ) self.assertIn("yolo", obj) def test_lasting_side_effects(self): def send(script): return self.marionette._send_message( "WebDriver:ExecuteScript", {"script": script}, key="value" ) send("window.foo = 1") foo = send("return window.foo") self.assertEqual(1, foo) for property in globals: exists = send("return typeof {} != 'undefined'".format(property)) self.assertTrue(exists, "property {} is undefined".format(property)) self.assertTrue( send( """ return (typeof Components == 'undefined') || (typeof Components.utils == '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): def content_timeout_triggered(mn): return mn.execute_script("return window.n", sandbox=None) > 0 # subsequent call to execute_script after this # should not cancel the setTimeout event self.marionette.navigate( inline( """ """ ) ) # as debug builds are inherently slow, # we need to assert the event did not already fire self.assertEqual( 0, self.marionette.execute_script("return window.n", sandbox=None), "setTimeout already fired", ) # if event was cancelled, this will time out Wait(self.marionette, timeout=8).until( content_timeout_triggered, message="Scheduled setTimeout event was cancelled by call to execute_script", ) def test_access_chrome_objects_in_event_listeners(self): # sandbox.window.addEventListener/removeEventListener # is used by Marionette for installing the unloadHandler which # is used to return an error when a document is unloaded during # script execution. # # Certain web frameworks, notably Angular, override # window.addEventListener/removeEventListener and introspects # objects passed to them. If these objects originates from chrome # without having been cloned, a permission denied error is thrown # as part of the security precautions put in place by the sandbox. # addEventListener is called when script is injected self.marionette.navigate( inline( """ """ ) ) self.marionette.execute_script("", sandbox=None) # removeEventListener is called when sandbox is unloaded self.marionette.navigate( inline( """ """ ) ) self.marionette.execute_script("", sandbox=None) def test_access_global_objects_from_chrome(self): # test inspection of arguments self.marionette.execute_script("__webDriverArguments.toString()") def test_toJSON(self): foo = self.marionette.execute_script( """ return { toJSON () { return "foo"; } } """, sandbox=None, ) self.assertEqual("foo", foo) def test_unsafe_toJSON(self): el = self.marionette.execute_script( """ return { toJSON () { return document.documentElement; } } """, sandbox=None, ) self.assert_is_web_element(el) self.assertEqual(el, self.marionette.find_element(By.CSS_SELECTOR, ":root")) def test_comment_in_last_line(self): self.marionette.execute_script(" // comment ") def test_return_value_on_alert(self): res = self.marionette.execute_script("alert()") self.assertIsNone(res) class TestExecuteChrome(WindowManagerMixin, TestExecuteContent): def setUp(self): super(TestExecuteChrome, self).setUp() self.marionette.set_context("chrome") win = self.open_chrome_window("chrome://remote/content/marionette/test.xhtml") self.marionette.switch_to_window(win) def tearDown(self): self.close_all_windows() super(TestExecuteChrome, self).tearDown() def test_permission(self): self.marionette.execute_script( "Components.classes['@mozilla.org/preferences-service;1']" ) def test_unmarshal_element_collection(self): expected = self.marionette.find_elements(By.TAG_NAME, "input") actual = self.marionette.execute_script( "return document.querySelectorAll('input')" ) self.assertTrue(len(expected) > 0) self.assertEqual(expected, actual) def test_argument_shadow_root(self): pass def test_argument_web_element(self): elem = self.marionette.find_element(By.TAG_NAME, "input") nodeType = self.marionette.execute_script( "return arguments[0].nodeType", script_args=(elem,) ) self.assertEqual(nodeType, 1) def test_async_script_timeout(self): with self.assertRaises(errors.ScriptTimeoutException): self.marionette.execute_async_script( """ var cb = arguments[arguments.length - 1]; setTimeout(function() { cb() }, 2500); """, script_timeout=100, ) def test_lasting_side_effects(self): pass def test_return_web_element(self): pass def test_return_web_element_array(self): pass def test_return_web_element_nested_array(self): pass def test_return_web_element_nested_dict(self): pass def test_return_web_element_nodelist(self): pass def test_window_set_timeout_is_not_cancelled(self): pass def test_mutable_sandbox_wrappedjsobject(self): pass def test_default_sandbox_wrappedjsobject(self): pass def test_system_sandbox_wrappedjsobject(self): pass def test_access_chrome_objects_in_event_listeners(self): pass def test_return_value_on_alert(self): pass