Bug 1092102 - Implement WorkerDebuggerGlobalScope.createSandbox;r=khuey

This commit is contained in:
Eddy Bruël 2015-03-31 10:19:04 +02:00
parent 066aa9747b
commit 0c47b481d2
13 changed files with 300 additions and 10 deletions

View File

@ -7,6 +7,11 @@
interface WorkerDebuggerGlobalScope : EventTarget {
readonly attribute object global;
object createSandbox(DOMString name, object prototype);
[Throws]
void loadSubScript(DOMString url, optional object sandbox);
void enterEventLoop();
void leaveEventLoop();

View File

@ -873,14 +873,14 @@ JSObject*
Wrap(JSContext *cx, JS::HandleObject existing, JS::HandleObject obj)
{
JSObject* targetGlobal = JS::CurrentGlobalOrNull(cx);
if (!IsDebuggerGlobal(targetGlobal)) {
if (!IsDebuggerGlobal(targetGlobal) && !IsDebuggerSandbox(targetGlobal)) {
MOZ_CRASH("There should be no edges from the debuggee to the debugger.");
}
JSObject* originGlobal = js::GetGlobalForObjectCrossCompartment(obj);
const js::Wrapper* wrapper = nullptr;
if (IsDebuggerGlobal(originGlobal)) {
if (IsDebuggerGlobal(originGlobal) || IsDebuggerSandbox(originGlobal)) {
wrapper = &js::CrossCompartmentWrapper::singleton;
} else {
if (obj != originGlobal) {

View File

@ -981,7 +981,8 @@ LoadMainScript(JSContext* aCx, const nsAString& aScriptURL,
void
Load(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
const Sequence<nsString>& aScriptURLs, ErrorResult& aRv)
const nsTArray<nsString>& aScriptURLs, WorkerScriptType aWorkerScriptType,
ErrorResult& aRv)
{
const uint32_t urlCount = aScriptURLs.Length();
@ -1001,7 +1002,7 @@ Load(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
loadInfos[index].mURL = aScriptURLs[index];
}
if (!LoadAllScripts(aCx, aWorkerPrivate, loadInfos, false, WorkerScript)) {
if (!LoadAllScripts(aCx, aWorkerPrivate, loadInfos, false, aWorkerScriptType)) {
// LoadAllScripts can fail if we're shutting down.
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
}

View File

@ -58,7 +58,8 @@ bool LoadMainScript(JSContext* aCx, const nsAString& aScriptURL,
void Load(JSContext* aCx,
WorkerPrivate* aWorkerPrivate,
const mozilla::dom::Sequence<nsString>& aScriptURLs,
const nsTArray<nsString>& aScriptURLs,
WorkerScriptType aWorkerScriptType,
mozilla::ErrorResult& aRv);
} // namespace scriptloader

View File

@ -1511,15 +1511,16 @@ public:
if (!globalScope) {
WorkerDebuggerGlobalScope* globalScope = nullptr;
UNWRAP_OBJECT(WorkerDebuggerGlobalScope, global, globalScope);
MOZ_ASSERT(globalScope);
MOZ_ASSERT(global == globalScope->GetWrapperPreserveColor());
MOZ_ASSERT_IF(globalScope, globalScope->GetWrapperPreserveColor() == global);
MOZ_ASSERT_IF(!globalScope, IsDebuggerSandbox(global));
aWorkerPrivate->ReportErrorToDebugger(aFilename, aLineNumber,
aMessage);
return true;
}
MOZ_ASSERT(global == globalScope->GetWrapperPreserveColor());
MOZ_ASSERT(globalScope->GetWrapperPreserveColor() == global);
nsIDOMEventTarget* target = static_cast<nsIDOMEventTarget*>(globalScope);
nsRefPtr<ErrorEvent> event =

View File

@ -208,7 +208,7 @@ WorkerGlobalScope::ImportScripts(JSContext* aCx,
ErrorResult& aRv)
{
mWorkerPrivate->AssertIsOnWorkerThread();
scriptloader::Load(aCx, mWorkerPrivate, aScriptURLs, aRv);
scriptloader::Load(aCx, mWorkerPrivate, aScriptURLs, WorkerScript, aRv);
}
int32_t
@ -513,6 +513,184 @@ WorkerDebuggerGlobalScope::GetGlobal(JSContext* aCx,
aGlobal.set(mWorkerPrivate->GetOrCreateGlobalScope(aCx)->GetWrapper());
}
class WorkerDebuggerSandboxPrivate : public nsIGlobalObject,
public nsWrapperCache
{
public:
WorkerDebuggerSandboxPrivate(JSObject *global)
{
SetWrapper(global);
}
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(WorkerDebuggerSandboxPrivate,
nsIGlobalObject)
JSObject *GetGlobalJSObject()
{
return GetWrapper();
}
virtual JSObject* WrapObject(JSContext* cx,
JS::Handle<JSObject*> aGivenProto) override
{
MOZ_CRASH("WorkerDebuggerSandboxPrivate doesn't use DOM bindings!");
}
private:
virtual ~WorkerDebuggerSandboxPrivate()
{
ClearWrapper();
}
};
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WorkerDebuggerSandboxPrivate)
NS_IMPL_CYCLE_COLLECTING_ADDREF(WorkerDebuggerSandboxPrivate)
NS_IMPL_CYCLE_COLLECTING_RELEASE(WorkerDebuggerSandboxPrivate)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerDebuggerSandboxPrivate)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
NS_INTERFACE_MAP_END
static bool
workerdebuggersandbox_enumerate(JSContext *cx, JS::Handle<JSObject *> obj)
{
return JS_EnumerateStandardClasses(cx, obj);
}
static bool
workerdebuggersandbox_resolve(JSContext *cx, JS::Handle<JSObject *> obj,
JS::Handle<jsid> id, bool *resolvedp)
{
return JS_ResolveStandardClass(cx, obj, id, resolvedp);
}
static bool
workerdebuggersandbox_convert(JSContext *cx, JS::Handle<JSObject *> obj,
JSType type, JS::MutableHandle<JS::Value> vp)
{
if (type == JSTYPE_OBJECT) {
vp.set(OBJECT_TO_JSVAL(obj));
return true;
}
return JS::OrdinaryToPrimitive(cx, obj, type, vp);
}
static void
workerdebuggersandbox_finalize(js::FreeOp *fop, JSObject *obj)
{
nsIGlobalObject *globalObject =
static_cast<nsIGlobalObject *>(JS_GetPrivate(obj));
NS_RELEASE(globalObject);
}
static void
workerdebuggersandbox_moved(JSObject *obj, const JSObject *old)
{
}
const js::Class workerdebuggersandbox_class = {
"workerdebuggersandbox",
JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS,
nullptr,
nullptr,
nullptr,
nullptr,
workerdebuggersandbox_enumerate,
workerdebuggersandbox_resolve,
workerdebuggersandbox_convert,
workerdebuggersandbox_finalize,
nullptr,
nullptr,
nullptr,
JS_GlobalObjectTraceHook,
JS_NULL_CLASS_SPEC, {
nullptr,
nullptr,
false,
nullptr,
workerdebuggersandbox_moved
}, JS_NULL_OBJECT_OPS
};
void
WorkerDebuggerGlobalScope::CreateSandbox(JSContext* aCx, const nsAString& aName,
JS::Handle<JSObject*> aPrototype,
JS::MutableHandle<JSObject*> aResult)
{
mWorkerPrivate->AssertIsOnWorkerThread();
JS::CompartmentOptions options;
options.setInvisibleToDebugger(true);
JS::Rooted<JSObject*> sandbox(aCx,
JS_NewGlobalObject(aCx, js::Jsvalify(&workerdebuggersandbox_class), nullptr,
JS::DontFireOnNewGlobalHook, options));
if (!sandbox) {
JS_ReportError(aCx, "Can't create sandbox!");
aResult.set(nullptr);
return;
}
{
JSAutoCompartment ac(aCx, sandbox);
JS::Rooted<JSObject*> prototype(aCx, aPrototype);
if (!JS_WrapObject(aCx, &prototype)) {
JS_ReportError(aCx, "Can't wrap sandbox prototype!");
aResult.set(nullptr);
return;
}
if (!JS_SetPrototype(aCx, sandbox, prototype)) {
JS_ReportError(aCx, "Can't set sandbox prototype!");
aResult.set(nullptr);
return;
}
nsCOMPtr<nsIGlobalObject> globalObject =
new WorkerDebuggerSandboxPrivate(sandbox);
// Pass on ownership of globalObject to |sandbox|.
JS_SetPrivate(sandbox, globalObject.forget().take());
}
JS_FireOnNewGlobalObject(aCx, sandbox);
if (!JS_WrapObject(aCx, &sandbox)) {
JS_ReportError(aCx, "Can't wrap sandbox!");
aResult.set(nullptr);
return;
}
aResult.set(sandbox);
}
void
WorkerDebuggerGlobalScope::LoadSubScript(JSContext* aCx,
const nsAString& aURL,
const Optional<JS::Handle<JSObject*>>& aSandbox,
ErrorResult& aRv)
{
mWorkerPrivate->AssertIsOnWorkerThread();
Maybe<JSAutoCompartment> ac;
if (aSandbox.WasPassed()) {
JS::Rooted<JSObject*> sandbox(aCx, js::CheckedUnwrap(aSandbox.Value()));
if (!IsDebuggerSandbox(sandbox)) {
aRv.Throw(NS_ERROR_INVALID_ARG);
return;
}
ac.emplace(aCx, sandbox);
}
nsTArray<nsString> urls;
urls.AppendElement(aURL);
scriptloader::Load(aCx, mWorkerPrivate, urls, DebuggerScript, aRv);
}
void
WorkerDebuggerGlobalScope::EnterEventLoop()
{
@ -561,10 +739,18 @@ GetGlobalObjectForGlobal(JSObject* global)
{
nsIGlobalObject* globalObject = nullptr;
UNWRAP_WORKER_OBJECT(WorkerGlobalScope, global, globalObject);
if (!globalObject) {
UNWRAP_OBJECT(WorkerDebuggerGlobalScope, global, globalObject);
MOZ_ASSERT(globalObject);
if (!globalObject) {
MOZ_ASSERT(IsDebuggerSandbox(global));
globalObject = static_cast<nsIGlobalObject *>(JS_GetPrivate(global));
MOZ_ASSERT(globalObject);
}
}
return globalObject;
}
@ -584,6 +770,12 @@ IsDebuggerGlobal(JSObject* object)
globalObject)) && !!globalObject;
}
bool
IsDebuggerSandbox(JSObject* object)
{
return js::GetObjectClass(object) == &workerdebuggersandbox_class;
}
bool
GetterOnlyJSNative(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
{

View File

@ -264,6 +264,16 @@ public:
void
GetGlobal(JSContext* aCx, JS::MutableHandle<JSObject*> aGlobal);
void
CreateSandbox(JSContext* aCx, const nsAString& aName,
JS::Handle<JSObject*> aPrototype,
JS::MutableHandle<JSObject*> aResult);
void
LoadSubScript(JSContext* aCx, const nsAString& aURL,
const Optional<JS::Handle<JSObject*>>& aSandbox,
ErrorResult& aRv);
void
EnterEventLoop();

View File

@ -339,6 +339,9 @@ IsWorkerGlobal(JSObject* global);
bool
IsDebuggerGlobal(JSObject* global);
bool
IsDebuggerSandbox(JSObject* object);
// Throws the JSMSG_GETTER_ONLY exception. This shouldn't be used going
// forward -- getter-only properties should just use JS_PSG for the setter
// (implying no setter at all), which will not throw when set in non-strict

View File

@ -0,0 +1,9 @@
"use strict";
const SANDBOX_URL = "WorkerDebuggerGlobalScope.createSandbox_sandbox.js";
let prototype = {
self: this,
};
let sandbox = createSandbox(SANDBOX_URL, prototype);
loadSubScript(SANDBOX_URL, sandbox);

View File

@ -0,0 +1,9 @@
"use strict";
self.addEventListener("message", function(event) {
switch (event.data) {
case "ping":
self.postMessage("pong");
break;
}
});

View File

@ -0,0 +1,3 @@
"use strict";
self.onmessage = function () {};

View File

@ -7,6 +7,9 @@ support-files =
WorkerDebugger.postMessage_childWorker.js
WorkerDebugger.postMessage_debugger.js
WorkerDebugger.postMessage_worker.js
WorkerDebuggerGlobalScope.createSandbox_debugger.js
WorkerDebuggerGlobalScope.createSandbox_sandbox.js
WorkerDebuggerGlobalScope.createSandbox_worker.js
WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js
WorkerDebuggerGlobalScope.enterEventLoop_debugger.js
WorkerDebuggerGlobalScope.enterEventLoop_worker.js
@ -45,6 +48,7 @@ support-files =
[test_WorkerDebugger.xul]
[test_WorkerDebugger.initialize.xul]
[test_WorkerDebugger.postMessage.xul]
[test_WorkerDebuggerGlobalScope.createSandbox.xul]
[test_WorkerDebuggerGlobalScope.enterEventLoop.xul]
[test_WorkerDebuggerGlobalScope.reportError.xul]
[test_WorkerDebuggerGlobalScope.setImmediate.xul]

View File

@ -0,0 +1,52 @@
<?xml version="1.0"?>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<window title="Test for WorkerDebuggerGlobalScope.createSandbox"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="test();">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
<script type="application/javascript" src="dom_worker_helper.js"/>
<script type="application/javascript">
<![CDATA[
const WORKER_URL = "WorkerDebuggerGlobalScope.createSandbox_worker.js";
const DEBUGGER_URL = BASE_URL + "WorkerDebuggerGlobalScope.createSandbox_debugger.js";
function test() {
Task.spawn(function* () {
SimpleTest.waitForExplicitFinish();
info("Create a worker, wait for its debugger to be registered, and " +
"initialize it.");
let promise = waitForRegister(WORKER_URL, DEBUGGER_URL);
let worker = new Worker(WORKER_URL);
let dbg = yield promise;
info("Send a request to the worker debugger. This should cause the " +
"worker debugger to send a response from within a sandbox.");
promise = waitForDebuggerMessage(dbg, "pong");
dbg.postMessage("ping");
yield promise;
SimpleTest.finish();
});
}
]]>
</script>
<body xmlns="http://www.w3.org/1999/xhtml">
<p id="display"></p>
<div id="content" style="display:none;"></div>
<pre id="test"></pre>
</body>
<label id="test-result"/>
</window>