提交 1dfc060a 编写于 作者: W Wes Kocher

Merge beta to release to keep things in sync a=merge

......@@ -1004,8 +1004,6 @@ class Watchdog
, mHibernating(false)
, mInitialized(false)
, mShuttingDown(false)
, mSlowScriptSecondHalf(false)
, mSlowScriptHalfLastElapsedTime(0)
, mMinScriptRunTimeSeconds(1)
{}
~Watchdog() { MOZ_ASSERT(!Initialized()); }
......@@ -1115,28 +1113,6 @@ class Watchdog
return mMinScriptRunTimeSeconds;
}
bool IsSlowScriptSecondHalf()
{
return mSlowScriptSecondHalf;
}
void FlipSlowScriptSecondHalf()
{
mSlowScriptSecondHalf = !mSlowScriptSecondHalf;
}
PRTime GetSlowScriptHalfLastElapsedTime()
{
return mSlowScriptHalfLastElapsedTime;
}
void SetSlowScriptHalfLastElapsedTime(PRTime t)
{
mSlowScriptHalfLastElapsedTime = t;
}
void ResetSlowScript()
{
mSlowScriptSecondHalf = false;
mSlowScriptHalfLastElapsedTime = 0;
}
private:
WatchdogManager* mManager;
......@@ -1146,11 +1122,6 @@ class Watchdog
bool mHibernating;
bool mInitialized;
bool mShuttingDown;
// See the comment in WatchdogMain.
bool mSlowScriptSecondHalf;
PRTime mSlowScriptHalfLastElapsedTime;
mozilla::Atomic<int32_t> mMinScriptRunTimeSeconds;
};
......@@ -1218,8 +1189,6 @@ class WatchdogManager : public nsIObserver
// Write state.
mTimestamps[TimestampRuntimeStateChange] = PR_Now();
if (mWatchdog)
mWatchdog->ResetSlowScript();
mRuntimeState = active ? RUNTIME_ACTIVE : RUNTIME_INACTIVE;
// The watchdog may be hibernating, waiting for the runtime to go
......@@ -1349,50 +1318,26 @@ WatchdogMain(void* arg)
// been running long enough that we might show the slow script dialog.
// Triggering the callback from off the main thread can be expensive.
// If we spend too much time running JS code in an event handler, then
// we want to show the slow script UI. The timeout T is controlled by
// prefs. We want to avoid showing the slow script dialog if the
// user's laptop goes to sleep in the middle of running a script. To
// ensure this, we invoke the interrupt callback only after the T/2
// elapsed twice. If the computer is put to sleep during one of the
// T/2 periods, the script still has the other T/2 seconds to finish.
//
// + <-- TimestampRuntimeStateChange = PR_Now()
// |
// | t0 >= T/2
// |
// + <-- mSlowScriptSecondHalf == false
// |
// | t1 >= T/2
// |
// + <-- mSlowScriptSecondHalf == true
// | Invoke interrupt callback
// |
// | t2 >= T/2
// |
// + <-- mSlowScriptSecondHalf == false
// |
// | t3 >= T/2
// |
// + <-- mSlowScriptSecondHalf == true
// Invoke interrupt callback
//
PRTime usecs = self->MinScriptRunTimeSeconds() * PR_USEC_PER_SEC;
if (manager->IsRuntimeActive()) {
PRTime elapsedTime = manager->TimeSinceLastRuntimeStateChange();
PRTime lastElapsedTime = self->GetSlowScriptHalfLastElapsedTime();
if (elapsedTime >= lastElapsedTime + usecs / 2) {
self->SetSlowScriptHalfLastElapsedTime(elapsedTime);
if (self->IsSlowScriptSecondHalf()) {
bool debuggerAttached = false;
nsCOMPtr<nsIDebug2> dbg = do_GetService("@mozilla.org/xpcom/debug;1");
if (dbg)
dbg->GetIsDebuggerAttached(&debuggerAttached);
if (!debuggerAttached)
JS_RequestInterruptCallback(manager->Runtime()->Runtime());
}
self->FlipSlowScriptSecondHalf();
}
// We want to avoid showing the slow script dialog if the user's laptop
// goes to sleep in the middle of running a script. To ensure this, we
// invoke the interrupt callback after only half the timeout has
// elapsed. The callback simply records the fact that it was called in
// the mSlowScriptSecondHalf flag. Then we wait another (timeout/2)
// seconds and invoke the callback again. This time around it sees
// mSlowScriptSecondHalf is set and so it shows the slow script
// dialog. If the computer is put to sleep during one of the (timeout/2)
// periods, the script still has the other (timeout/2) seconds to
// finish.
PRTime usecs = self->MinScriptRunTimeSeconds() * PR_USEC_PER_SEC / 2;
if (manager->IsRuntimeActive() &&
manager->TimeSinceLastRuntimeStateChange() >= usecs)
{
bool debuggerAttached = false;
nsCOMPtr<nsIDebug2> dbg = do_GetService("@mozilla.org/xpcom/debug;1");
if (dbg)
dbg->GetIsDebuggerAttached(&debuggerAttached);
if (!debuggerAttached)
JS_RequestInterruptCallback(manager->Runtime()->Runtime());
}
}
......@@ -1435,6 +1380,7 @@ XPCJSRuntime::InterruptCallback(JSContext* cx)
// care of that case.
if (self->mSlowScriptCheckpoint.IsNull()) {
self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
self->mSlowScriptSecondHalf = false;
self->mSlowScriptActualWait = mozilla::TimeDuration();
self->mTimeoutAccumulated = false;
return true;
......@@ -1455,11 +1401,20 @@ XPCJSRuntime::InterruptCallback(JSContext* cx)
int32_t limit = Preferences::GetInt(prefName, chrome ? 20 : 10);
// If there's no limit, or we're within the limit, let it go.
if (limit == 0 || duration.ToSeconds() < limit)
if (limit == 0 || duration.ToSeconds() < limit / 2.0)
return true;
self->mSlowScriptActualWait += duration;
// In order to guard against time changes or laptops going to sleep, we
// don't trigger the slow script warning until (limit/2) seconds have
// elapsed twice.
if (!self->mSlowScriptSecondHalf) {
self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
self->mSlowScriptSecondHalf = true;
return true;
}
//
// This has gone on long enough! Time to take action. ;-)
//
......@@ -3440,6 +3395,7 @@ XPCJSRuntime::XPCJSRuntime()
mObjectHolderRoots(nullptr),
mWatchdogManager(new WatchdogManager(this)),
mAsyncSnowWhiteFreer(new AsyncFreeSnowWhite()),
mSlowScriptSecondHalf(false),
mTimeoutAccumulated(false)
{
}
......@@ -3778,6 +3734,7 @@ XPCJSRuntime::BeforeProcessTask(bool aMightBlock)
// Start the slow script timer.
mSlowScriptCheckpoint = mozilla::TimeStamp::NowLoRes();
mSlowScriptSecondHalf = false;
mSlowScriptActualWait = mozilla::TimeDuration();
mTimeoutAccumulated = false;
......@@ -3797,6 +3754,7 @@ XPCJSRuntime::AfterProcessTask(uint32_t aNewRecursionDepth)
{
// Now that we're back to the event loop, reset the slow script checkpoint.
mSlowScriptCheckpoint = mozilla::TimeStamp();
mSlowScriptSecondHalf = false;
// Call cycle collector occasionally.
MOZ_ASSERT(NS_IsMainThread());
......
......@@ -640,9 +640,23 @@ private:
JS::PersistentRootedObject mCompilationScope;
RefPtr<AsyncFreeSnowWhite> mAsyncSnowWhiteFreer;
// mSlowScriptCheckpoint is set to the time when we started processing the
// current event. We use it to determine whether the interrupt callback
// needs to do anything.
// If we spend too much time running JS code in an event handler, then we
// want to show the slow script UI. The timeout T is controlled by prefs. We
// invoke the interrupt callback once after T/2 seconds and set
// mSlowScriptSecondHalf to true. After another T/2 seconds, we invoke the
// interrupt callback again. Since mSlowScriptSecondHalf is now true, it
// shows the slow script UI. The reason we invoke the callback twice is to
// ensure that putting the computer to sleep while running a script doesn't
// cause the UI to be shown. If the laptop goes to sleep during one of the
// timeout periods, the script still has the other T/2 seconds to complete
// before the slow script UI is shown.
bool mSlowScriptSecondHalf;
// mSlowScriptCheckpoint is set to the time when:
// 1. We started processing the current event, or
// 2. mSlowScriptSecondHalf was set to true
// (whichever comes later). We use it to determine whether the interrupt
// callback needs to do anything.
mozilla::TimeStamp mSlowScriptCheckpoint;
// Accumulates total time we actually waited for telemetry
mozilla::TimeDuration mSlowScriptActualWait;
......
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/AddonManager.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("chrome://marionette/content/error.js");
this.EXPORTED_SYMBOLS = ["addon"];
this.addon = {};
/**
* Installs Firefox addon.
*
* If the addon is restartless, it can be used right away. Otherwise a
* restart is needed.
*
* Temporary addons will automatically be unisntalled on shutdown and
* do not need to be signed, though they must be restartless.
*
* @param {string} path
* Full path to the extension package archive to be installed.
* @param {boolean=} temporary
* Install the add-on temporarily if true.
*
* @return {Promise.<string>}
* Addon ID string of the newly installed addon.
*
* @throws {AddonError}
* if installation fails
*/
addon.install = function(path, temporary = false) {
return new Promise((resolve, reject) => {
let listener = {
onInstallEnded: function(install, addon) {
resolve(addon.id);
},
onInstallFailed: function(install) {
reject(new AddonError(install.error));
},
onInstalled: function(addon) {
AddonManager.removeAddonListener(listener);
resolve(addon.id);
}
};
let file = new FileUtils.File(path);
// temporary addons
if (temp) {
AddonManager.addAddonListener(listener);
AddonManager.installTemporaryAddon(file);
}
// addons that require restart
else {
AddonManager.getInstallForFile(file, function(aInstall) {
if (aInstall.error != 0) {
reject(new AddonError(aInstall.error));
}
aInstall.addListener(listener);
aInstall.install();
});
}
});
};
/**
* Uninstall a Firefox addon.
*
* If the addon is restartless, it will be uninstalled right
* away. Otherwise a restart is necessary.
*
* @param {string} id
* Addon ID to uninstall.
*
* @return {Promise}
*/
addon.uninstall = function(id) {
return new Promise(resolve => {
AddonManager.getAddonByID(arguments[0], function(addon) {
addon.uninstall();
});
});
};
......@@ -768,10 +768,10 @@ class Marionette(object):
'type': arguments[0]
};
return value;"""
with self.using_context('content'):
value = self.execute_script(script, script_args=[perm], sandbox='system')
with self.using_context("content"):
value = self.execute_script(script, script_args=(perm,), sandbox="system")
with self.using_context('chrome'):
with self.using_context("chrome"):
permission = self.execute_script("""
Components.utils.import("resource://gre/modules/Services.jsm");
let perm = arguments[0];
......@@ -784,7 +784,7 @@ class Marionette(object):
let testPerm = Services.perms.testPermissionFromPrincipal(
principal, perm.type);
return testPerm;
""", script_args=[value])
""", script_args=(value,))
return permission
def push_permission(self, perm, allow):
......@@ -824,20 +824,20 @@ class Marionette(object):
};
return value;
"""
with self.using_context('content'):
perm = self.execute_script(script, script_args=[allow, perm], sandbox='system')
with self.using_context("content"):
perm = self.execute_script(script, script_args=(allow, perm,), sandbox="system")
current_perm = self.get_permission(perm['type'])
if current_perm == perm['action']:
with self.using_context('content'):
current_perm = self.get_permission(perm["type"])
if current_perm == perm["action"]:
with self.using_context("content"):
self.execute_script("""
Components.utils.import("resource://gre/modules/Services.jsm");
Services.obs.removeObserver(window.wrappedJSObject.permObserver,
"perm-changed");
""", sandbox='system')
""", sandbox="system")
return
with self.using_context('chrome'):
with self.using_context("chrome"):
self.execute_script("""
Components.utils.import("resource://gre/modules/Services.jsm");
let perm = arguments[0];
......@@ -849,7 +849,7 @@ class Marionette(object):
attrs);
Services.perms.addFromPrincipal(principal, perm.type, perm.action);
return true;
""", script_args=[perm])
""", script_args=(perm,))
with self.using_context("content"):
self.execute_async_script("""
......@@ -902,7 +902,7 @@ class Marionette(object):
pref_value = self.execute_script("""
Components.utils.import("resource://gre/modules/Preferences.jsm");
return Preferences.get(arguments[0], null);
""", script_args=[pref], sandbox="system")
""", script_args=(pref,), sandbox="system")
return pref_value
def clear_pref(self, pref):
......@@ -910,7 +910,7 @@ class Marionette(object):
self.execute_script("""
Components.utils.import("resource://gre/modules/Preferences.jsm");
Preferences.reset(arguments[0]);
""", script_args=[pref])
""", script_args=(pref,))
def set_pref(self, pref, value):
with self.using_context(self.CONTEXT_CHROME):
......@@ -921,29 +921,29 @@ class Marionette(object):
self.execute_script("""
Components.utils.import("resource://gre/modules/Preferences.jsm");
Preferences.set(arguments[0], arguments[1]);
""", script_args=[pref, value])
""", script_args=(pref, value,))
def set_prefs(self, prefs):
'''Sets preferences.
"""Sets preferences.
If the value of the preference to be set is None, reset the preference
to its default value. If no default value exists, the preference will
cease to exist.
:param prefs: A dict containing one or more preferences and their values
to be set.
:param prefs: A dict containing one or more preferences and
their values to be set.
Usage example::
marionette.set_prefs({'browser.tabs.warnOnClose': True})
marionette.set_prefs({"browser.tabs.warnOnClose": True})
'''
"""
for pref, value in prefs.items():
self.set_pref(pref, value)
@contextmanager
def using_prefs(self, prefs):
'''Sets preferences for code being executed in a `with` block,
"""Sets preferences for code being executed in a `with` block,
and restores them on exit.
:param prefs: A dict containing one or more preferences and their values
......@@ -951,10 +951,10 @@ class Marionette(object):
Usage example::
with marionette.using_prefs({'browser.tabs.warnOnClose': True}):
# ... do stuff ...
with marionette.using_prefs({"browser.tabs.warnOnClose": True}):
# ... do stuff ...
'''
"""
original_prefs = {p: self.get_pref(p) for p in prefs}
self.set_prefs(prefs)
......@@ -964,9 +964,9 @@ class Marionette(object):
self.set_prefs(original_prefs)
def enforce_gecko_prefs(self, prefs):
"""
Checks if the running instance has the given prefs. If not, it will kill the
currently running instance, and spawn a new instance with the requested preferences.
"""Checks if the running instance has the given prefs. If not,
it will kill the currently running instance, and spawn a new
instance with the requested preferences.
: param prefs: A dictionary whose keys are preference names.
"""
......@@ -1452,15 +1452,15 @@ class Marionette(object):
"""Causes the browser to perform to refresh the current page."""
self._send_message("refresh")
def wrapArguments(self, args):
if isinstance(args, list):
def _to_json(self, args):
if isinstance(args, list) or isinstance(args, tuple):
wrapped = []
for arg in args:
wrapped.append(self.wrapArguments(arg))
wrapped.append(self._to_json(arg))
elif isinstance(args, dict):
wrapped = {}
for arg in args:
wrapped[arg] = self.wrapArguments(args[arg])
wrapped[arg] = self._to_json(args[arg])
elif type(args) == HTMLElement:
wrapped = {W3C_WEBELEMENT_KEY: args.id,
WEBELEMENT_KEY: args.id}
......@@ -1469,11 +1469,11 @@ class Marionette(object):
wrapped = args
return wrapped
def unwrapValue(self, value):
def _from_json(self, value):
if isinstance(value, list):
unwrapped = []
for item in value:
unwrapped.append(self.unwrapValue(item))
unwrapped.append(self._from_json(item))
elif isinstance(value, dict):
unwrapped = {}
for key in value:
......@@ -1484,18 +1484,16 @@ class Marionette(object):
unwrapped = HTMLElement(self, value[key])
break
else:
unwrapped[key] = self.unwrapValue(value[key])
unwrapped[key] = self._from_json(value[key])
else:
unwrapped = value
return unwrapped
def execute_js_script(self, script, script_args=None, async=True,
def execute_js_script(self, script, script_args=(), async=True,
new_sandbox=True, script_timeout=None,
inactivity_timeout=None, filename=None,
sandbox='default'):
if script_args is None:
script_args = []
args = self.wrapArguments(script_args)
args = self._to_json(script_args)
body = {"script": script,
"args": args,
"async": async,
......@@ -1505,9 +1503,9 @@ class Marionette(object):
"filename": filename,
"line": None}
rv = self._send_message("executeJSScript", body, key="value")
return self.unwrapValue(rv)
return self._from_json(rv)
def execute_script(self, script, script_args=None, new_sandbox=True,
def execute_script(self, script, script_args=(), new_sandbox=True,
sandbox="default", script_timeout=None):
"""Executes a synchronous JavaScript script, and returns the
result (or None if the script does return a value).
......@@ -1517,7 +1515,7 @@ class Marionette(object):
has not been called.
:param script: A string containing the JavaScript to execute.
:param script_args: A list of arguments to pass to the script.
:param script_args: An interable of arguments to pass to the script.
:param sandbox: A tag referring to the sandbox you wish to use;
if you specify a new tag, a new sandbox will be created.
If you use the special tag `system`, the sandbox will
......@@ -1540,10 +1538,10 @@ class Marionette(object):
::
result = marionette.execute_script("return arguments[0] + arguments[1];",
script_args=[2, 3])
script_args=(2, 3,))
assert result == 5
some_element = marionette.find_element(By.ID, "someElement")
sid = marionette.execute_script("return arguments[0].id;", script_args=[some_element])
sid = marionette.execute_script("return arguments[0].id;", script_args=(some_element,))
assert some_element.get_attribute("id") == sid
Scripts wishing to access non-standard properties of the window
......@@ -1570,9 +1568,7 @@ class Marionette(object):
assert result == "foo"
"""
if script_args is None:
script_args = []
args = self.wrapArguments(script_args)
args = self._to_json(script_args)
stack = traceback.extract_stack()
frame = stack[-2:-1][0] # grab the second-to-last frame
body = {"script": script,
......@@ -1583,9 +1579,9 @@ class Marionette(object):
"line": int(frame[1]),
"filename": os.path.basename(frame[0])}
rv = self._send_message("executeScript", body, key="value")
return self.unwrapValue(rv)
return self._from_json(rv)
def execute_async_script(self, script, script_args=None, new_sandbox=True,
def execute_async_script(self, script, script_args=(), new_sandbox=True,
sandbox="default", script_timeout=None,
debug_script=False):
"""Executes an asynchronous JavaScript script, and returns the
......@@ -1596,7 +1592,7 @@ class Marionette(object):
set_context() has not been called.
:param script: A string containing the JavaScript to execute.
:param script_args: A list of arguments to pass to the script.
:param script_args: An interable of arguments to pass to the script.
:param sandbox: A tag referring to the sandbox you wish to use; if
you specify a new tag, a new sandbox will be created. If you
use the special tag `system`, the sandbox will be created
......@@ -1620,9 +1616,7 @@ class Marionette(object):
''')
assert result == 1
"""
if script_args is None:
script_args = []
args = self.wrapArguments(script_args)
args = self._to_json(script_args)
stack = traceback.extract_stack()
frame = stack[-2:-1][0] # grab the second-to-last frame
body = {"script": script,
......@@ -1634,7 +1628,7 @@ class Marionette(object):
"filename": os.path.basename(frame[0]),
"debug_script": debug_script}
rv = self._send_message("executeAsyncScript", body, key="value")
return self.unwrapValue(rv)
return self._from_json(rv)
def find_element(self, method, target, id=None):
"""Returns an HTMLElement instances that matches the specified
......
......@@ -2014,36 +2014,7 @@ GeckoDriver.prototype.sendKeysToElement = function*(cmd, resp) {
break;
case Context.CONTENT:
let err;
let listener = function(msg) {
this.mm.removeMessageListener("Marionette:setElementValue", listener);
let val = msg.data.value;
let el = msg.objects.element;
let win = this.getCurrentWindow();
if (el.type == "file") {
Cu.importGlobalProperties(["File"]);
let fs = Array.prototype.slice.call(el.files);
let file;
try {
file = new File(val);
} catch (e) {
err = new InvalidArgumentError(`File not found: ${val}`);
}
fs.push(file);
el.mozSetFileArray(fs);
} else {
el.value = val;
}
}.bind(this);
this.mm.addMessageListener("Marionette:setElementValue", listener);
yield this.listener.sendKeysToElement({id: id, value: value});
this.mm.removeMessageListener("Marionette:setElementValue", listener);
if (err) {
throw err;
}
break;
}
};
......
......@@ -102,6 +102,7 @@ evaluate.sandbox = function(sb, script, args = [], opts = {}) {
let timeoutId, timeoutHandler, unloadHandler;
let promise = new Promise((resolve, reject) => {
let src = "";
sb[COMPLETE] = resolve;
timeoutHandler = () => reject(new ScriptTimeoutError("Timed out"));
unloadHandler = () => reject(
......@@ -109,11 +110,20 @@ evaluate.sandbox = function(sb, script, args = [], opts = {}) {
// wrap in function
if (!opts.directInject) {
sb[CALLBACK] = sb[COMPLETE];
if (opts.async) {
sb[CALLBACK] = sb[COMPLETE];
}
sb[ARGUMENTS] = Cu.cloneInto(args, sb, {wrapReflectors: true});
script = `${ARGUMENTS}.push(${CALLBACK});` +
`(function() { ${script} }).apply(null, ${ARGUMENTS})`;
// callback function made private
// so that introspection is possible
// on the arguments object
if (opts.async) {
sb[CALLBACK] = sb[COMPLETE];
src += `${ARGUMENTS}.push(rv => ${CALLBACK}(rv));`;
}
src += `(function() { ${script} }).apply(null, ${ARGUMENTS})`;
// marionetteScriptFinished is not WebDriver conformant,
// hence it is only exposed to immutable sandboxes
......@@ -141,7 +151,7 @@ evaluate.sandbox = function(sb, script, args = [], opts = {}) {
let res;
try {
res = Cu.evalInSandbox(
script, sb, "1.8", opts.filename || "dummy file", 0);
src, sb, "1.8", opts.filename || "dummy file", 0);
} catch (e) {
let err = new JavaScriptError(
e,
......
......@@ -1306,29 +1306,29 @@ event.focus = function(el, opts = {}) {
};
event.mouseover = function(el, modifiers = {}, opts = {}) {
return event.sendEvent({type: "mouseover"}, el, modifiers, opts);
return event.sendEvent("mouseover", el, modifiers, opts);
};
event.mousemove = function(el, modifiers = {}, opts = {}) {
return event.sendEvent({type: "mousemove"}, el, modifiers, opts);
return event.sendEvent("mousemove", el, modifiers, opts);
};
event.mousedown = function(el, modifiers = {}, opts = {}) {
return event.sendEvent({type: "mousedown"}, el, modifiers, opts);
return event.sendEvent("mousedown", el, modifiers, opts);
};
event.mouseup = function(el, modifiers = {}, opts = {}) {
return event.sendEvent({type: "mouseup"}, el, modifiers, opts);
return event.sendEvent("mouseup", el, modifiers, opts);
};
event.click = function(el, modifiers = {}, opts = {}) {
return event.sendEvent({type: "click"}, el, modifiers, opts);
return event.sendEvent("click", el, modifiers, opts);
};
event.change = function(el, modifiers = {}, opts = {}) {
return event.sendEvent({type: "change"}, el, modifiers, opts);
return event.sendEvent("change", el, modifiers, opts);
};
event.input = function(el, modifiers = {}, opts = {}) {
return event.sendEvent({type: "input"}, el, modifiers, opts);
return event.sendEvent("input", el, modifiers, opts);
};
# 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/.
from marionette import MarionetteTestCase
from marionette_driver.errors import UnknownException
class TestCertificates(MarionetteTestCase):
def test_block_insecure_sites(self):
self.marionette.delete_session()
self.marionette.start_session()
self.marionette.navigate(self.fixtures.where_is("test.html", on="http"))
self.assertIn("http://", self.marionette.get_url())
with self.assertRaises(UnknownException):
self.marionette.navigate(self.fixtures.where_is("test.html", on="https"))
def test_accept_all_insecure(self):
self.marionette.delete_session()
self.marionette.start_session({"desiredCapability": {"acceptSslCerts": ["*"]}})
self.marionette.navigate(self.fixtures.where_is("test.html", on="https"))
self.assertIn("https://", self.marionette.url)
"""
def test_accept_some_insecure(self):
self.marionette.delete_session()
self.marionette.start_session({"requiredCapabilities": {"acceptSslCerts": ["127.0.0.1"]}})
self.marionette.navigate(self.fixtures.where_is("test.html", on="https"))
self.assertIn("https://", self.marionette.url)
"""
\ No newline at end of file
......@@ -111,6 +111,19 @@ marionetteScriptFinished(4);
"marionetteScriptFinished(global.barfoo);", new_sandbox=False),
[42, 23])
# Functions defined in higher privilege scopes, such as the privileged
# content frame script listener.js runs in, cannot be accessed from
# content. This tests that it is possible to introspect the objects on
# `arguments` without getting permission defined errors. This is made
# possible because the last argument is always the callback/complete
# function.
#
# See bug 1290966.
def test_introspection_of_arguments(self):
self.marionette.execute_async_script(
"arguments[0].cheese; __webDriverCallback();",
script_args=[], sandbox=None)
class TestExecuteAsyncChrome(TestExecuteAsyncContent):
def setUp(self):
......
......@@ -232,6 +232,10 @@ class TestExecuteContent(MarionetteTestCase):
self.assertTrue(
send("return typeof window.wrappedJSObject == 'undefined'"))
def test_no_callback(self):
self.assertTrue(self.marionette.execute_script(
"return typeof arguments[0] == 'undefined'"))
class TestExecuteChrome(TestExecuteContent):
def setUp(self):
......
......@@ -106,6 +106,22 @@ class TestFileUpload(MarionetteTestCase):
Wait(self.marionette).until(lambda m: m.get_url() != url)
self.assertIn("multipart/form-data", self.body.text)
def test_change_event(self):
self.marionette.navigate(single)
self.marionette.execute_script("""
window.changeEvs = [];
let el = arguments[arguments.length - 1];
el.addEventListener("change", ev => window.changeEvs.push(ev));
console.log(window.changeEvs.length);
""", script_args=(self.input,), sandbox=None)
with tempfile() as f:
self.input.send_keys(f.name)
nevs = self.marionette.execute_script(
"return window.changeEvs.length", sandbox=None)
self.assertEqual(1, nevs)
def find_inputs(self):
return self.marionette.find_elements(By.TAG_NAME, "input")
......
......@@ -221,6 +221,33 @@ interaction.selectOption = function(el) {
event.click(parent);
};
/**
* Appends |path| to an <input type=file>'s file list.
*
* @param {HTMLInputElement} el
* An <input type=file> element.
* @param {File} file
* File object to assign to |el|.
*/
interaction.uploadFile = function(el, file) {
let fs = Array.prototype.slice.call(el.files);
fs.push(file);
// <input type=file> opens OS widget dialogue
// which means the mousedown/focus/mouseup/click events
// occur before the change event
event.mouseover(el);
event.mousemove(el);
event.mousedown(el);
event.focus(el);
event.mouseup(el);
event.click(el);
el.mozSetFileArray(fs);
event.change(el);
};
/**
* Locate the <select> element that encapsulate an <option> element.
*
......
......@@ -592,20 +592,21 @@ function setTestName(msg) {
* sendKeysToElement action on a file input element.
*/
function receiveFiles(msg) {
if ('error' in msg.json) {
if ("error" in msg.json) {
let err = new InvalidArgumentError(msg.json.error);
sendError(err, msg.json.command_id);
return;
}
if (!fileInputElement) {
let err = new InvalidElementStateError("receiveFiles called with no valid fileInputElement");
sendError(err, msg.json.command_id);
return;
}
let fs = Array.prototype.slice.call(fileInputElement.files);
fs.push(msg.json.file);
fileInputElement.mozSetFileArray(fs);
interaction.uploadFile(fileInputElement, msg.json.file);
fileInputElement = null;
sendOk(msg.json.command_id);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册