gecko-dev/testing/marionette/listener.js

1812 строки
60 KiB
JavaScript
Исходник Обычный вид История

/* 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/. */
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
"use strict";
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
var uuidGen = Cc["@mozilla.org/uuid-generator;1"]
.getService(Ci.nsIUUIDGenerator);
var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader);
Cu.import("chrome://marionette/content/accessibility.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/legacyaction.js");
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
Cu.import("chrome://marionette/content/logging.js");
Cu.import("chrome://marionette/content/navigate.js");
Cu.import("chrome://marionette/content/proxy.js");
Cu.import("chrome://marionette/content/session.js");
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
Cu.import("chrome://marionette/content/simpletest.js");
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.importGlobalProperties(["URL"]);
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
var contentLog = new logging.ContentLogger();
var isB2G = false;
var marionetteTestName;
var winUtil = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
var listenerId = null; // unique ID of this listener
var curContainer = { frame: content, shadowRoot: null };
var isRemoteBrowser = () => curContainer.frame.contentWindow !== null;
var previousContainer = null;
var seenEls = new element.Store();
var SUPPORTED_STRATEGIES = new Set([
element.Strategy.ClassName,
element.Strategy.Selector,
element.Strategy.ID,
element.Strategy.Name,
element.Strategy.LinkText,
element.Strategy.PartialLinkText,
element.Strategy.TagName,
element.Strategy.XPath,
]);
var capabilities;
var legacyactions = new legacyaction.Chain(checkForInterrupted);
// the unload handler
var onunload;
// Flag to indicate whether an async script is currently running or not.
var asyncTestRunning = false;
var asyncTestCommandId;
var asyncTestTimeoutId;
var inactivityTimeoutId = null;
var originalOnError;
//timer for doc changes
var checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
//timer for readystate
var readyStateTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
// timer for navigation commands.
var navTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
var onDOMContentLoaded;
// Send move events about this often
var EVENT_INTERVAL = 30; // milliseconds
// last touch for each fingerId
var multiLast = {};
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
var asyncChrome = proxy.toChromeAsync({
addMessageListener: addMessageListenerId.bind(this),
removeMessageListener: removeMessageListenerId.bind(this),
sendAsyncMessage: sendAsyncMessage.bind(this),
});
var syncChrome = proxy.toChrome(sendSyncMessage.bind(this));
var cookies = new Cookies(() => curContainer.frame.document, syncChrome);
var importedScripts = new evaluate.ScriptStorageServiceClient(syncChrome);
Cu.import("resource://gre/modules/Log.jsm");
var logger = Log.repository.getLogger("Marionette");
logger.debug("loaded listener.js");
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
var modalHandler = function() {
// This gets called on the system app only since it receives the mozbrowserprompt event
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
sendSyncMessage("Marionette:switchedToFrame", {frameValue: null, storePrevious: true});
let isLocal = sendSyncMessage("MarionetteFrame:handleModal", {})[0].value;
if (isLocal) {
previousContainer = curContainer;
}
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
curContainer = {frame: content, shadowRoot: null};
};
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
// sandbox storage and name of the current sandbox
var sandboxes = new Sandboxes(() => curContainer.frame);
var sandboxName = "default";
/**
* Called when listener is first started up.
* The listener sends its unique window ID and its current URI to the actor.
* If the actor returns an ID, we start the listeners. Otherwise, nothing happens.
*/
function registerSelf() {
let msg = {value: winUtil.outerWindowID};
// register will have the ID and a boolean describing if this is the main process or not
let register = sendSyncMessage("Marionette:register", msg);
if (register[0]) {
let {id, remotenessChange} = register[0][0];
capabilities = session.Capabilities.fromJSON(register[0][2]);
listenerId = id;
if (typeof id != "undefined") {
// check if we're the main process
if (register[0][1]) {
addMessageListener("MarionetteMainListener:emitTouchEvent", emitTouchEventForIFrame);
}
startListeners();
let rv = {};
if (remotenessChange) {
rv.listenerId = id;
}
sendAsyncMessage("Marionette:listenersAttached", rv);
}
}
}
function emitTouchEventForIFrame(message) {
message = message.json;
let identifier = legacyactions.nextTouchId;
let domWindowUtils = curContainer.frame.
QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsIDOMWindowUtils);
var ratio = domWindowUtils.screenPixelsPerCSSPixel;
var typeForUtils;
switch (message.type) {
case 'touchstart':
typeForUtils = domWindowUtils.TOUCH_CONTACT;
break;
case 'touchend':
typeForUtils = domWindowUtils.TOUCH_REMOVE;
break;
case 'touchcancel':
typeForUtils = domWindowUtils.TOUCH_CANCEL;
break;
case 'touchmove':
typeForUtils = domWindowUtils.TOUCH_CONTACT;
break;
}
domWindowUtils.sendNativeTouchPoint(identifier, typeForUtils,
Math.round(message.screenX * ratio), Math.round(message.screenY * ratio),
message.force, 90);
}
// 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);
}
});
let okOrValueResponse = rv => {
if (typeof rv == "undefined") {
sendOk(id);
} else {
sendResponse(rv, id);
}
};
req.then(okOrValueResponse, err => sendError(err, id))
.catch(error.report);
};
}
/**
* Add a message listener that's tied to our listenerId.
*/
function addMessageListenerId(messageName, handler) {
addMessageListener(messageName + listenerId, handler);
}
/**
* Remove a message listener that's tied to our listenerId.
*/
function removeMessageListenerId(messageName, handler) {
removeMessageListener(messageName + listenerId, handler);
}
var getTitleFn = dispatch(getTitle);
var getPageSourceFn = dispatch(getPageSource);
var getActiveElementFn = dispatch(getActiveElement);
var clickElementFn = dispatch(clickElement);
var getElementAttributeFn = dispatch(getElementAttribute);
var getElementPropertyFn = dispatch(getElementProperty);
var getElementTextFn = dispatch(getElementText);
var getElementTagNameFn = dispatch(getElementTagName);
var getElementRectFn = dispatch(getElementRect);
var isElementEnabledFn = dispatch(isElementEnabled);
var getCurrentUrlFn = dispatch(getCurrentUrl);
var findElementContentFn = dispatch(findElementContent);
var findElementsContentFn = dispatch(findElementsContent);
var isElementSelectedFn = dispatch(isElementSelected);
var clearElementFn = dispatch(clearElement);
var isElementDisplayedFn = dispatch(isElementDisplayed);
var getElementValueOfCssPropertyFn = dispatch(getElementValueOfCssProperty);
var switchToShadowRootFn = dispatch(switchToShadowRoot);
var getCookiesFn = dispatch(getCookies);
var singleTapFn = dispatch(singleTap);
var takeScreenshotFn = dispatch(takeScreenshot);
var performActionsFn = dispatch(performActions);
var releaseActionsFn = dispatch(releaseActions);
var actionChainFn = dispatch(actionChain);
var multiActionFn = dispatch(multiAction);
var addCookieFn = dispatch(addCookie);
var deleteCookieFn = dispatch(deleteCookie);
var deleteAllCookiesFn = dispatch(deleteAllCookies);
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
var executeFn = dispatch(execute);
var executeInSandboxFn = dispatch(executeInSandbox);
var executeSimpleTestFn = dispatch(executeSimpleTest);
var sendKeysToElementFn = dispatch(sendKeysToElement);
/**
* Start all message listeners
*/
function startListeners() {
addMessageListenerId("Marionette:newSession", newSession);
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
addMessageListenerId("Marionette:execute", executeFn);
addMessageListenerId("Marionette:executeInSandbox", executeInSandboxFn);
addMessageListenerId("Marionette:executeSimpleTest", executeSimpleTestFn);
addMessageListenerId("Marionette:singleTap", singleTapFn);
addMessageListenerId("Marionette:performActions", performActionsFn);
addMessageListenerId("Marionette:releaseActions", releaseActionsFn);
addMessageListenerId("Marionette:actionChain", actionChainFn);
addMessageListenerId("Marionette:multiAction", multiActionFn);
addMessageListenerId("Marionette:get", get);
addMessageListenerId("Marionette:pollForReadyState", pollForReadyState);
addMessageListenerId("Marionette:cancelRequest", cancelRequest);
addMessageListenerId("Marionette:getCurrentUrl", getCurrentUrlFn);
addMessageListenerId("Marionette:getTitle", getTitleFn);
addMessageListenerId("Marionette:getPageSource", getPageSourceFn);
addMessageListenerId("Marionette:goBack", goBack);
addMessageListenerId("Marionette:goForward", goForward);
addMessageListenerId("Marionette:refresh", refresh);
addMessageListenerId("Marionette:findElementContent", findElementContentFn);
addMessageListenerId("Marionette:findElementsContent", findElementsContentFn);
addMessageListenerId("Marionette:getActiveElement", getActiveElementFn);
addMessageListenerId("Marionette:clickElement", clickElementFn);
addMessageListenerId("Marionette:getElementAttribute", getElementAttributeFn);
addMessageListenerId("Marionette:getElementProperty", getElementPropertyFn);
addMessageListenerId("Marionette:getElementText", getElementTextFn);
addMessageListenerId("Marionette:getElementTagName", getElementTagNameFn);
addMessageListenerId("Marionette:isElementDisplayed", isElementDisplayedFn);
addMessageListenerId("Marionette:getElementValueOfCssProperty", getElementValueOfCssPropertyFn);
addMessageListenerId("Marionette:getElementRect", getElementRectFn);
addMessageListenerId("Marionette:isElementEnabled", isElementEnabledFn);
addMessageListenerId("Marionette:isElementSelected", isElementSelectedFn);
addMessageListenerId("Marionette:sendKeysToElement", sendKeysToElementFn);
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:getAppCacheStatus", getAppCacheStatus);
addMessageListenerId("Marionette:setTestName", setTestName);
addMessageListenerId("Marionette:takeScreenshot", takeScreenshotFn);
addMessageListenerId("Marionette:addCookie", addCookieFn);
Bug 1153822: Adjust Marionette responses to match WebDriver protocol Introduce protocol version levels in the Marionette server. On establishing a connection to a local end, the remote will return a `marionetteProtocol` field indicating which level it speaks. The protocol level can be used by local ends to either fall into compatibility mode or warn the user that the local end is incompatible with the remote. The protocol is currently also more expressive than it needs to be and this expressiveness has previously resulted in subtle inconsistencies in the fields returned. This patch reduces the amount of superfluous fields, reducing the amount of data sent. Aligning the protocol closer to the WebDriver specification's expectations will also reduce the amount of post-processing required in the httpd. Previous to this patch, this is a value response: {"from":"0","value":null,"status":0,"sessionId":"{6b6d68d2-4ac9-4308-9f07-d2e72519c407}"} And this for ok responses: {"from":"0","ok":true} And this for errors: {"from":"0","status":21,"sessionId":"{6b6d68d2-4ac9-4308-9f07-d2e72519c407}","error":{"message":"Error loading page, timed out (onDOMContentLoaded)","stacktrace":null,"status":21}} This patch drops the `from` and `sessionId` fields, and the `status` field from non-error responses. It also drops the `ok` field in non-value responses and flattens the error response to a simple dictionary with the `error` (previously `status`), `message`, and `stacktrace` properties, which are now all required. r=jgriffin --HG-- extra : commitid : FbEkv70rxl9 extra : rebase_source : 3116110a0d197289cc95eba8748be0a33566c5a5
2015-05-21 13:26:58 +03:00
addMessageListenerId("Marionette:getCookies", getCookiesFn);
addMessageListenerId("Marionette:deleteAllCookies", deleteAllCookiesFn);
addMessageListenerId("Marionette:deleteCookie", deleteCookieFn);
}
/**
* Used during newSession and restart, called to set up the modal dialog listener in b2g
*/
function waitForReady() {
if (content.document.readyState == 'complete') {
readyStateTimer.cancel();
content.addEventListener("mozbrowsershowmodalprompt", modalHandler);
content.addEventListener("unload", waitForReady);
}
else {
readyStateTimer.initWithCallback(waitForReady, 100, Ci.nsITimer.TYPE_ONE_SHOT);
}
}
/**
* Called when we start a new session. It registers the
* current environment, and resets all values
*/
function newSession(msg) {
capabilities = session.Capabilities.fromJSON(msg.json);
isB2G = capabilities.get("platformName") === "B2G";
resetValues();
if (isB2G) {
readyStateTimer.initWithCallback(waitForReady, 100, Ci.nsITimer.TYPE_ONE_SHOT);
// We have to set correct mouse event source to MOZ_SOURCE_TOUCH
// to offer a way for event listeners to differentiate
// events being the result of a physical mouse action.
// This is especially important for the touch event shim,
// in order to prevent creating touch event for these fake mouse events.
legacyactions.inputSource = Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH;
}
}
/**
* Puts the current session to sleep, so all listeners are removed except
* for the 'restart' listener. This is used to keep the content listener
* alive for reuse in B2G instead of reloading it each time.
*/
function sleepSession(msg) {
deleteSession();
addMessageListener("Marionette:restart", restart);
}
/**
* Restarts all our listeners after this listener was put to sleep
*/
function restart(msg) {
removeMessageListener("Marionette:restart", restart);
if (isB2G) {
readyStateTimer.initWithCallback(waitForReady, 100, Ci.nsITimer.TYPE_ONE_SHOT);
}
registerSelf();
}
/**
* Removes all listeners
*/
function deleteSession(msg) {
removeMessageListenerId("Marionette:newSession", newSession);
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
removeMessageListenerId("Marionette:execute", executeFn);
removeMessageListenerId("Marionette:executeInSandbox", executeInSandboxFn);
removeMessageListenerId("Marionette:executeSimpleTest", executeSimpleTestFn);
removeMessageListenerId("Marionette:singleTap", singleTapFn);
removeMessageListenerId("Marionette:performActions", performActionsFn);
removeMessageListenerId("Marionette:releaseActions", releaseActionsFn);
removeMessageListenerId("Marionette:actionChain", actionChainFn);
removeMessageListenerId("Marionette:multiAction", multiActionFn);
removeMessageListenerId("Marionette:get", get);
removeMessageListenerId("Marionette:pollForReadyState", pollForReadyState);
removeMessageListenerId("Marionette:cancelRequest", cancelRequest);
removeMessageListenerId("Marionette:getTitle", getTitleFn);
removeMessageListenerId("Marionette:getPageSource", getPageSourceFn);
removeMessageListenerId("Marionette:getCurrentUrl", getCurrentUrlFn);
removeMessageListenerId("Marionette:goBack", goBack);
removeMessageListenerId("Marionette:goForward", goForward);
removeMessageListenerId("Marionette:refresh", refresh);
removeMessageListenerId("Marionette:findElementContent", findElementContentFn);
removeMessageListenerId("Marionette:findElementsContent", findElementsContentFn);
removeMessageListenerId("Marionette:getActiveElement", getActiveElementFn);
removeMessageListenerId("Marionette:clickElement", clickElementFn);
removeMessageListenerId("Marionette:getElementAttribute", getElementAttributeFn);
removeMessageListenerId("Marionette:getElementProperty", getElementPropertyFn);
removeMessageListenerId("Marionette:getElementText", getElementTextFn);
removeMessageListenerId("Marionette:getElementTagName", getElementTagNameFn);
removeMessageListenerId("Marionette:isElementDisplayed", isElementDisplayedFn);
removeMessageListenerId("Marionette:getElementValueOfCssProperty", getElementValueOfCssPropertyFn);
removeMessageListenerId("Marionette:getElementRect", getElementRectFn);
removeMessageListenerId("Marionette:isElementEnabled", isElementEnabledFn);
removeMessageListenerId("Marionette:isElementSelected", isElementSelectedFn);
removeMessageListenerId("Marionette:sendKeysToElement", sendKeysToElementFn);
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:getAppCacheStatus", getAppCacheStatus);
removeMessageListenerId("Marionette:setTestName", setTestName);
removeMessageListenerId("Marionette:takeScreenshot", takeScreenshotFn);
removeMessageListenerId("Marionette:addCookie", addCookieFn);
Bug 1153822: Adjust Marionette responses to match WebDriver protocol Introduce protocol version levels in the Marionette server. On establishing a connection to a local end, the remote will return a `marionetteProtocol` field indicating which level it speaks. The protocol level can be used by local ends to either fall into compatibility mode or warn the user that the local end is incompatible with the remote. The protocol is currently also more expressive than it needs to be and this expressiveness has previously resulted in subtle inconsistencies in the fields returned. This patch reduces the amount of superfluous fields, reducing the amount of data sent. Aligning the protocol closer to the WebDriver specification's expectations will also reduce the amount of post-processing required in the httpd. Previous to this patch, this is a value response: {"from":"0","value":null,"status":0,"sessionId":"{6b6d68d2-4ac9-4308-9f07-d2e72519c407}"} And this for ok responses: {"from":"0","ok":true} And this for errors: {"from":"0","status":21,"sessionId":"{6b6d68d2-4ac9-4308-9f07-d2e72519c407}","error":{"message":"Error loading page, timed out (onDOMContentLoaded)","stacktrace":null,"status":21}} This patch drops the `from` and `sessionId` fields, and the `status` field from non-error responses. It also drops the `ok` field in non-value responses and flattens the error response to a simple dictionary with the `error` (previously `status`), `message`, and `stacktrace` properties, which are now all required. r=jgriffin --HG-- extra : commitid : FbEkv70rxl9 extra : rebase_source : 3116110a0d197289cc95eba8748be0a33566c5a5
2015-05-21 13:26:58 +03:00
removeMessageListenerId("Marionette:getCookies", getCookiesFn);
removeMessageListenerId("Marionette:deleteAllCookies", deleteAllCookiesFn);
removeMessageListenerId("Marionette:deleteCookie", deleteCookieFn);
if (isB2G) {
content.removeEventListener("mozbrowsershowmodalprompt", modalHandler);
}
seenEls.clear();
// reset container frame to the top-most frame
curContainer = { frame: content, shadowRoot: null };
curContainer.frame.focus();
legacyactions.touchIds = {};
if (action.inputStateMap !== undefined) {
action.inputStateMap.clear();
}
if (action.inputsToCancel !== undefined) {
action.inputsToCancel.length = 0;
}
}
/**
* Send asynchronous reply to chrome.
*
* @param {UUID} uuid
* Unique identifier of the request.
* @param {AsyncContentSender.ResponseType} type
* Type of response.
* @param {?=} data
* JSON serialisable object to accompany the message. Defaults to
* an empty dictionary.
*/
function sendToServer(uuid, data = undefined) {
let channel = new proxy.AsyncMessageChannel(
() => this,
sendAsyncMessage.bind(this));
channel.reply(uuid, data);
}
/**
* Send asynchronous reply with value to chrome.
*
* @param {?} obj
* JSON serialisable object of arbitrary type and complexity.
* @param {UUID} uuid
* Unique identifier of the request.
*/
function sendResponse(obj, id) {
sendToServer(id, obj);
}
/**
* Send asynchronous reply to chrome.
*
* @param {UUID} uuid
* Unique identifier of the request.
*/
function sendOk(uuid) {
sendToServer(uuid);
}
/**
* Send asynchronous error reply to chrome.
*
* @param {Error} err
* Error to notify chrome of.
* @param {UUID} uuid
* Unique identifier of the request.
*/
function sendError(err, uuid) {
sendToServer(uuid, err);
}
/**
* Send log message to server
*/
function sendLog(msg) {
sendToServer("Marionette:log", {message: msg});
}
/**
* Clear test values after completion of test
*/
function resetValues() {
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
sandboxes.clear();
curContainer = {frame: content, shadowRoot: null};
legacyactions.mouseEventsOnly = false;
action.inputStateMap = new Map();
action.inputsToCancel = [];
}
/**
* Dump a logline to stdout. Prepends logline with a timestamp.
*/
function dumpLog(logline) {
dump(Date.now() + " Marionette: " + logline);
}
/**
* Check if our context was interrupted
*/
function wasInterrupted() {
if (previousContainer) {
let element = content.document.elementFromPoint((content.innerWidth/2), (content.innerHeight/2));
if (element.id.indexOf("modal-dialog") == -1) {
return true;
}
else {
return false;
}
}
return sendSyncMessage("MarionetteFrame:getInterruptedState", {})[0].value;
}
function checkForInterrupted() {
if (wasInterrupted()) {
if (previousContainer) {
// if previousContainer is set, then we're in a single process environment
curContainer = legacyactions.container = previousContainer;
previousContainer = null;
}
else {
//else we're in OOP environment, so we'll switch to the original OOP frame
sendSyncMessage("Marionette:switchToModalOrigin");
}
sendSyncMessage("Marionette:switchedToFrame", { restorePrevious: true });
}
}
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
function* execute(script, args, timeout, opts) {
opts.timeout = timeout;
script = importedScripts.for("content").concat(script);
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
let sb = sandbox.createMutable(curContainer.frame);
let wargs = element.fromJson(
args, seenEls, curContainer.frame, curContainer.shadowRoot);
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
let res = yield evaluate.sandbox(sb, script, wargs, opts);
return element.toJson(res, seenEls);
}
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
function* executeInSandbox(script, args, timeout, opts) {
opts.timeout = timeout;
script = importedScripts.for("content").concat(script);
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
let sb = sandboxes.get(opts.sandboxName, opts.newSandbox);
if (opts.sandboxName) {
sb = sandbox.augment(sb, {global: sb});
sb = sandbox.augment(sb, new logging.Adapter(contentLog));
}
let wargs = element.fromJson(
args, seenEls, curContainer.frame, curContainer.shadowRoot);
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
let evaluatePromise = evaluate.sandbox(sb, script, wargs, opts);
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
let res = yield evaluatePromise;
sendSyncMessage(
"Marionette:shareData",
{log: element.toJson(contentLog.get(), seenEls)});
return element.toJson(res, seenEls);
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
}
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
function* executeSimpleTest(script, args, timeout, opts) {
opts.timeout = timeout;
let win = curContainer.frame;
script = importedScripts.for("content").concat(script);
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
let harness = new simpletest.Harness(
win,
"content",
contentLog,
timeout,
marionetteTestName);
let sb = sandbox.createSimpleTest(curContainer.frame, harness);
// TODO(ato): Not sure this is needed:
sb = sandbox.augment(sb, new logging.Adapter(contentLog));
let wargs = element.fromJson(
args, seenEls, curContainer.frame, curContainer.shadowRoot);
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
let evaluatePromise = evaluate.sandbox(sb, script, wargs, opts);
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
let res = yield evaluatePromise;
sendSyncMessage(
"Marionette:shareData",
{log: element.toJson(contentLog.get(), seenEls)});
return element.toJson(res, seenEls);
}
/**
* Sets the test name, used in logging messages.
*/
function setTestName(msg) {
marionetteTestName = msg.json.value;
sendOk(msg.json.command_id);
}
/**
* This function creates a touch event given a touch type and a touch
*/
function emitTouchEvent(type, touch) {
if (!wasInterrupted()) {
let loggingInfo = "emitting Touch event of type " + type + " to element with id: " + touch.target.id + " and tag name: " + touch.target.tagName + " at coordinates (" + touch.clientX + ", " + touch.clientY + ") relative to the viewport";
dumpLog(loggingInfo);
var docShell = curContainer.frame.document.defaultView.
QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsIWebNavigation).
QueryInterface(Components.interfaces.nsIDocShell);
if (docShell.asyncPanZoomEnabled && legacyactions.scrolling) {
// if we're in APZ and we're scrolling, we must use sendNativeTouchPoint to dispatch our touchmove events
let index = sendSyncMessage("MarionetteFrame:getCurrentFrameId");
// only call emitTouchEventForIFrame if we're inside an iframe.
if (index != null) {
sendSyncMessage("Marionette:emitTouchEvent",
{ index: index, type: type, id: touch.identifier,
clientX: touch.clientX, clientY: touch.clientY,
screenX: touch.screenX, screenY: touch.screenY,
radiusX: touch.radiusX, radiusY: touch.radiusY,
rotation: touch.rotationAngle, force: touch.force });
return;
}
}
// we get here if we're not in asyncPacZoomEnabled land, or if we're the main process
/*
Disabled per bug 888303
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
contentLog.log(loggingInfo, "TRACE");
sendSyncMessage(
"Marionette:shareData",
{log: element.toJson(contentLog.get(), seenEls)});
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
contentLog.clear();
*/
let domWindowUtils = curContainer.frame.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowUtils);
domWindowUtils.sendTouchEvent(type, [touch.identifier], [touch.clientX], [touch.clientY], [touch.radiusX], [touch.radiusY], [touch.rotationAngle], [touch.force], 1, 0);
}
}
/**
* Function that perform a single tap
*/
function singleTap(id, corx, cory) {
let el = seenEls.get(id, curContainer);
// after this block, the element will be scrolled into view
let visible = element.isVisible(el, corx, cory);
if (!visible) {
throw new ElementNotVisibleError("Element is not currently visible and may not be manipulated");
}
let a11y = accessibility.get(capabilities.get("moz:accessibilityChecks"));
return a11y.getAccessible(el, true).then(acc => {
a11y.assertVisible(acc, el, visible);
a11y.assertActionable(acc, el);
if (!curContainer.frame.document.createTouch) {
legacyactions.mouseEventsOnly = true;
}
let c = element.coordinates(el, corx, cory);
if (!legacyactions.mouseEventsOnly) {
let touchId = legacyactions.nextTouchId++;
let touch = createATouch(el, c.x, c.y, touchId);
emitTouchEvent('touchstart', touch);
emitTouchEvent('touchend', touch);
}
legacyactions.mouseTap(el.ownerDocument, c.x, c.y);
});
}
/**
* Function to create a touch based on the element
* corx and cory are relative to the viewport, id is the touchId
*/
function createATouch(el, corx, cory, touchId) {
let doc = el.ownerDocument;
let win = doc.defaultView;
let [clientX, clientY, pageX, pageY, screenX, screenY] =
legacyactions.getCoordinateInfo(el, corx, cory);
let atouch = doc.createTouch(win, el, touchId, pageX, pageY, screenX, screenY, clientX, clientY);
return atouch;
}
/**
* Perform a series of grouped actions at the specified points in time.
*
* @param {obj} msg
* Object with an |actions| attribute that is an Array of objects
* each of which represents an action sequence.
*/
function* performActions(msg) {
let chain = action.Chain.fromJson(msg.actions);
yield action.dispatch(chain, seenEls, curContainer);
}
/**
* The Release Actions command is used to release all the keys and pointer
* buttons that are currently depressed. This causes events to be fired as if
* the state was released by an explicit series of actions. It also clears all
* the internal state of the virtual devices.
*/
function* releaseActions() {
yield action.dispatchTickActions(action.inputsToCancel.reverse(), 0, seenEls, curContainer);
action.inputsToCancel.length = 0;
action.inputStateMap.clear();
}
/**
* Start action chain on one finger.
*/
function actionChain(chain, touchId) {
let touchProvider = {};
touchProvider.createATouch = createATouch;
touchProvider.emitTouchEvent = emitTouchEvent;
return legacyactions.dispatchActions(
chain,
touchId,
curContainer,
seenEls,
touchProvider);
}
/**
* Function to emit touch events which allow multi touch on the screen
* @param type represents the type of event, touch represents the current touch,touches are all pending touches
*/
function emitMultiEvents(type, touch, touches) {
let target = touch.target;
let doc = target.ownerDocument;
let win = doc.defaultView;
// touches that are in the same document
let documentTouches = doc.createTouchList(touches.filter(function (t) {
return ((t.target.ownerDocument === doc) && (type != 'touchcancel'));
}));
// touches on the same target
let targetTouches = doc.createTouchList(touches.filter(function (t) {
return ((t.target === target) && ((type != 'touchcancel') || (type != 'touchend')));
}));
// Create changed touches
let changedTouches = doc.createTouchList(touch);
// Create the event object
let event = doc.createEvent('TouchEvent');
event.initTouchEvent(type,
true,
true,
win,
0,
false, false, false, false,
documentTouches,
targetTouches,
changedTouches);
target.dispatchEvent(event);
}
/**
* Function to dispatch one set of actions
* @param touches represents all pending touches, batchIndex represents the batch we are dispatching right now
*/
function setDispatch(batches, touches, batchIndex=0) {
// check if all the sets have been fired
if (batchIndex >= batches.length) {
multiLast = {};
return;
}
// a set of actions need to be done
let batch = batches[batchIndex];
// each action for some finger
let pack;
// the touch id for the finger (pack)
let touchId;
// command for the finger
let command;
// touch that will be created for the finger
let el;
let corx;
let cory;
let touch;
let lastTouch;
let touchIndex;
let waitTime = 0;
let maxTime = 0;
let c;
// loop through the batch
batchIndex++;
for (let i = 0; i < batch.length; i++) {
pack = batch[i];
touchId = pack[0];
command = pack[1];
switch (command) {
case "press":
el = seenEls.get(pack[2], curContainer);
c = element.coordinates(el, pack[3], pack[4]);
touch = createATouch(el, c.x, c.y, touchId);
multiLast[touchId] = touch;
touches.push(touch);
emitMultiEvents("touchstart", touch, touches);
break;
case "release":
touch = multiLast[touchId];
// the index of the previous touch for the finger may change in the touches array
touchIndex = touches.indexOf(touch);
touches.splice(touchIndex, 1);
emitMultiEvents("touchend", touch, touches);
break;
case "move":
el = seenEls.get(pack[2], curContainer);
c = element.coordinates(el);
touch = createATouch(multiLast[touchId].target, c.x, c.y, touchId);
touchIndex = touches.indexOf(lastTouch);
touches[touchIndex] = touch;
multiLast[touchId] = touch;
emitMultiEvents("touchmove", touch, touches);
break;
case "moveByOffset":
el = multiLast[touchId].target;
lastTouch = multiLast[touchId];
touchIndex = touches.indexOf(lastTouch);
let doc = el.ownerDocument;
let win = doc.defaultView;
// since x and y are relative to the last touch, therefore, it's relative to the position of the last touch
let clientX = lastTouch.clientX + pack[2],
clientY = lastTouch.clientY + pack[3];
let pageX = clientX + win.pageXOffset,
pageY = clientY + win.pageYOffset;
let screenX = clientX + win.mozInnerScreenX,
screenY = clientY + win.mozInnerScreenY;
touch = doc.createTouch(win, el, touchId, pageX, pageY, screenX, screenY, clientX, clientY);
touches[touchIndex] = touch;
multiLast[touchId] = touch;
emitMultiEvents("touchmove", touch, touches);
break;
case "wait":
if (typeof pack[2] != "undefined") {
waitTime = pack[2] * 1000;
if (waitTime > maxTime) {
maxTime = waitTime;
}
}
break;
}
}
if (maxTime != 0) {
checkTimer.initWithCallback(function() {
setDispatch(batches, touches, batchIndex);
}, maxTime, Ci.nsITimer.TYPE_ONE_SHOT);
} else {
setDispatch(batches, touches, batchIndex);
}
}
/**
* Start multi-action.
*
* @param {Number} maxLen
* Longest action chain for one finger.
*/
function multiAction(args, maxLen) {
// unwrap the original nested array
let commandArray = element.fromJson(
args, seenEls, curContainer.frame, curContainer.shadowRoot);
let concurrentEvent = [];
let temp;
for (let i = 0; i < maxLen; i++) {
let row = [];
for (let j = 0; j < commandArray.length; j++) {
if (typeof commandArray[j][i] != "undefined") {
// add finger id to the front of each action, i.e. [finger_id, action, element]
temp = commandArray[j][i];
temp.unshift(j);
row.push(temp);
}
}
concurrentEvent.push(row);
}
// now concurrent event is made of sets where each set contain a list of actions that need to be fired.
// note: each action belongs to a different finger
// pendingTouches keeps track of current touches that's on the screen
let pendingTouches = [];
setDispatch(concurrentEvent, pendingTouches);
}
/**
* This implements the latter part of a get request (for the case we need to resume one
* when a remoteness update happens in the middle of a navigate request). This is most of
* of the work of a navigate request, but doesn't assume DOMContentLoaded is yet to fire.
*
* @param {function=} cleanupCallback
* Callback to execute when registered event handlers or observer notifications
* have to be cleaned-up.
* @param {number} command_id
* ID of the currently handled message between the driver and listener.
* @param {string=} lastSeenURL
* Last URL as seen before the navigation request got triggered.
* @param {number} pageTimeout
* Timeout in seconds the method has to wait for the page being finished loading.
* @param {number} startTime
* Unix timestap when the navitation request got triggred.
*/
function pollForReadyState(msg) {
let {cleanupCallback, command_id, lastSeenURL, pageTimeout, startTime} = msg.json;
if (typeof startTime == "undefined") {
startTime = new Date().getTime();
}
if (typeof cleanupCallback == "undefined") {
cleanupCallback = () => {};
}
let endTime = startTime + pageTimeout;
let checkLoad = () => {
navTimer.cancel();
let doc = curContainer.frame.document;
if (pageTimeout === null || new Date().getTime() <= endTime) {
// Under some conditions (eg. for error pages) the pagehide event is fired
// even with a readyState complete for the formerly loaded page.
// To prevent race conditition for goBack and goForward we have to wait
// until the last seen page has been fully unloaded.
// TODO: Bug 1333458 has to improve this.
if (!doc.location || lastSeenURL && doc.location.href === lastSeenURL) {
navTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT);
// document fully loaded
} else if (doc.readyState === "complete") {
cleanupCallback();
sendOk(command_id);
// document with an insecure cert
} else if (doc.readyState === "interactive" &&
doc.baseURI.startsWith("about:certerror")) {
cleanupCallback();
sendError(new InsecureCertificateError(), command_id);
// we have reached an error url without requesting it
} else if (doc.readyState === "interactive" &&
/about:.+(error)\?/.exec(doc.baseURI)) {
cleanupCallback();
sendError(new UnknownError("Reached error page: " + doc.baseURI), command_id);
// return early for about: urls
} else if (doc.readyState === "interactive" && doc.baseURI.startsWith("about:")) {
cleanupCallback();
sendOk(command_id);
// document not fully loaded
} else {
navTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT);
}
} else {
cleanupCallback();
sendError(new TimeoutError("Error loading page, timed out (checkLoad)"), command_id);
}
};
checkLoad();
}
/**
* Navigate to the given URL. The operation will be performed on the
* current browsing context, which means it handles the case where we
* navigate within an iframe. All other navigation is handled by the
* driver (in chrome space).
*/
function get(msg) {
let {pageTimeout, url, command_id} = msg.json;
let startTime = new Date().getTime();
// We need to move to the top frame before navigating
sendSyncMessage("Marionette:switchedToFrame", {frameValue: null});
curContainer.frame = content;
let docShell = curContainer.frame
.document
.defaultView
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
let sawLoad = false;
let requestedURL;
let loadEventExpected = false;
try {
requestedURL = new URL(url).toString();
let curURL = curContainer.frame.location;
loadEventExpected = navigate.isLoadEventExpected(curURL, requestedURL);
} catch (e) {
sendError(new InvalidArgumentError("Malformed URL: " + e.message), command_id);
return;
}
// It's possible that a site we're being sent to will end up redirecting
// us before we end up on a page that fires DOMContentLoaded. We can ensure
// This loadListener ensures that we don't send a success signal back to
// the caller until we've seen the load of the requested URL attempted
// on this frame.
let loadListener = {
QueryInterface: XPCOMUtils.generateQI(
[Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]),
onStateChange(webProgress, request, state, status) {
if (!(request instanceof Ci.nsIChannel)) {
return;
}
const isDocument = state & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
const loadedURL = request.URI.spec;
// We have to look at the originalURL because of about: pages,
// the loadedURL is what the about: page resolves to, and is
// not the one that was requested.
const originalURL = request.originalURI.spec;
const isRequestedURL = loadedURL == requestedURL ||
originalURL == requestedURL;
if (!isDocument || !isRequestedURL) {
return;
}
// We started loading the requested document. This document
// might not be the one that ends up firing DOMContentLoaded
// (if it, for example, redirects), but because we've started
// loading this URL, we know that any future DOMContentLoaded's
// are fair game to tell the Marionette client about.
if (state & Ci.nsIWebProgressListener.STATE_START) {
sawLoad = true;
}
// This indicates network stop or last request stop outside of
// loading the document. We hit this when DOMContentLoaded is
// not triggered, which is the case for image documents.
else if (state & Ci.nsIWebProgressListener.STATE_STOP &&
content.document instanceof content.ImageDocument) {
pollForReadyState({json: {
command_id: command_id,
pageTimeout: pageTimeout,
startTime: startTime,
cleanupCallback: () => {
webProgress.removeProgressListener(loadListener);
removeEventListener("DOMContentLoaded", onDOMContentLoaded, false);
}
}});
}
},
onLocationChange() {},
onProgressChange() {},
onStatusChange() {},
onSecurityChange() {},
};
webProgress.addProgressListener(
loadListener, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
// Prevent DOMContentLoaded events from frames from invoking this
// code, unless the event is coming from the frame associated with
// the current window (i.e. someone has used switch_to_frame).
onDOMContentLoaded = ev => {
let frameEl = ev.originalTarget.defaultView.frameElement;
let correctFrame = !frameEl || frameEl == curContainer.frame.frameElement;
// If the page we're at fired DOMContentLoaded and appears to
// be the one we asked to load, then we definitely saw the load
// occur. We need this because for error pages, like about:neterror
// for unsupported protocols, we don't end up opening a channel that
// our WebProgressListener can monitor.
if (curContainer.frame.location == requestedURL) {
sawLoad = true;
}
// We also need to make sure that if the requested URL is not
// about:blank the DOMContentLoaded we saw isn't for the initial
// about:blank of a newly created docShell.
let loadedRequestedURI = (requestedURL == "about:blank") ||
docShell.hasLoadedNonBlankURI;
if (correctFrame && sawLoad && loadedRequestedURI) {
pollForReadyState({json: {
command_id: command_id,
pageTimeout: pageTimeout,
startTime: startTime,
cleanupCallback: () => {
webProgress.removeProgressListener(loadListener);
removeEventListener("DOMContentLoaded", onDOMContentLoaded, false);
}
}});
}
};
if (typeof pageTimeout != "undefined") {
let onTimeout = () => {
if (loadEventExpected) {
removeEventListener("DOMContentLoaded", onDOMContentLoaded, false);
}
webProgress.removeProgressListener(loadListener);
sendError(new TimeoutError("Error loading page, timed out (onDOMContentLoaded)"), command_id);
};
navTimer.initWithCallback(onTimeout, pageTimeout, Ci.nsITimer.TYPE_ONE_SHOT);
}
if (loadEventExpected) {
addEventListener("DOMContentLoaded", onDOMContentLoaded, false);
}
curContainer.frame.location = requestedURL;
if (!loadEventExpected) {
sendOk(command_id);
}
}
/**
* Cancel the polling and remove the event listener associated with a
* current navigation request in case we're interupted by an onbeforeunload
* handler and navigation doesn't complete.
*/
function cancelRequest() {
navTimer.cancel();
if (onDOMContentLoaded) {
removeEventListener("DOMContentLoaded", onDOMContentLoaded, false);
}
}
/**
* Get URL of the top-level browsing context.
*/
function getCurrentUrl() {
return content.location.href;
}
/**
* Get the title of the current browsing context.
*/
function getTitle() {
return curContainer.frame.top.document.title;
}
/**
* Get source of the current browsing context's DOM.
*/
function getPageSource() {
return curContainer.frame.document.documentElement.outerHTML;
}
/**
* Wait for the current page to be unloaded after a navigation got triggered.
*
* @param {function} trigger
* Callback to execute which triggers a page navigation.
* @param {function} doneCallback
* Callback to execute when the current page has been unloaded.
*
* It receives a dictionary with the following items as argument:
* loading - Flag if a page load will follow.
* lastSeenURL - Last seen URL before the navigation request.
* startTime - Time when the navigation request has been triggered.
*/
function waitForPageUnloaded(trigger, doneCallback) {
let currentURL = curContainer.frame.location.href;
let start = new Date().getTime();
function handleEvent(event) {
// In case of a remoteness change it can happen that we are no longer able
// to access the document's location. In those cases ignore the event,
// but keep the code waiting, and assume in the driver that waiting for the
// page load is necessary. Bug 1333458 should improve things.
if (typeof event.originalTarget.location == "undefined") {
return;
}
switch (event.type) {
case "hashchange":
removeEventListener("hashchange", handleEvent);
removeEventListener("pagehide", handleEvent);
removeEventListener("unload", handleEvent);
doneCallback({loading: false, lastSeenURL: currentURL});
break;
case "pagehide":
case "unload":
if (event.originalTarget === curContainer.frame.document) {
removeEventListener("hashchange", handleEvent);
removeEventListener("pagehide", handleEvent);
removeEventListener("unload", handleEvent);
doneCallback({loading: true, lastSeenURL: currentURL, startTime: start});
}
break;
}
}
addEventListener("hashchange", handleEvent, false);
addEventListener("pagehide", handleEvent, false);
addEventListener("unload", handleEvent, false);
trigger();
}
/**
* Cause the browser to traverse one step backward in the joint history
* of the current browsing context.
*
* @param {number} command_id
* ID of the currently handled message between the driver and listener.
* @param {number} pageTimeout
* Timeout in milliseconds the method has to wait for the page being finished loading.
*/
function goBack(msg) {
let {command_id, pageTimeout} = msg.json;
waitForPageUnloaded(() => {
curContainer.frame.history.back();
}, pageLoadStatus => {
if (pageLoadStatus.loading) {
pollForReadyState({json: {
command_id: command_id,
lastSeenURL: pageLoadStatus.lastSeenURL,
pageTimeout: pageTimeout,
startTime: pageLoadStatus.startTime,
}});
} else {
sendOk(command_id);
}
});
}
/**
* Cause the browser to traverse one step forward in the joint history
* of the current browsing context.
*
* @param {number} command_id
* ID of the currently handled message between the driver and listener.
* @param {number} pageTimeout
* Timeout in milliseconds the method has to wait for the page being finished loading.
*/
function goForward(msg) {
let {command_id, pageTimeout} = msg.json;
waitForPageUnloaded(() => {
curContainer.frame.history.forward();
}, pageLoadStatus => {
if (pageLoadStatus.loading) {
pollForReadyState({json: {
command_id: command_id,
lastSeenURL: pageLoadStatus.lastSeenURL,
pageTimeout: pageTimeout,
startTime: pageLoadStatus.startTime,
}});
} else {
sendOk(command_id);
}
});
}
/**
* Refresh the page
*/
function refresh(msg) {
let command_id = msg.json.command_id;
curContainer.frame.location.reload(true);
let listen = function() {
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
removeEventListener("DOMContentLoaded", listen, false);
sendOk(command_id);
};
addEventListener("DOMContentLoaded", listen, false);
}
/**
* Find an element in the current browsing context's document using the
* given search strategy.
*/
function* findElementContent(strategy, selector, opts = {}) {
if (!SUPPORTED_STRATEGIES.has(strategy)) {
throw new InvalidSelectorError("Strategy not supported: " + strategy);
}
opts.all = false;
if (opts.startNode) {
opts.startNode = seenEls.get(opts.startNode, curContainer);
}
let el = yield element.find(curContainer, strategy, selector, opts);
let elRef = seenEls.add(el);
let webEl = element.makeWebElement(elRef);
return webEl;
}
/**
* Find elements in the current browsing context's document using the
* given search strategy.
*/
function* findElementsContent(strategy, selector, opts = {}) {
if (!SUPPORTED_STRATEGIES.has(strategy)) {
throw new InvalidSelectorError("Strategy not supported: " + strategy);
}
opts.all = true;
if (opts.startNode) {
opts.startNode = seenEls.get(opts.startNode, curContainer);
}
let els = yield element.find(curContainer, strategy, selector, opts);
let elRefs = seenEls.addAll(els);
let webEls = elRefs.map(element.makeWebElement);
return webEls;
}
/** Find and return the active element on the page. */
function getActiveElement() {
let el = curContainer.frame.document.activeElement;
return element.toJson(el, seenEls);
}
/**
* Send click event to element.
*
* @param {WebElement} id
* Reference to the web element to click.
*/
function clickElement(id) {
let el = seenEls.get(id, curContainer);
return interaction.clickElement(
el,
capabilities.get("moz:accessibilityChecks"),
capabilities.get("specificationLevel") >= 1);
}
function getElementAttribute(id, name) {
let el = seenEls.get(id, curContainer);
if (element.isBooleanAttribute(el, name)) {
if (el.hasAttribute(name)) {
return "true";
} else {
return null;
}
} else {
return el.getAttribute(name);
}
}
function getElementProperty(id, name) {
let el = seenEls.get(id, curContainer);
return typeof el[name] != "undefined" ? el[name] : null;
}
/**
* Get the text of this element. This includes text from child elements.
*
* @param {WebElement} id
* Reference to web element.
*
* @return {string}
* Text of element.
*/
function getElementText(id) {
let el = seenEls.get(id, curContainer);
return atom.getElementText(el, curContainer.frame);
}
/**
* Get the tag name of an element.
*
* @param {WebElement} id
* Reference to web element.
*
* @return {string}
* Tag name of element.
*/
function getElementTagName(id) {
let el = seenEls.get(id, curContainer);
return el.tagName.toLowerCase();
}
/**
* Determine the element displayedness of the given web element.
*
* Also performs additional accessibility checks if enabled by session
* capability.
*/
function isElementDisplayed(id) {
let el = seenEls.get(id, curContainer);
return interaction.isElementDisplayed(
el, capabilities.get("moz:accessibilityChecks"));
}
/**
* Retrieves the computed value of the given CSS property of the given
* web element.
*
* @param {String} id
* Web element reference.
* @param {String} prop
* The CSS property to get.
*
* @return {String}
* Effective value of the requested CSS property.
*/
function getElementValueOfCssProperty(id, prop) {
let el = seenEls.get(id, curContainer);
let st = curContainer.frame.document.defaultView.getComputedStyle(el);
return st.getPropertyValue(prop);
}
/**
* Get the position and dimensions of the element.
*
* @param {WebElement} id
* Reference to web element.
*
* @return {Object.<string, number>}
* The x, y, width, and height properties of the element.
*/
function getElementRect(id) {
let el = seenEls.get(id, curContainer);
let clientRect = el.getBoundingClientRect();
return {
x: clientRect.x + curContainer.frame.pageXOffset,
y: clientRect.y + curContainer.frame.pageYOffset,
width: clientRect.width,
height: clientRect.height
};
}
/**
* Check if element is enabled.
*
* @param {WebElement} id
* Reference to web element.
*
* @return {boolean}
* True if enabled, false otherwise.
*/
function isElementEnabled(id) {
let el = seenEls.get(id, curContainer);
return interaction.isElementEnabled(
el, capabilities.get("moz:accessibilityChecks"));
}
/**
* Determines if the referenced element is selected or not.
*
* This operation only makes sense on input elements of the Checkbox-
* and Radio Button states, or option elements.
*/
function isElementSelected(id) {
let el = seenEls.get(id, curContainer);
return interaction.isElementSelected(
el, capabilities.get("moz:accessibilityChecks"));
}
function* sendKeysToElement(id, val) {
let el = seenEls.get(id, curContainer);
if (el.type == "file") {
let path = val.join("");
yield interaction.uploadFile(el, path);
} else {
yield interaction.sendKeysToElement(
el, val, false, capabilities.get("moz:accessibilityChecks"));
}
}
/**
* Clear the text of an element.
*/
function clearElement(id) {
try {
let el = seenEls.get(id, curContainer);
if (el.type == "file") {
el.value = null;
} else {
atom.clearElement(el, curContainer.frame);
}
} catch (e) {
// Bug 964738: Newer atoms contain status codes which makes wrapping
// this in an error prototype that has a status property unnecessary
if (e.name == "InvalidElementStateError") {
throw new InvalidElementStateError(e.message);
} else {
throw e;
}
}
}
/**
* Switch the current context to the specified host's Shadow DOM.
* @param {WebElement} id
* Reference to web element.
*/
function switchToShadowRoot(id) {
if (!id) {
// If no host element is passed, attempt to find a parent shadow root or, if
// none found, unset the current shadow root
if (curContainer.shadowRoot) {
let parent;
try {
parent = curContainer.shadowRoot.host;
} catch (e) {
// There is a chance that host element is dead and we are trying to
// access a dead object.
curContainer.shadowRoot = null;
return;
}
while (parent && !(parent instanceof curContainer.frame.ShadowRoot)) {
parent = parent.parentNode;
}
curContainer.shadowRoot = parent;
}
return;
}
let foundShadowRoot;
let hostEl = seenEls.get(id, curContainer);
foundShadowRoot = hostEl.shadowRoot;
if (!foundShadowRoot) {
throw new NoSuchElementError('Unable to locate shadow root: ' + id);
}
curContainer.shadowRoot = foundShadowRoot;
}
/**
* Switch to the parent frame of the current Frame. If the frame is the top most
* is the current frame then no action will happen.
*/
function switchToParentFrame(msg) {
let command_id = msg.json.command_id;
curContainer.frame = curContainer.frame.parent;
let parentElement = seenEls.add(curContainer.frame);
sendSyncMessage(
"Marionette:switchedToFrame", {frameValue: parentElement});
sendOk(msg.json.command_id);
}
/**
* Switch to frame given either the server-assigned element id,
* its index in window.frames, or the iframe's name or id.
*/
function switchToFrame(msg) {
let command_id = msg.json.command_id;
function checkLoad() {
let errorRegex = /about:.+(error)|(blocked)\?/;
if (curContainer.frame.document.readyState == "complete") {
sendOk(command_id);
return;
} else if (curContainer.frame.document.readyState == "interactive" &&
errorRegex.exec(curContainer.frame.document.baseURI)) {
sendError(new UnknownError("Error loading page"), command_id);
return;
}
checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT);
}
let foundFrame = null;
let frames = [];
let parWindow = null;
// Check of the curContainer.frame reference is dead
try {
frames = curContainer.frame.frames;
//Until Bug 761935 lands, we won't have multiple nested OOP iframes. We will only have one.
//parWindow will refer to the iframe above the nested OOP frame.
parWindow = curContainer.frame.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
} catch (e) {
// We probably have a dead compartment so accessing it is going to make Firefox
// very upset. Let's now try redirect everything to the top frame even if the
// user has given us a frame since search doesnt look up.
msg.json.id = null;
msg.json.element = null;
}
if ((msg.json.id === null || msg.json.id === undefined) && (msg.json.element == null)) {
// returning to root frame
sendSyncMessage("Marionette:switchedToFrame", { frameValue: null });
curContainer.frame = content;
if(msg.json.focus == true) {
curContainer.frame.focus();
}
checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT);
return;
}
let id = msg.json.element;
if (seenEls.has(id)) {
let wantedFrame;
try {
wantedFrame = seenEls.get(id, curContainer);
} catch (e) {
sendError(e, command_id);
}
if (frames.length > 0) {
for (let i = 0; i < frames.length; i++) {
// use XPCNativeWrapper to compare elements; see bug 834266
if (XPCNativeWrapper(frames[i].frameElement) == XPCNativeWrapper(wantedFrame)) {
curContainer.frame = frames[i].frameElement;
foundFrame = i;
}
}
}
if (foundFrame === null) {
// Either the frame has been removed or we have a OOP frame
// so lets just get all the iframes and do a quick loop before
// throwing in the towel
let iframes = curContainer.frame.document.getElementsByTagName("iframe");
for (var i = 0; i < iframes.length; i++) {
if (XPCNativeWrapper(iframes[i]) == XPCNativeWrapper(wantedFrame)) {
curContainer.frame = iframes[i];
foundFrame = i;
}
}
}
}
if (foundFrame === null) {
if (typeof(msg.json.id) === 'number') {
try {
foundFrame = frames[msg.json.id].frameElement;
if (foundFrame !== null) {
curContainer.frame = foundFrame;
foundFrame = seenEls.add(curContainer.frame);
}
else {
// If foundFrame is null at this point then we have the top level browsing
// context so should treat it accordingly.
sendSyncMessage("Marionette:switchedToFrame", { frameValue: null});
curContainer.frame = content;
if(msg.json.focus == true) {
curContainer.frame.focus();
}
checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT);
return;
}
} catch (e) {
// Since window.frames does not return OOP frames it will throw
// and we land up here. Let's not give up and check if there are
// iframes and switch to the indexed frame there
let iframes = curContainer.frame.document.getElementsByTagName("iframe");
if (msg.json.id >= 0 && msg.json.id < iframes.length) {
curContainer.frame = iframes[msg.json.id];
foundFrame = msg.json.id;
}
}
}
}
if (foundFrame === null) {
sendError(new NoSuchFrameError("Unable to locate frame: " + (msg.json.id || msg.json.element)), command_id);
return true;
}
// send a synchronous message to let the server update the currently active
// frame element (for getActiveFrame)
let frameValue = element.toJson(
curContainer.frame.wrappedJSObject, seenEls)[element.Key];
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
sendSyncMessage("Marionette:switchedToFrame", {frameValue: frameValue});
let rv = null;
if (curContainer.frame.contentWindow === null) {
// The frame we want to switch to is a remote/OOP frame;
// notify our parent to handle the switch
curContainer.frame = content;
rv = {win: parWindow, frame: foundFrame};
} else {
curContainer.frame = curContainer.frame.contentWindow;
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
if (msg.json.focus) {
curContainer.frame.focus();
Bug 1123506 - Evaluate scripts in content with lasting side-effects; r=automatedtester In order to achieve WebDriver parity, Marionette needs the ability to evaluate scripts in content space with lasting side-effects. This means that state modifications should affect behaviour and state of the browsing context, and such transgress the boundaries of the sandbox. This patch brings a new script evaluation module that is shared between code in chrome- and content space. This brings the number of unique script evaluation implementations in Marionette down from six to one. evaluate.sandbox provides the main entry-point for execution. It is compatible with existing Marionette uses of Execute Script and Execute Async Script commands in Mozilla clients, but also provides a new stateful sandbox for evaluation that should have lasting side-effects. It is not expected that Mozilla clients, such as testing/marionette/client and the Node.js client in Gaia, should have to change as a consequence of this change. A substantial change to the script's runtime environment is that many globals that previously existed are now only exposed whenever needed. This means for example that Simple Test harness functionality (waitFor, ok, isnot, is, &c.) is only available when using a sandbox augmented with a Simple Test harness adapter. Conversely, this patch does not expose marionetteScriptFinished as a callback to asynchronous scripts for sandboxes which sandboxName parameter is undefined, because this is what determines if the script should be evaluated under WebDriver conformance constraints. In all other cases where sandboxName _is_ defined, the traditional marionetteScriptFinished et al. runtime environment is preserved. MozReview-Commit-ID: 8FZ6rNVImuC
2016-02-26 17:36:39 +03:00
}
checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT);
}
sendResponse(rv, command_id);
}
function addCookie(cookie) {
cookies.add(cookie.name, cookie.value, cookie);
}
/**
* Get all cookies for the current domain.
*/
Bug 1153822: Adjust Marionette responses to match WebDriver protocol Introduce protocol version levels in the Marionette server. On establishing a connection to a local end, the remote will return a `marionetteProtocol` field indicating which level it speaks. The protocol level can be used by local ends to either fall into compatibility mode or warn the user that the local end is incompatible with the remote. The protocol is currently also more expressive than it needs to be and this expressiveness has previously resulted in subtle inconsistencies in the fields returned. This patch reduces the amount of superfluous fields, reducing the amount of data sent. Aligning the protocol closer to the WebDriver specification's expectations will also reduce the amount of post-processing required in the httpd. Previous to this patch, this is a value response: {"from":"0","value":null,"status":0,"sessionId":"{6b6d68d2-4ac9-4308-9f07-d2e72519c407}"} And this for ok responses: {"from":"0","ok":true} And this for errors: {"from":"0","status":21,"sessionId":"{6b6d68d2-4ac9-4308-9f07-d2e72519c407}","error":{"message":"Error loading page, timed out (onDOMContentLoaded)","stacktrace":null,"status":21}} This patch drops the `from` and `sessionId` fields, and the `status` field from non-error responses. It also drops the `ok` field in non-value responses and flattens the error response to a simple dictionary with the `error` (previously `status`), `message`, and `stacktrace` properties, which are now all required. r=jgriffin --HG-- extra : commitid : FbEkv70rxl9 extra : rebase_source : 3116110a0d197289cc95eba8748be0a33566c5a5
2015-05-21 13:26:58 +03:00
function getCookies() {
let rv = [];
for (let cookie of cookies) {
Bug 1153822: Adjust Marionette responses to match WebDriver protocol Introduce protocol version levels in the Marionette server. On establishing a connection to a local end, the remote will return a `marionetteProtocol` field indicating which level it speaks. The protocol level can be used by local ends to either fall into compatibility mode or warn the user that the local end is incompatible with the remote. The protocol is currently also more expressive than it needs to be and this expressiveness has previously resulted in subtle inconsistencies in the fields returned. This patch reduces the amount of superfluous fields, reducing the amount of data sent. Aligning the protocol closer to the WebDriver specification's expectations will also reduce the amount of post-processing required in the httpd. Previous to this patch, this is a value response: {"from":"0","value":null,"status":0,"sessionId":"{6b6d68d2-4ac9-4308-9f07-d2e72519c407}"} And this for ok responses: {"from":"0","ok":true} And this for errors: {"from":"0","status":21,"sessionId":"{6b6d68d2-4ac9-4308-9f07-d2e72519c407}","error":{"message":"Error loading page, timed out (onDOMContentLoaded)","stacktrace":null,"status":21}} This patch drops the `from` and `sessionId` fields, and the `status` field from non-error responses. It also drops the `ok` field in non-value responses and flattens the error response to a simple dictionary with the `error` (previously `status`), `message`, and `stacktrace` properties, which are now all required. r=jgriffin --HG-- extra : commitid : FbEkv70rxl9 extra : rebase_source : 3116110a0d197289cc95eba8748be0a33566c5a5
2015-05-21 13:26:58 +03:00
let expires = cookie.expires;
// session cookie, don't return an expiry
if (expires == 0) {
expires = null;
Bug 1153822: Adjust Marionette responses to match WebDriver protocol Introduce protocol version levels in the Marionette server. On establishing a connection to a local end, the remote will return a `marionetteProtocol` field indicating which level it speaks. The protocol level can be used by local ends to either fall into compatibility mode or warn the user that the local end is incompatible with the remote. The protocol is currently also more expressive than it needs to be and this expressiveness has previously resulted in subtle inconsistencies in the fields returned. This patch reduces the amount of superfluous fields, reducing the amount of data sent. Aligning the protocol closer to the WebDriver specification's expectations will also reduce the amount of post-processing required in the httpd. Previous to this patch, this is a value response: {"from":"0","value":null,"status":0,"sessionId":"{6b6d68d2-4ac9-4308-9f07-d2e72519c407}"} And this for ok responses: {"from":"0","ok":true} And this for errors: {"from":"0","status":21,"sessionId":"{6b6d68d2-4ac9-4308-9f07-d2e72519c407}","error":{"message":"Error loading page, timed out (onDOMContentLoaded)","stacktrace":null,"status":21}} This patch drops the `from` and `sessionId` fields, and the `status` field from non-error responses. It also drops the `ok` field in non-value responses and flattens the error response to a simple dictionary with the `error` (previously `status`), `message`, and `stacktrace` properties, which are now all required. r=jgriffin --HG-- extra : commitid : FbEkv70rxl9 extra : rebase_source : 3116110a0d197289cc95eba8748be0a33566c5a5
2015-05-21 13:26:58 +03:00
// date before epoch time, cap to epoch
} else if (expires == 1) {
expires = 0;
}
Bug 1153822: Adjust Marionette responses to match WebDriver protocol Introduce protocol version levels in the Marionette server. On establishing a connection to a local end, the remote will return a `marionetteProtocol` field indicating which level it speaks. The protocol level can be used by local ends to either fall into compatibility mode or warn the user that the local end is incompatible with the remote. The protocol is currently also more expressive than it needs to be and this expressiveness has previously resulted in subtle inconsistencies in the fields returned. This patch reduces the amount of superfluous fields, reducing the amount of data sent. Aligning the protocol closer to the WebDriver specification's expectations will also reduce the amount of post-processing required in the httpd. Previous to this patch, this is a value response: {"from":"0","value":null,"status":0,"sessionId":"{6b6d68d2-4ac9-4308-9f07-d2e72519c407}"} And this for ok responses: {"from":"0","ok":true} And this for errors: {"from":"0","status":21,"sessionId":"{6b6d68d2-4ac9-4308-9f07-d2e72519c407}","error":{"message":"Error loading page, timed out (onDOMContentLoaded)","stacktrace":null,"status":21}} This patch drops the `from` and `sessionId` fields, and the `status` field from non-error responses. It also drops the `ok` field in non-value responses and flattens the error response to a simple dictionary with the `error` (previously `status`), `message`, and `stacktrace` properties, which are now all required. r=jgriffin --HG-- extra : commitid : FbEkv70rxl9 extra : rebase_source : 3116110a0d197289cc95eba8748be0a33566c5a5
2015-05-21 13:26:58 +03:00
rv.push({
'name': cookie.name,
'value': cookie.value,
'path': cookie.path,
'domain': cookie.host,
'secure': cookie.isSecure,
'httpOnly': cookie.httpOnly,
'expiry': expires
});
}
Bug 1153822: Adjust Marionette responses to match WebDriver protocol Introduce protocol version levels in the Marionette server. On establishing a connection to a local end, the remote will return a `marionetteProtocol` field indicating which level it speaks. The protocol level can be used by local ends to either fall into compatibility mode or warn the user that the local end is incompatible with the remote. The protocol is currently also more expressive than it needs to be and this expressiveness has previously resulted in subtle inconsistencies in the fields returned. This patch reduces the amount of superfluous fields, reducing the amount of data sent. Aligning the protocol closer to the WebDriver specification's expectations will also reduce the amount of post-processing required in the httpd. Previous to this patch, this is a value response: {"from":"0","value":null,"status":0,"sessionId":"{6b6d68d2-4ac9-4308-9f07-d2e72519c407}"} And this for ok responses: {"from":"0","ok":true} And this for errors: {"from":"0","status":21,"sessionId":"{6b6d68d2-4ac9-4308-9f07-d2e72519c407}","error":{"message":"Error loading page, timed out (onDOMContentLoaded)","stacktrace":null,"status":21}} This patch drops the `from` and `sessionId` fields, and the `status` field from non-error responses. It also drops the `ok` field in non-value responses and flattens the error response to a simple dictionary with the `error` (previously `status`), `message`, and `stacktrace` properties, which are now all required. r=jgriffin --HG-- extra : commitid : FbEkv70rxl9 extra : rebase_source : 3116110a0d197289cc95eba8748be0a33566c5a5
2015-05-21 13:26:58 +03:00
return rv;
}
/**
* Delete a cookie by name.
*/
function deleteCookie(name) {
cookies.delete(name);
}
/**
* Delete all the visibile cookies on a page.
*/
function deleteAllCookies() {
for (let cookie of cookies) {
cookies.delete(cookie);
}
}
function getAppCacheStatus(msg) {
sendResponse(
curContainer.frame.applicationCache.status, msg.json.command_id);
}
/**
* Perform a screen capture in content context.
*
* Accepted values for |opts|:
*
* @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
* considered if {@code id} is not defined. Defaults to true.
* @param {Array.<UUID>=} highlights
* Draw a border around the elements found by their web element
* references.
* @param {boolean=} scroll
* When |id| is given, scroll it into view before taking the
* screenshot. Defaults to true.
*
* @param {capture.Format} format
* Format to return the screenshot in.
* @param {Object.<string, ?>} opts
* Options.
*
* @return {string}
* Base64 encoded string or a SHA-256 hash of the screenshot.
*/
function takeScreenshot(format, opts = {}) {
let id = opts.id;
let full = !!opts.full;
let highlights = opts.highlights || [];
let scroll = !!opts.scroll;
let highlightEls = highlights.map(ref => seenEls.get(ref, curContainer));
let canvas;
// viewport
if (!id && !full) {
canvas = capture.viewport(curContainer.frame, highlightEls);
// element or full document element
} else {
let el;
if (id) {
el = seenEls.get(id, curContainer);
if (scroll) {
element.scrollIntoView(el);
}
} else {
el = curContainer.frame.document.documentElement;
}
canvas = capture.element(el, highlightEls);
}
switch (format) {
case capture.Format.Base64:
return capture.toBase64(canvas);
case capture.Format.Hash:
return capture.toHash(canvas);
default:
throw new TypeError("Unknown screenshot format: " + format);
}
}
// Call register self when we get loaded
registerSelf();