Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openthos
gecko-dev
提交
1dfc060a
G
gecko-dev
项目概览
openthos
/
gecko-dev
通知
0
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
G
gecko-dev
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
1dfc060a
编写于
9月 12, 2016
作者:
W
Wes Kocher
浏览文件
操作
浏览文件
下载
差异文件
Merge beta to release to keep things in sync a=merge
上级
b42b33fe
1559315e
变更
13
隐藏空白更改
内联
并排
Showing
13 changed file
with
308 addition
and
176 deletion
+308
-176
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/XPCJSRuntime.cpp
+34
-76
js/xpconnect/src/xpcprivate.h
js/xpconnect/src/xpcprivate.h
+17
-3
testing/marionette/addon.js
testing/marionette/addon.js
+93
-0
testing/marionette/client/marionette_driver/marionette.py
testing/marionette/client/marionette_driver/marionette.py
+47
-53
testing/marionette/driver.js
testing/marionette/driver.js
+0
-29
testing/marionette/evaluate.js
testing/marionette/evaluate.js
+14
-4
testing/marionette/event.js
testing/marionette/event.js
+7
-7
testing/marionette/harness/marionette/tests/unit/test_certificates.py
...onette/harness/marionette/tests/unit/test_certificates.py
+31
-0
testing/marionette/harness/marionette/tests/unit/test_execute_async_script.py
...arness/marionette/tests/unit/test_execute_async_script.py
+13
-0
testing/marionette/harness/marionette/tests/unit/test_execute_script.py
...ette/harness/marionette/tests/unit/test_execute_script.py
+4
-0
testing/marionette/harness/marionette/tests/unit/test_file_upload.py
...ionette/harness/marionette/tests/unit/test_file_upload.py
+16
-0
testing/marionette/interaction.js
testing/marionette/interaction.js
+27
-0
testing/marionette/listener.js
testing/marionette/listener.js
+5
-4
未找到文件。
js/xpconnect/src/XPCJSRuntime.cpp
浏览文件 @
1dfc060a
...
...
@@ -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
());
...
...
js/xpconnect/src/xpcprivate.h
浏览文件 @
1dfc060a
...
...
@@ -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
;
...
...
testing/marionette/addon.js
0 → 100644
浏览文件 @
1dfc060a
/* 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
();
});
});
};
testing/marionette/client/marionette_driver/marionette.py
浏览文件 @
1dfc060a
...
...
@@ -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: A
n 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: A
n 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
...
...
testing/marionette/driver.js
浏览文件 @
1dfc060a
...
...
@@ -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
;
}
};
...
...
testing/marionette/evaluate.js
浏览文件 @
1dfc060a
...
...
@@ -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
(
s
cript
,
sb
,
"
1.8
"
,
opts
.
filename
||
"
dummy file
"
,
0
);
s
rc
,
sb
,
"
1.8
"
,
opts
.
filename
||
"
dummy file
"
,
0
);
}
catch
(
e
)
{
let
err
=
new
JavaScriptError
(
e
,
...
...
testing/marionette/event.js
浏览文件 @
1dfc060a
...
...
@@ -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
);
};
testing/marionette/harness/marionette/tests/unit/test_certificates.py
0 → 100644
浏览文件 @
1dfc060a
# 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
testing/marionette/harness/marionette/tests/unit/test_execute_async_script.py
浏览文件 @
1dfc060a
...
...
@@ -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
):
...
...
testing/marionette/harness/marionette/tests/unit/test_execute_script.py
浏览文件 @
1dfc060a
...
...
@@ -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
):
...
...
testing/marionette/harness/marionette/tests/unit/test_file_upload.py
浏览文件 @
1dfc060a
...
...
@@ -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"
)
...
...
testing/marionette/interaction.js
浏览文件 @
1dfc060a
...
...
@@ -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.
*
...
...
testing/marionette/listener.js
浏览文件 @
1dfc060a
...
...
@@ -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.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录