--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -6,35 +6,35 @@
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader);
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/Log.jsm");
-Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var {devtools} = Cu.import("resource://devtools/shared/Loader.jsm", {});
this.DevToolsUtils = devtools.require("devtools/shared/DevToolsUtils");
XPCOMUtils.defineLazyServiceGetter(
this, "cookieManager", "@mozilla.org/cookiemanager;1", "nsICookieManager2");
Cu.import("chrome://marionette/content/action.js");
Cu.import("chrome://marionette/content/atom.js");
-Cu.import("chrome://marionette/content/interaction.js");
Cu.import("chrome://marionette/content/element.js");
+Cu.import("chrome://marionette/content/error.js");
+Cu.import("chrome://marionette/content/evaluate.js");
Cu.import("chrome://marionette/content/event.js");
Cu.import("chrome://marionette/content/frame.js");
-Cu.import("chrome://marionette/content/error.js");
+Cu.import("chrome://marionette/content/interaction.js");
Cu.import("chrome://marionette/content/modal.js");
Cu.import("chrome://marionette/content/proxy.js");
Cu.import("chrome://marionette/content/simpletest.js");
loader.loadSubScript("chrome://marionette/content/common.js");
this.EXPORTED_SYMBOLS = ["GeckoDriver", "Context"];
@@ -116,20 +116,17 @@ this.GeckoDriver = function(appName, dev
// called by simpletest methods
this.heartbeatCallback = function() {};
this.marionetteLog = new MarionetteLogObj();
// topmost chrome frame
this.mainFrame = null;
// chrome iframe that currently has focus
this.curFrame = null;
this.mainContentFrameId = null;
- this.importedScripts = FileUtils.getFile("TmpD", ["marionetteChromeScripts"]);
- this.importedScriptHashes = {};
- this.importedScriptHashes[Context.CONTENT] = [];
- this.importedScriptHashes[Context.CHROME] = [];
+ this.importedScripts = new evaluate.ScriptStorageService([Context.CHROME, Context.CONTENT]);
this.currentFrameElement = null;
this.testName = null;
this.mozBrowserClose = null;
this.sandboxes = {};
// frame ID of the current remote frame, used for mozbrowserclose events
this.oopFrameId = null;
this.observing = null;
this._browserIds = new WeakMap();
@@ -831,25 +828,17 @@ GeckoDriver.prototype.executeScriptInSan
directInject,
async,
timeout,
filename) {
if (directInject && async && (timeout === null || timeout === 0)) {
throw new TimeoutError("Please set a timeout");
}
- if (this.importedScripts.exists()) {
- let stream = Cc["@mozilla.org/network/file-input-stream;1"]
- .createInstance(Ci.nsIFileInputStream);
- stream.init(this.importedScripts, -1, 0, 0);
- let data = NetUtil.readInputStreamToString(stream, stream.available());
- stream.close();
- script = data + script;
- }
-
+ script = this.importedScripts.for(Context.CHROME).concat(script);
let res = Cu.evalInSandbox(script, sandbox, "1.8", filename ? filename : "dummy file", 0);
if (directInject && !async &&
(typeof res == "undefined" || typeof res.passed == "undefined")) {
throw new WebDriverError("finish() not called");
}
if (!async) {
@@ -2494,68 +2483,41 @@ GeckoDriver.prototype.deleteSession = fu
this.sessionTearDown();
};
/** Returns the current status of the Application Cache. */
GeckoDriver.prototype.getAppCacheStatus = function*(cmd, resp) {
resp.body.value = yield this.listener.getAppCacheStatus();
};
+/**
+ * Import script to the JS evaluation runtime.
+ *
+ * Imported scripts are exposed in the contexts of all subsequent
+ * calls to {@code executeScript}, {@code executeAsyncScript}, and
+ * {@code executeJSScript} by prepending them to the evaluated script.
+ *
+ * Scripts can be cleared with the {@code clearImportedScripts} command.
+ *
+ * @param {string} script
+ * Script to include. If the script is byte-by-byte equal to an
+ * existing imported script, it is not imported.
+ */
GeckoDriver.prototype.importScript = function*(cmd, resp) {
let script = cmd.parameters.script;
-
- let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
- .createInstance(Ci.nsIScriptableUnicodeConverter);
- converter.charset = "UTF-8";
- let result = {};
- let data = converter.convertToByteArray(cmd.parameters.script, result);
- let ch = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
- ch.init(ch.MD5);
- ch.update(data, data.length);
- let hash = ch.finish(true);
- // return if we've already imported this script
- if (this.importedScriptHashes[this.context].indexOf(hash) > -1) {
- return;
- }
- this.importedScriptHashes[this.context].push(hash);
-
- switch (this.context) {
- case Context.CHROME:
- let file;
- if (this.importedScripts.exists()) {
- file = FileUtils.openFileOutputStream(this.importedScripts,
- FileUtils.MODE_APPEND | FileUtils.MODE_WRONLY);
- } else {
- // the permission bits here don't actually get set (bug 804563)
- this.importedScripts.createUnique(
- Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("0666", 8));
- file = FileUtils.openFileOutputStream(this.importedScripts,
- FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE);
- this.importedScripts.permissions = parseInt("0666", 8);
- }
- file.write(script, script.length);
- file.close();
- break;
-
- case Context.CONTENT:
- yield this.listener.importScript({script: script});
- break;
- }
+ this.importedScripts.for(this.context).add(script);
};
-GeckoDriver.prototype.clearImportedScripts = function(cmd, resp) {
- switch (this.context) {
- case Context.CHROME:
- this.deleteFile("marionetteChromeScripts");
- break;
-
- case Context.CONTENT:
- this.deleteFile("marionetteContentScripts");
- break;
- }
+/**
+ * Clear all scripts that are imported into the JS evaluation runtime.
+ *
+ * Scripts can be imported using the {@code importScript} command.
+ */
+GeckoDriver.prototype.clearImportedScripts = function*(cmd, resp) {
+ this.importedScripts.for(this.context).clear();
};
/**
* Takes a screenshot of a web element, current frame, or viewport.
*
* The screen capture is returned as a lossless PNG image encoded as
* a base 64 string.
*
new file mode 100644
--- /dev/null
+++ b/testing/marionette/evaluate.js
@@ -0,0 +1,179 @@
+/* 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/. */
+
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/Log.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const logger = Log.repository.getLogger("Marionette");
+
+this.EXPORTED_SYMBOLS = ["evaluate"];
+
+this.evaluate = {};
+
+/**
+ * Stores scripts imported from the local end through the
+ * {@code GeckoDriver#importScript} command.
+ *
+ * Imported scripts are prepended to the script that is evaluated
+ * on each call to {@code GeckoDriver#executeScript},
+ * {@code GeckoDriver#executeAsyncScript}, and
+ * {@code GeckoDriver#executeJSScript}.
+ *
+ * Usage:
+ *
+ * let importedScripts = new evaluate.ScriptStorage();
+ * importedScripts.add(firstScript);
+ * importedScripts.add(secondScript);
+ *
+ * let scriptToEval = importedScripts.concat(script);
+ * // firstScript and secondScript are prepended to script
+ *
+ */
+evaluate.ScriptStorage = class extends Set {
+
+ /**
+ * Produce a string of all stored scripts.
+ *
+ * The stored scripts are concatenated into a string, with optional
+ * additional scripts then appended.
+ *
+ * @param {...string} addional
+ * Optional scripts to include.
+ *
+ * @return {string}
+ * Concatenated string consisting of stored scripts and additional
+ * scripts, in that order.
+ */
+ concat(...additional) {
+ let rv = "";
+ for (let s of this) {
+ rv = s + rv;
+ }
+ for (let s of additional) {
+ rv = rv + s;
+ }
+ return rv;
+ }
+
+ toJson() {
+ return Array.from(this);
+ }
+
+};
+
+/**
+ * Service that enables the script storage service to be queried from
+ * content space.
+ *
+ * The storage can back multiple |ScriptStorage|, each typically belonging
+ * to a |Context|. Since imported scripts' scope are global and not
+ * scoped to the current browsing context, all imported scripts are stored
+ * in chrome space and fetched by content space as needed.
+ *
+ * Usage in chrome space:
+ *
+ * let service = new evaluate.ScriptStorageService(
+ * [Context.CHROME, Context.CONTENT]);
+ * let storage = service.for(Context.CHROME);
+ * let scriptToEval = storage.concat(script);
+ *
+ */
+evaluate.ScriptStorageService = class extends Map {
+
+ /**
+ * Create the service.
+ *
+ * An optional array of names for script storages to initially create
+ * can be provided.
+ *
+ * @param {Array.<string>=} initialStorages
+ * List of names of the script storages to create initially.
+ */
+ constructor(initialStorages = []) {
+ super(initialStorages.map(name => [name, new evaluate.ScriptStorage()]));
+ }
+
+ /**
+ * Retrieve the scripts associated with the given context.
+ *
+ * @param {Context} context
+ * Context to retrieve the scripts from.
+ *
+ * @return {ScriptStorage}
+ * Scrips associated with given |context|.
+ */
+ for(context) {
+ return this.get(context);
+ }
+
+ processMessage(msg) {
+ switch (msg.name) {
+ case "Marionette:getImportedScripts":
+ let storage = this.for.apply(this, msg.json);
+ return storage.toJson();
+
+ default:
+ throw new TypeError("Unknown message: " + msg.name);
+ }
+ }
+
+ // TODO(ato): The idea of services in chrome space
+ // can be generalised at some later time (see cookies.js:38).
+ receiveMessage(msg) {
+ try {
+ return this.processMessage(msg);
+ } catch (e) {
+ logger.error(e);
+ }
+ }
+
+};
+
+evaluate.ScriptStorageService.prototype.QueryInterface =
+ XPCOMUtils.generateQI([
+ Ci.nsIMessageListener,
+ Ci.nsISupportsWeakReference,
+ ]);
+
+/**
+ * Bridges the script storage in chrome space, to make it possible to
+ * retrieve a {@code ScriptStorage} associated with a given
+ * {@code Context} from content space.
+ *
+ * Usage in content space:
+ *
+ * let client = new evaluate.ScriptStorageServiceClient(chromeProxy);
+ * let storage = client.for(Context.CONTENT);
+ * let scriptToEval = storage.concat(script);
+ *
+ */
+evaluate.ScriptStorageServiceClient = class {
+
+ /**
+ * @param {proxy.SyncChromeSender} chromeProxy
+ * Proxy for communicating with chrome space.
+ */
+ constructor(chromeProxy) {
+ this.chrome = chromeProxy;
+ }
+
+ /**
+ * Retrieve scripts associated with the given context.
+ *
+ * @param {Context} context
+ * Context to retrieve scripts from.
+ *
+ * @return {ScriptStorage}
+ * Scripts associated with given |context|.
+ */
+ for(context) {
+ let scripts = this.chrome.getImportedScripts(context)[0];
+ return new evaluate.ScriptStorage(scripts);
+ }
+
+};
--- a/testing/marionette/frame.js
+++ b/testing/marionette/frame.js
@@ -217,16 +217,17 @@ frame.Manager = class {
mm.addWeakMessageListener("Marionette:emitTouchEvent", this.driver);
mm.addWeakMessageListener("Marionette:log", this.driver);
mm.addWeakMessageListener("Marionette:runEmulatorCmd", this.driver.emulator);
mm.addWeakMessageListener("Marionette:runEmulatorShell", this.driver.emulator);
mm.addWeakMessageListener("Marionette:shareData", this.driver);
mm.addWeakMessageListener("Marionette:switchToModalOrigin", this.driver);
mm.addWeakMessageListener("Marionette:switchedToFrame", this.driver);
mm.addWeakMessageListener("Marionette:getVisibleCookies", this.driver);
+ mm.addWeakMessageListener("Marionette:getImportedScripts", this.driver.importedScripts);
mm.addWeakMessageListener("Marionette:register", this.driver);
mm.addWeakMessageListener("Marionette:listenersAttached", this.driver);
mm.addWeakMessageListener("Marionette:getFiles", this.driver);
mm.addWeakMessageListener("MarionetteFrame:handleModal", this);
mm.addWeakMessageListener("MarionetteFrame:getCurrentFrameId", this);
mm.addWeakMessageListener("MarionetteFrame:getInterruptedState", this);
}
@@ -247,16 +248,17 @@ frame.Manager = class {
mm.removeWeakMessageListener("Marionette:done", this.driver);
mm.removeWeakMessageListener("Marionette:error", this.driver);
mm.removeWeakMessageListener("Marionette:log", this.driver);
mm.removeWeakMessageListener("Marionette:shareData", this.driver);
mm.removeWeakMessageListener("Marionette:runEmulatorCmd", this.driver.emulator);
mm.removeWeakMessageListener("Marionette:runEmulatorShell", this.driver.emulator);
mm.removeWeakMessageListener("Marionette:switchedToFrame", this.driver);
mm.removeWeakMessageListener("Marionette:getVisibleCookies", this.driver);
+ mm.removeWeakMessageListener("Marionette:getImportedScripts", this.driver.importedScripts);
mm.removeWeakMessageListener("Marionette:listenersAttached", this.driver);
mm.removeWeakMessageListener("Marionette:register", this.driver);
mm.removeWeakMessageListener("Marionette:getFiles", this.driver);
mm.removeWeakMessageListener("MarionetteFrame:handleModal", this);
mm.removeWeakMessageListener("MarionetteFrame:getCurrentFrameId", this);
}
};
--- a/testing/marionette/harness/marionette/marionette_test.py
+++ b/testing/marionette/harness/marionette/marionette_test.py
@@ -74,16 +74,23 @@ def expectedFailure(func):
def wrapper(*args, **kwargs):
try:
func(*args, **kwargs)
except Exception:
raise _ExpectedFailure(sys.exc_info())
raise _UnexpectedSuccess
return wrapper
+def skip_if_chrome(target):
+ def wrapper(self, *args, **kwargs):
+ if self.marionette._send_message("getContext", key="value") == "chrome":
+ raise SkipTest("skipping test in chrome context")
+ return target(self, *args, **kwargs)
+ return wrapper
+
def skip_if_desktop(target):
def wrapper(self, *args, **kwargs):
if self.marionette.session_capabilities.get('b2g') is None:
raise SkipTest('skipping due to desktop')
return target(self, *args, **kwargs)
return wrapper
def skip_if_b2g(target):
--- a/testing/marionette/harness/marionette/tests/unit/test_import_script.py
+++ b/testing/marionette/harness/marionette/tests/unit/test_import_script.py
@@ -1,142 +1,125 @@
# 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
-from marionette import MarionetteTestCase
+
+from marionette.marionette_test import MarionetteTestCase, skip_if_chrome
from marionette_driver.errors import JavascriptException
-class TestImportScript(MarionetteTestCase):
+class TestImportScriptContent(MarionetteTestCase):
+ contexts = set(["chrome", "content"])
+
+ script_file = os.path.abspath(
+ os.path.join(__file__, os.path.pardir, "importscript.js"))
+ another_script_file = os.path.abspath(
+ os.path.join(__file__, os.path.pardir, "importanotherscript.js"))
+
def setUp(self):
MarionetteTestCase.setUp(self)
+ for context in self.contexts:
+ with self.marionette.using_context(context):
+ self.marionette.clear_imported_scripts()
+ self.reset_context()
- def clear_other_context(self):
- self.marionette.set_context("chrome")
- self.marionette.clear_imported_scripts()
+ def reset_context(self):
self.marionette.set_context("content")
- def check_file_exists(self):
- return self.marionette.execute_script("""
- let FileUtils = Components.utils.import("resource://gre/modules/FileUtils.jsm", {}).FileUtils;
- let importedScripts = FileUtils.getFile('TmpD', ['marionetteContentScripts']);
- return importedScripts.exists();
- """, sandbox='system')
+ @property
+ def current_context(self):
+ return self.marionette._send_message("getContext", key="value")
+
+ @property
+ def other_context(self):
+ return self.contexts.copy().difference([self.current_context]).pop()
+
+ def is_defined(self, symbol):
+ return self.marionette.execute_script(
+ "return typeof %s != 'undefined'" % symbol)
- def get_file_size(self):
- return self.marionette.execute_script("""
- let FileUtils = Components.utils.import("resource://gre/modules/FileUtils.jsm", {}).FileUtils;
- let importedScripts = FileUtils.getFile('TmpD', ['marionetteContentScripts']);
- return importedScripts.fileSize;
- """, sandbox='system')
+ def assert_defined(self, symbol, msg=None):
+ if msg is None:
+ msg = "Expected symbol %s to be defined" % symbol
+ self.assertTrue(self.is_defined(symbol), msg)
+
+ def assert_undefined(self, symbol, msg=None):
+ if msg is None:
+ msg = "Expected symbol %s to be undefined" % symbol
+ self.assertFalse(self.is_defined(symbol), msg)
+
+ def assert_scripts_cleared(self):
+ self.marionette.import_script(self.script_file)
+ self.assert_defined("testFunc")
+ self.marionette.clear_imported_scripts()
+ self.assert_undefined("testFunc")
def test_import_script(self):
- js = os.path.abspath(os.path.join(__file__, os.path.pardir, "importscript.js"))
- self.marionette.import_script(js)
- self.assertEqual("i'm a test function!", self.marionette.execute_script("return testFunc();"))
- self.assertEqual("i'm a test function!", self.marionette.execute_async_script("marionetteScriptFinished(testFunc());"))
+ self.marionette.import_script(self.script_file)
+ self.assertEqual(
+ "i'm a test function!", self.marionette.execute_script("return testFunc();"))
+ self.assertEqual("i'm a test function!", self.marionette.execute_async_script(
+ "marionetteScriptFinished(testFunc());"))
def test_import_script_twice(self):
- js = os.path.abspath(os.path.join(__file__, os.path.pardir, "importscript.js"))
- self.marionette.import_script(js)
- self.assertEqual("i'm a test function!", self.marionette.execute_script("return testFunc();"))
- self.assertEqual("i'm a test function!", self.marionette.execute_async_script("marionetteScriptFinished(testFunc());"))
- self.assertTrue(self.check_file_exists())
- file_size = self.get_file_size()
- self.assertNotEqual(file_size, None)
- self.marionette.import_script(js)
- file_size = self.get_file_size()
- self.assertEqual(file_size, self.get_file_size())
- self.assertEqual("i'm a test function!", self.marionette.execute_script("return testFunc();"))
- self.assertEqual("i'm a test function!", self.marionette.execute_async_script("marionetteScriptFinished(testFunc());"))
+ self.marionette.import_script(self.script_file)
+ self.assert_defined("testFunc")
- def test_import_two_scripts_twice(self):
- js = os.path.abspath(os.path.join(__file__, os.path.pardir, "importscript.js"))
- self.marionette.import_script(js)
- self.assertEqual("i'm a test function!", self.marionette.execute_script("return testFunc();"))
- self.assertEqual("i'm a test function!", self.marionette.execute_async_script("marionetteScriptFinished(testFunc());"))
- self.assertTrue(self.check_file_exists())
- file_size = self.get_file_size()
- self.assertNotEqual(file_size, None)
- self.marionette.import_script(js)
- # same script should not append to file
- self.assertEqual(file_size, self.get_file_size())
- self.assertEqual("i'm a test function!", self.marionette.execute_script("return testFunc();"))
- self.assertEqual("i'm a test function!", self.marionette.execute_async_script("marionetteScriptFinished(testFunc());"))
- js = os.path.abspath(os.path.join(__file__, os.path.pardir, "importanotherscript.js"))
- self.marionette.import_script(js)
- new_size = self.get_file_size()
- # new script should append to file
- self.assertNotEqual(file_size, new_size)
- file_size = new_size
- self.assertEqual("i'm yet another test function!",
- self.marionette.execute_script("return testAnotherFunc();"))
- self.assertEqual("i'm yet another test function!",
- self.marionette.execute_async_script("marionetteScriptFinished(testAnotherFunc());"))
- self.marionette.import_script(js)
- # same script should not append to file
- self.assertEqual(file_size, self.get_file_size())
+ # TODO(ato): Note that the WebDriver command primitives
+ # does not allow us to check what scripts have been imported.
+ # I suspect we must to do this through an xpcshell test.
+
+ self.marionette.import_script(self.script_file)
+ self.assert_defined("testFunc")
def test_import_script_and_clear(self):
- js = os.path.abspath(os.path.join(__file__, os.path.pardir, "importscript.js"))
- self.marionette.import_script(js)
- self.assertEqual("i'm a test function!", self.marionette.execute_script("return testFunc();"))
- self.assertEqual("i'm a test function!", self.marionette.execute_async_script("marionetteScriptFinished(testFunc());"))
+ self.marionette.import_script(self.script_file)
+ self.assert_defined("testFunc")
self.marionette.clear_imported_scripts()
- self.assertFalse(self.check_file_exists())
- self.assertRaises(JavascriptException, self.marionette.execute_script, "return testFunc();")
- self.assertRaises(JavascriptException, self.marionette.execute_async_script, "marionetteScriptFinished(testFunc());")
+ self.assert_scripts_cleared()
+ self.assert_undefined("testFunc")
+ with self.assertRaises(JavascriptException):
+ self.marionette.execute_script("return testFunc()")
+ with self.assertRaises(JavascriptException):
+ self.marionette.execute_async_script(
+ "marionetteScriptFinished(testFunc())")
- def test_import_script_and_clear_in_chrome(self):
- js = os.path.abspath(os.path.join(__file__, os.path.pardir, "importscript.js"))
- self.marionette.import_script(js)
- self.assertTrue(self.check_file_exists())
- file_size = self.get_file_size()
- self.assertEqual("i'm a test function!", self.marionette.execute_script("return testFunc();"))
- self.assertEqual("i'm a test function!", self.marionette.execute_async_script("marionetteScriptFinished(testFunc());"))
- self.clear_other_context()
+ def test_clear_scripts_in_other_context(self):
+ self.marionette.import_script(self.script_file)
+ self.assert_defined("testFunc")
+
# clearing other context's script file should not affect ours
- self.assertTrue(self.check_file_exists())
- self.assertEqual(file_size, self.get_file_size())
- self.assertEqual("i'm a test function!", self.marionette.execute_script("return testFunc();"))
- self.assertEqual("i'm a test function!", self.marionette.execute_async_script("marionetteScriptFinished(testFunc());"))
+ with self.marionette.using_context(self.other_context):
+ self.marionette.clear_imported_scripts()
+ self.assert_undefined("testFunc")
- def test_importing_another_script_and_check_they_append(self):
- firstjs = os.path.abspath(
- os.path.join(__file__, os.path.pardir, "importscript.js"))
- secondjs = os.path.abspath(
- os.path.join(__file__, os.path.pardir, "importanotherscript.js"))
-
- self.marionette.import_script(firstjs)
- self.marionette.import_script(secondjs)
+ self.assert_defined("testFunc")
- self.assertEqual("i'm a test function!",
- self.marionette.execute_script("return testFunc();"))
+ def test_multiple_imports(self):
+ self.marionette.import_script(self.script_file)
+ self.marionette.import_script(self.another_script_file)
+ self.assert_defined("testFunc")
+ self.assert_defined("testAnotherFunc")
- self.assertEqual("i'm yet another test function!",
- self.marionette.execute_script("return testAnotherFunc();"))
+ @skip_if_chrome
+ def test_imports_apply_globally(self):
+ self.marionette.navigate(
+ self.marionette.absolute_url("test_windows.html"))
+ original_window = self.marionette.current_window_handle
+ self.marionette.find_element("link text", "Open new window").click()
-class TestImportScriptChrome(TestImportScript):
- def setUp(self):
- MarionetteTestCase.setUp(self)
- self.marionette.set_script_timeout(30000)
- self.marionette.set_context("chrome")
+ windows = set(self.marionette.window_handles)
+ print "windows=%s" % windows
+ new_window = windows.difference([original_window]).pop()
+ self.marionette.switch_to_window(new_window)
+
+ self.marionette.import_script(self.script_file)
+ self.marionette.close()
- def clear_other_context(self):
- self.marionette.set_context("content")
- self.marionette.clear_imported_scripts()
+ print "switching to original window: %s" % original_window
+ self.marionette.switch_to_window(original_window)
+ self.assert_defined("testFunc")
+
+
+class TestImportScriptChrome(TestImportScriptContent):
+ def reset_context(self):
self.marionette.set_context("chrome")
-
- def check_file_exists(self):
- return self.marionette.execute_async_script("""
- let FileUtils = Components.utils.import("resource://gre/modules/FileUtils.jsm", {}).FileUtils;
- let importedScripts = FileUtils.getFile('TmpD', ['marionetteChromeScripts']);
- marionetteScriptFinished(importedScripts.exists());
- """)
-
- def get_file_size(self):
- return self.marionette.execute_async_script("""
- let FileUtils = Components.utils.import("resource://gre/modules/FileUtils.jsm", {}).FileUtils;
- let importedScripts = FileUtils.getFile('TmpD', ['marionetteChromeScripts']);
- marionetteScriptFinished(importedScripts.fileSize);
- """)
-
deleted file mode 100644
--- a/testing/marionette/harness/marionette/tests/unit/test_import_script_reuse_window.py
+++ /dev/null
@@ -1,28 +0,0 @@
-
-# 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
-from marionette import MarionetteTestCase
-
-class TestImportScriptContent(MarionetteTestCase):
-
- def test_importing_script_then_reusing_it(self):
- test_html = self.marionette.absolute_url("test_windows.html")
- self.marionette.navigate(test_html)
- js = os.path.abspath(os.path.join(__file__, os.path.pardir, "importscript.js"))
- self.current_window = self.marionette.current_window_handle
- link = self.marionette.find_element("link text", "Open new window")
- link.click()
-
- windows = self.marionette.window_handles
- windows.remove(self.current_window)
- self.marionette.switch_to_window(windows[0])
-
- self.marionette.import_script(js)
- self.marionette.close()
-
- self.marionette.switch_to_window(self.current_window)
- self.assertEqual("i'm a test function!", self.marionette.execute_script("return testFunc();"))
-
--- a/testing/marionette/harness/marionette/tests/unit/unit-tests.ini
+++ b/testing/marionette/harness/marionette/tests/unit/unit-tests.ini
@@ -18,18 +18,16 @@ skip = false
[test_accessibility.py]
b2g = false
[test_expectedfail.py]
expected = fail
[test_import_script.py]
b2g = false
-[test_import_script_reuse_window.py]
-b2g = false
[test_click.py]
[test_click_chrome.py]
b2g = false
[test_selected.py]
[test_selected_chrome.py]
b2g = false
[test_getattr.py]
[test_getattr_chrome.py]
--- a/testing/marionette/jar.mn
+++ b/testing/marionette/jar.mn
@@ -19,16 +19,17 @@ marionette.jar:
content/message.js (message.js)
content/dispatcher.js (dispatcher.js)
content/emulator.js (emulator.js)
content/modal.js (modal.js)
content/proxy.js (proxy.js)
content/capture.js (capture.js)
content/cookies.js (cookies.js)
content/atom.js (atom.js)
+ content/evaluate.js (evaluate.js)
#ifdef ENABLE_TESTS
content/test.xul (harness/marionette/chrome/test.xul)
content/test2.xul (harness/marionette/chrome/test2.xul)
content/test_nested_iframe.xul (harness/marionette/chrome/test_nested_iframe.xul)
content/test_anonymous_content.xul (harness/marionette/chrome/test_anonymous_content.xul)
#endif
% content specialpowers %content/
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -14,22 +14,22 @@ loader.loadSubScript("chrome://marionett
loader.loadSubScript("chrome://marionette/content/common.js");
Cu.import("chrome://marionette/content/action.js");
Cu.import("chrome://marionette/content/atom.js");
Cu.import("chrome://marionette/content/capture.js");
Cu.import("chrome://marionette/content/cookies.js");
Cu.import("chrome://marionette/content/element.js");
Cu.import("chrome://marionette/content/error.js");
+Cu.import("chrome://marionette/content/evaluate.js");
Cu.import("chrome://marionette/content/event.js");
+Cu.import("chrome://marionette/content/interaction.js");
Cu.import("chrome://marionette/content/proxy.js");
-Cu.import("chrome://marionette/content/interaction.js");
Cu.import("resource://gre/modules/FileUtils.jsm");
-Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var marionetteLogObj = new MarionetteLogObj();
var isB2G = false;
var marionetteTestName;
@@ -41,17 +41,16 @@ var isRemoteBrowser = () => curContainer
var previousContainer = null;
var elementManager = new ElementManager([]);
// Holds session capabilities.
var capabilities = {};
var interactions = new Interactions(() => capabilities);
var actions = new action.Chain(checkForInterrupted);
-var importedScripts = null;
// Contains the last file input element that was the target of
// sendKeysToElement.
var fileInputElement;
// A dict of sandboxes used this session
var sandboxes = {};
// The name of the current sandbox
@@ -78,16 +77,17 @@ var navTimer = Cc["@mozilla.org/timer;1"
var onDOMContentLoaded;
// Send move events about this often
var EVENT_INTERVAL = 30; // milliseconds
// last touch for each fingerId
var multiLast = {};
var chrome = proxy.toChrome(sendSyncMessage.bind(this));
var cookies = new Cookies(() => curContainer.frame.document, chrome);
+var importedScripts = new evaluate.ScriptStorageServiceClient(chrome);
Cu.import("resource://gre/modules/Log.jsm");
var logger = Log.repository.getLogger("Marionette");
logger.debug("loaded listener.js");
var modalHandler = function() {
// This gets called on the system app only since it receives the mozbrowserprompt event
sendSyncMessage("Marionette:switchedToFrame", { frameValue: null, storePrevious: true });
let isLocal = sendSyncMessage("MarionetteFrame:handleModal", {})[0].value;
@@ -112,18 +112,16 @@ function registerSelf() {
capabilities = register[0][2];
isB2G = capabilities.B2G;
listenerId = id;
if (typeof id != "undefined") {
// check if we're the main process
if (register[0][1] == true) {
addMessageListener("MarionetteMainListener:emitTouchEvent", emitTouchEventForIFrame);
}
- importedScripts = FileUtils.getDir('TmpD', [], false);
- importedScripts.append('marionetteContentScripts');
startListeners();
let rv = {};
if (remotenessChange) {
rv.listenerId = id;
}
sendAsyncMessage("Marionette:listenersAttached", rv);
}
}
@@ -159,16 +157,20 @@ function emitTouchEventForIFrame(message
}
// Eventually we will not have a closure for every single command, but
// use a generic dispatch for all listener commands.
//
// Perhaps one could even conceive having a separate instance of
// CommandProcessor for the listener, because the code is mostly the same.
function dispatch(fn) {
+ if (typeof fn != "function") {
+ throw new TypeError("Provided dispatch handler is not a function");
+ }
+
return function(msg) {
let id = msg.json.command_id;
let req = Task.spawn(function*() {
if (typeof msg.json == "undefined" || msg.json instanceof Array) {
return yield fn.apply(null, msg.json);
} else {
return yield fn(msg.json);
@@ -265,17 +267,16 @@ function startListeners() {
addMessageListenerId("Marionette:sendKeysToElement", sendKeysToElement);
addMessageListenerId("Marionette:clearElement", clearElementFn);
addMessageListenerId("Marionette:switchToFrame", switchToFrame);
addMessageListenerId("Marionette:switchToParentFrame", switchToParentFrame);
addMessageListenerId("Marionette:switchToShadowRoot", switchToShadowRootFn);
addMessageListenerId("Marionette:deleteSession", deleteSession);
addMessageListenerId("Marionette:sleepSession", sleepSession);
addMessageListenerId("Marionette:emulatorCmdResult", emulatorCmdResult);
- addMessageListenerId("Marionette:importScript", importScript);
addMessageListenerId("Marionette:getAppCacheStatus", getAppCacheStatus);
addMessageListenerId("Marionette:setTestName", setTestName);
addMessageListenerId("Marionette:takeScreenshot", takeScreenshotFn);
addMessageListenerId("Marionette:addCookie", addCookieFn);
addMessageListenerId("Marionette:getCookies", getCookiesFn);
addMessageListenerId("Marionette:deleteAllCookies", deleteAllCookiesFn);
addMessageListenerId("Marionette:deleteCookie", deleteCookieFn);
}
@@ -370,17 +371,16 @@ function deleteSession(msg) {
removeMessageListenerId("Marionette:sendKeysToElement", sendKeysToElement);
removeMessageListenerId("Marionette:clearElement", clearElementFn);
removeMessageListenerId("Marionette:switchToFrame", switchToFrame);
removeMessageListenerId("Marionette:switchToParentFrame", switchToParentFrame);
removeMessageListenerId("Marionette:switchToShadowRoot", switchToShadowRootFn);
removeMessageListenerId("Marionette:deleteSession", deleteSession);
removeMessageListenerId("Marionette:sleepSession", sleepSession);
removeMessageListenerId("Marionette:emulatorCmdResult", emulatorCmdResult);
- removeMessageListenerId("Marionette:importScript", importScript);
removeMessageListenerId("Marionette:getAppCacheStatus", getAppCacheStatus);
removeMessageListenerId("Marionette:setTestName", setTestName);
removeMessageListenerId("Marionette:takeScreenshot", takeScreenshotFn);
removeMessageListenerId("Marionette:addCookie", addCookieFn);
removeMessageListenerId("Marionette:getCookies", getCookiesFn);
removeMessageListenerId("Marionette:deleteAllCookies", deleteAllCookiesFn);
removeMessageListenerId("Marionette:deleteCookie", deleteCookieFn);
if (isB2G) {
@@ -617,25 +617,19 @@ function executeScript(msg, directInject
} else {
sandboxes[sandboxName].asyncTestCommandId = asyncTestCommandId;
}
let sandbox = sandboxes[sandboxName];
try {
if (directInject) {
- if (importedScripts.exists()) {
- let stream = Components.classes["@mozilla.org/network/file-input-stream;1"].
- createInstance(Components.interfaces.nsIFileInputStream);
- stream.init(importedScripts, -1, 0, 0);
- let data = NetUtil.readInputStreamToString(stream, stream.available());
- stream.close();
- script = data + script;
- }
+ script = importedScripts.for("content").concat(script);
let res = Cu.evalInSandbox(script, sandbox, "1.8", filename ? filename : "dummy file" ,0);
+
sendSyncMessage("Marionette:shareData",
{log: elementManager.wrapValue(marionetteLogObj.getLogs())});
marionetteLogObj.clearLogs();
if (res == undefined || res.passed == undefined) {
sendError(new JavaScriptError("Marionette.finish() not called"), asyncTestCommandId);
}
else {
@@ -648,24 +642,18 @@ function executeScript(msg, directInject
msg.json.args, curContainer), sandbox, { wrapReflectors: true });
} catch (e) {
sendError(e, asyncTestCommandId);
return;
}
script = "var __marionetteFunc = function(){" + script + "};" +
"__marionetteFunc.apply(null, __marionetteParams);";
- if (importedScripts.exists()) {
- let stream = Components.classes["@mozilla.org/network/file-input-stream;1"].
- createInstance(Components.interfaces.nsIFileInputStream);
- stream.init(importedScripts, -1, 0, 0);
- let data = NetUtil.readInputStreamToString(stream, stream.available());
- stream.close();
- script = data + script;
- }
+ script = importedScripts.for("content").concat(script);
+
let res = Cu.evalInSandbox(script, sandbox, "1.8", filename ? filename : "dummy file", 0);
sendSyncMessage("Marionette:shareData",
{log: elementManager.wrapValue(marionetteLogObj.getLogs())});
marionetteLogObj.clearLogs();
sendResponse(elementManager.wrapValue(res), asyncTestCommandId);
}
} catch (e) {
let err = new JavaScriptError(
@@ -804,24 +792,17 @@ function executeWithCallback(msg, useFin
scriptSrc = "__marionetteParams.push(marionetteScriptFinished);" +
"var __marionetteFunc = function() { " + script + "};" +
"__marionetteFunc.apply(null, __marionetteParams); ";
}
try {
asyncTestRunning = true;
- if (importedScripts.exists()) {
- let stream = Cc["@mozilla.org/network/file-input-stream;1"].
- createInstance(Ci.nsIFileInputStream);
- stream.init(importedScripts, -1, 0, 0);
- let data = NetUtil.readInputStreamToString(stream, stream.available());
- stream.close();
- scriptSrc = data + scriptSrc;
- }
+ scriptSrc = importedScripts.for("content").concat(scriptSrc);
Cu.evalInSandbox(scriptSrc, sandbox, "1.8", filename ? filename : "dummy file", 0);
} catch (e) {
let err = new JavaScriptError(
e,
"execute_async_script",
msg.json.filename,
msg.json.line,
scriptSrc);
@@ -1758,36 +1739,16 @@ function emulatorCmdResult(msg) {
try {
cb(result);
} catch (e) {
let err = new JavaScriptError(e);
sendError(err, id);
}
}
-function importScript(msg) {
- let command_id = msg.json.command_id;
- let file;
- if (importedScripts.exists()) {
- file = FileUtils.openFileOutputStream(importedScripts,
- FileUtils.MODE_APPEND | FileUtils.MODE_WRONLY);
- }
- else {
- //Note: The permission bits here don't actually get set (bug 804563)
- importedScripts.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE,
- parseInt("0666", 8));
- file = FileUtils.openFileOutputStream(importedScripts,
- FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE);
- importedScripts.permissions = parseInt("0666", 8); //actually set permissions
- }
- file.write(msg.json.script, msg.json.script.length);
- file.close();
- sendOk(command_id);
-}
-
/**
* Perform a screen capture in content context.
*
* @param {UUID=} id
* Optional web element reference of an element to take a screenshot
* of.
* @param {boolean=} full
* True to take a screenshot of the entire document element. Is not