2019-04-18 22:37:15 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
|
|
/* 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/. */
|
|
|
|
|
|
|
|
#include "mozilla/dom/JSWindowActor.h"
|
|
|
|
#include "mozilla/dom/JSWindowActorBinding.h"
|
2020-02-11 23:44:24 +03:00
|
|
|
|
|
|
|
#include "mozilla/Telemetry.h"
|
2019-12-07 21:59:21 +03:00
|
|
|
#include "mozilla/dom/ClonedErrorHolder.h"
|
|
|
|
#include "mozilla/dom/ClonedErrorHolderBinding.h"
|
|
|
|
#include "mozilla/dom/DOMException.h"
|
|
|
|
#include "mozilla/dom/DOMExceptionBinding.h"
|
2019-04-18 22:37:15 +03:00
|
|
|
#include "mozilla/dom/MessageManagerBinding.h"
|
|
|
|
#include "mozilla/dom/PWindowGlobal.h"
|
Bug 1541557: Part 1 - Use correct globals for JSWindowActors not in the shared JSM global. r=nika
The current JSWindowActor code assumes that all actors will be created in the
shared JSM global. This has a few problems:
1) For actors in other scopes, it enters the wrong compartment before decoding
messages, which leads to a compartment checker error when trying to call
message handlers. That could be fixed by just wrapping the result for the
caller, but that would lead to other problems. Aside from the efficiency
concerns of having to deal with cross-compartment wrappers, SpecialPowers in
particular would not be able to pass the resulting objects to unprivileged
scopes, since only SpecialPowers compartments have permissive CCWs enabled.
2) It also leads to the prototype objects for the actor instances being
created in the shared JSM scope, even when the actors themselves are defined
in other compartments. Again, aside from CCW efficiency issues, this prevents
the SpecialPowers instances from being accessed by the unprivileged scopes
that they're exposed to, since the prototype objects live in privileged scopes
which don't have permissive CCWs enabled.
This patch changes child actors to always create their objects in the global
of their constructors.
The parent objects are still created in the shared JSM global, but they now
wrap values for the appropriate compartment before trying to call message
handlers.
Differential Revision: https://phabricator.services.mozilla.com/D35051
--HG--
extra : rebase_source : 436ce516080812680d1433bf3ecbd12f91d88165
extra : source : 61fa2745733f3631488a3ecccc144823683b7b6d
2019-06-13 02:06:40 +03:00
|
|
|
#include "mozilla/dom/Promise.h"
|
2019-06-14 01:01:05 +03:00
|
|
|
#include "js/Promise.h"
|
2019-09-20 21:01:07 +03:00
|
|
|
#include "xpcprivate.h"
|
2020-02-11 23:44:24 +03:00
|
|
|
#include "nsASCIIMask.h"
|
2019-04-18 22:37:15 +03:00
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace dom {
|
|
|
|
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSWindowActor)
|
|
|
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
|
|
NS_INTERFACE_MAP_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(JSWindowActor)
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(JSWindowActor)
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(JSWindowActor)
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(JSWindowActor)
|
2019-12-16 23:51:21 +03:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWrappedJS)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingQueries)
|
2019-04-18 22:37:15 +03:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(JSWindowActor)
|
2019-09-20 21:01:07 +03:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWrappedJS)
|
2019-12-16 23:51:21 +03:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingQueries)
|
2019-04-18 22:37:15 +03:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(JSWindowActor)
|
|
|
|
|
|
|
|
JSWindowActor::JSWindowActor() : mNextQueryId(0) {}
|
|
|
|
|
2019-05-10 18:01:40 +03:00
|
|
|
void JSWindowActor::StartDestroy() {
|
2019-08-15 02:13:13 +03:00
|
|
|
InvokeCallback(CallbackFunction::WillDestroy);
|
2019-05-10 18:01:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void JSWindowActor::AfterDestroy() {
|
2019-08-15 02:13:13 +03:00
|
|
|
InvokeCallback(CallbackFunction::DidDestroy);
|
2019-12-16 23:51:21 +03:00
|
|
|
// Clear out & reject mPendingQueries
|
|
|
|
RejectPendingQueries();
|
2019-05-10 18:01:40 +03:00
|
|
|
}
|
|
|
|
|
2019-08-15 02:13:13 +03:00
|
|
|
void JSWindowActor::InvokeCallback(CallbackFunction callback) {
|
2020-02-04 21:16:07 +03:00
|
|
|
MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
|
|
|
|
|
2019-06-14 01:01:05 +03:00
|
|
|
AutoEntryScript aes(GetParentObject(), "JSWindowActor destroy callback");
|
2019-05-10 18:01:40 +03:00
|
|
|
JSContext* cx = aes.cx();
|
2019-08-15 02:13:13 +03:00
|
|
|
MozJSWindowActorCallbacks callbacksHolder;
|
2019-05-10 18:01:40 +03:00
|
|
|
NS_ENSURE_TRUE_VOID(GetWrapper());
|
|
|
|
JS::Rooted<JS::Value> val(cx, JS::ObjectValue(*GetWrapper()));
|
|
|
|
if (NS_WARN_IF(!callbacksHolder.Init(cx, val))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Destroy callback is optional.
|
2019-08-15 02:13:13 +03:00
|
|
|
if (callback == CallbackFunction::WillDestroy) {
|
2019-05-10 18:01:40 +03:00
|
|
|
if (callbacksHolder.mWillDestroy.WasPassed()) {
|
2019-06-10 20:53:22 +03:00
|
|
|
callbacksHolder.mWillDestroy.Value()->Call(this);
|
2019-05-10 18:01:40 +03:00
|
|
|
}
|
2019-08-15 02:13:13 +03:00
|
|
|
} else if (callback == CallbackFunction::DidDestroy) {
|
2019-05-10 18:01:40 +03:00
|
|
|
if (callbacksHolder.mDidDestroy.WasPassed()) {
|
2019-06-10 20:53:22 +03:00
|
|
|
callbacksHolder.mDidDestroy.Value()->Call(this);
|
2019-05-10 18:01:40 +03:00
|
|
|
}
|
2019-08-15 02:13:13 +03:00
|
|
|
} else {
|
|
|
|
if (callbacksHolder.mActorCreated.WasPassed()) {
|
|
|
|
callbacksHolder.mActorCreated.Value()->Call(this);
|
|
|
|
}
|
2019-05-10 18:01:40 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-20 21:01:07 +03:00
|
|
|
nsresult JSWindowActor::QueryInterfaceActor(const nsIID& aIID, void** aPtr) {
|
2020-02-04 21:16:07 +03:00
|
|
|
MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
|
|
|
|
|
2019-09-20 21:01:07 +03:00
|
|
|
if (!mWrappedJS) {
|
|
|
|
AutoEntryScript aes(GetParentObject(), "JSWindowActor query interface");
|
|
|
|
JSContext* cx = aes.cx();
|
|
|
|
|
|
|
|
JS::Rooted<JSObject*> self(cx, GetWrapper());
|
|
|
|
JSAutoRealm ar(cx, self);
|
|
|
|
|
|
|
|
RefPtr<nsXPCWrappedJS> wrappedJS;
|
|
|
|
nsresult rv = nsXPCWrappedJS::GetNewOrUsed(
|
|
|
|
cx, self, NS_GET_IID(nsISupports), getter_AddRefs(wrappedJS));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
mWrappedJS = do_QueryInterface(wrappedJS);
|
|
|
|
MOZ_ASSERT(mWrappedJS);
|
|
|
|
}
|
|
|
|
|
|
|
|
return mWrappedJS->QueryInterface(aIID, aPtr);
|
|
|
|
}
|
|
|
|
|
2019-04-18 22:37:15 +03:00
|
|
|
void JSWindowActor::RejectPendingQueries() {
|
2020-02-04 21:16:07 +03:00
|
|
|
MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
|
|
|
|
|
2019-04-18 22:37:15 +03:00
|
|
|
// Take our queries out, in case somehow rejecting promises can trigger
|
|
|
|
// additions or removals.
|
|
|
|
nsRefPtrHashtable<nsUint64HashKey, Promise> pendingQueries;
|
|
|
|
mPendingQueries.SwapElements(pendingQueries);
|
|
|
|
for (auto iter = pendingQueries.Iter(); !iter.Done(); iter.Next()) {
|
|
|
|
iter.Data()->MaybeReject(NS_ERROR_NOT_AVAILABLE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-20 00:17:11 +03:00
|
|
|
/* static */
|
|
|
|
bool JSWindowActor::AllowMessage(const JSWindowActorMessageMeta& aMetadata,
|
|
|
|
size_t aDataLength) {
|
|
|
|
// A message includes more than structured clone data, so subtract
|
|
|
|
// 20KB to make it more likely that a message within this bound won't
|
|
|
|
// result in an overly large IPC message.
|
|
|
|
static const size_t kMaxMessageSize =
|
|
|
|
IPC::Channel::kMaximumMessageSize - 20 * 1024;
|
|
|
|
if (aDataLength < kMaxMessageSize) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-02-11 23:44:24 +03:00
|
|
|
nsAutoString messageName(aMetadata.actorName());
|
|
|
|
messageName.AppendLiteral("::");
|
|
|
|
messageName.Append(aMetadata.messageName());
|
|
|
|
|
|
|
|
// Remove digits to avoid spamming telemetry if anybody is dynamically
|
|
|
|
// generating message names with numbers in them.
|
|
|
|
messageName.StripTaggedASCII(ASCIIMask::Mask0to9());
|
|
|
|
|
|
|
|
Telemetry::ScalarAdd(
|
|
|
|
Telemetry::ScalarID::DOM_IPC_REJECTED_WINDOW_ACTOR_MESSAGE, messageName,
|
|
|
|
1);
|
|
|
|
|
2019-12-20 00:17:11 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-04-18 22:37:15 +03:00
|
|
|
void JSWindowActor::SetName(const nsAString& aName) {
|
|
|
|
MOZ_ASSERT(mName.IsEmpty(), "Cannot set name twice!");
|
|
|
|
mName = aName;
|
|
|
|
}
|
|
|
|
|
2019-12-07 21:59:23 +03:00
|
|
|
static ipc::StructuredCloneData CloneJSStack(JSContext* aCx,
|
|
|
|
JS::Handle<JSObject*> aStack) {
|
|
|
|
JS::Rooted<JS::Value> stackVal(aCx, JS::ObjectOrNullValue(aStack));
|
|
|
|
|
|
|
|
{
|
|
|
|
IgnoredErrorResult rv;
|
|
|
|
ipc::StructuredCloneData data;
|
|
|
|
data.Write(aCx, stackVal, rv);
|
|
|
|
if (!rv.Failed()) {
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ErrorResult rv;
|
|
|
|
ipc::StructuredCloneData data;
|
|
|
|
data.Write(aCx, JS::NullHandleValue, rv);
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ipc::StructuredCloneData CaptureJSStack(JSContext* aCx) {
|
|
|
|
JS::Rooted<JSObject*> stack(aCx, nullptr);
|
|
|
|
if (JS::ContextOptionsRef(aCx).asyncStack() &&
|
|
|
|
!JS::CaptureCurrentStack(aCx, &stack)) {
|
|
|
|
JS_ClearPendingException(aCx);
|
|
|
|
}
|
|
|
|
|
|
|
|
return CloneJSStack(aCx, stack);
|
|
|
|
}
|
|
|
|
|
2019-04-18 22:37:15 +03:00
|
|
|
void JSWindowActor::SendAsyncMessage(JSContext* aCx,
|
|
|
|
const nsAString& aMessageName,
|
|
|
|
JS::Handle<JS::Value> aObj,
|
|
|
|
ErrorResult& aRv) {
|
|
|
|
ipc::StructuredCloneData data;
|
2019-09-13 19:51:23 +03:00
|
|
|
if (!nsFrameMessageManager::GetParamsForMessage(
|
|
|
|
aCx, aObj, JS::UndefinedHandleValue, data)) {
|
2019-04-18 22:37:15 +03:00
|
|
|
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSWindowActorMessageMeta meta;
|
|
|
|
meta.actorName() = mName;
|
|
|
|
meta.messageName() = aMessageName;
|
|
|
|
meta.kind() = JSWindowActorMessageKind::Message;
|
|
|
|
|
2019-12-07 21:59:23 +03:00
|
|
|
SendRawMessage(meta, std::move(data), CaptureJSStack(aCx), aRv);
|
2019-04-18 22:37:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<Promise> JSWindowActor::SendQuery(
|
|
|
|
JSContext* aCx, const nsAString& aMessageName, JS::Handle<JS::Value> aObj,
|
2019-09-13 19:51:23 +03:00
|
|
|
ErrorResult& aRv) {
|
2019-04-18 22:37:15 +03:00
|
|
|
ipc::StructuredCloneData data;
|
2019-09-13 19:51:23 +03:00
|
|
|
if (!nsFrameMessageManager::GetParamsForMessage(
|
|
|
|
aCx, aObj, JS::UndefinedHandleValue, data)) {
|
2019-04-18 22:37:15 +03:00
|
|
|
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
|
|
|
|
if (NS_WARN_IF(!global)) {
|
|
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<Promise> promise = Promise::Create(global, aRv);
|
|
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSWindowActorMessageMeta meta;
|
|
|
|
meta.actorName() = mName;
|
|
|
|
meta.messageName() = aMessageName;
|
|
|
|
meta.queryId() = mNextQueryId++;
|
|
|
|
meta.kind() = JSWindowActorMessageKind::Query;
|
|
|
|
|
2020-02-25 20:03:36 +03:00
|
|
|
mPendingQueries.Put(meta.queryId(), RefPtr{promise});
|
2019-04-18 22:37:15 +03:00
|
|
|
|
2019-12-07 21:59:23 +03:00
|
|
|
SendRawMessage(meta, std::move(data), CaptureJSStack(aCx), aRv);
|
2019-04-18 22:37:15 +03:00
|
|
|
return promise.forget();
|
|
|
|
}
|
|
|
|
|
2019-12-07 21:59:23 +03:00
|
|
|
#define CHILD_DIAGNOSTIC_ASSERT(test, msg) \
|
|
|
|
do { \
|
|
|
|
if (XRE_IsParentProcess()) { \
|
|
|
|
MOZ_ASSERT(test, msg); \
|
|
|
|
} else { \
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(test, msg); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
2019-04-18 22:37:15 +03:00
|
|
|
void JSWindowActor::ReceiveRawMessage(const JSWindowActorMessageMeta& aMetadata,
|
2019-12-07 21:59:23 +03:00
|
|
|
ipc::StructuredCloneData&& aData,
|
|
|
|
ipc::StructuredCloneData&& aStack) {
|
2020-02-04 21:16:07 +03:00
|
|
|
MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
|
|
|
|
|
2019-06-14 01:01:05 +03:00
|
|
|
AutoEntryScript aes(GetParentObject(), "JSWindowActor message handler");
|
2019-04-18 22:37:15 +03:00
|
|
|
JSContext* cx = aes.cx();
|
|
|
|
|
|
|
|
// Read the message into a JS object from IPC.
|
|
|
|
ErrorResult error;
|
|
|
|
JS::Rooted<JS::Value> data(cx);
|
|
|
|
aData.Read(cx, &data, error);
|
|
|
|
if (NS_WARN_IF(error.Failed())) {
|
2019-12-07 21:59:23 +03:00
|
|
|
CHILD_DIAGNOSTIC_ASSERT(false, "Should not receive non-decodable data");
|
2019-04-18 22:37:15 +03:00
|
|
|
MOZ_ALWAYS_TRUE(error.MaybeSetPendingException(cx));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-12-07 21:59:23 +03:00
|
|
|
JS::Rooted<JSObject*> stack(cx);
|
|
|
|
Maybe<JS::AutoSetAsyncStackForNewCalls> stackSetter;
|
|
|
|
{
|
|
|
|
JS::Rooted<JS::Value> stackVal(cx);
|
|
|
|
aStack.Read(cx, &stackVal, IgnoreErrors());
|
|
|
|
if (stackVal.isObject()) {
|
|
|
|
stack = &stackVal.toObject();
|
|
|
|
if (!js::IsSavedFrame(stack)) {
|
|
|
|
CHILD_DIAGNOSTIC_ASSERT(false, "Stack must be a SavedFrame object");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
stackSetter.emplace(cx, stack, "JSWindowActor query");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-18 22:37:15 +03:00
|
|
|
switch (aMetadata.kind()) {
|
|
|
|
case JSWindowActorMessageKind::QueryResolve:
|
|
|
|
case JSWindowActorMessageKind::QueryReject:
|
|
|
|
ReceiveQueryReply(cx, aMetadata, data, error);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case JSWindowActorMessageKind::Message:
|
|
|
|
case JSWindowActorMessageKind::Query:
|
|
|
|
ReceiveMessageOrQuery(cx, aMetadata, data, error);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
MOZ_ASSERT_UNREACHABLE();
|
|
|
|
}
|
|
|
|
|
2020-01-10 00:27:25 +03:00
|
|
|
Unused << error.MaybeSetPendingException(cx);
|
2019-04-18 22:37:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void JSWindowActor::ReceiveMessageOrQuery(
|
|
|
|
JSContext* aCx, const JSWindowActorMessageMeta& aMetadata,
|
|
|
|
JS::Handle<JS::Value> aData, ErrorResult& aRv) {
|
|
|
|
// The argument which we want to pass to IPC.
|
|
|
|
RootedDictionary<ReceiveMessageArgument> argument(aCx);
|
|
|
|
argument.mObjects = JS_NewPlainObject(aCx);
|
|
|
|
argument.mTarget = this;
|
|
|
|
argument.mName = aMetadata.messageName();
|
|
|
|
argument.mData = aData;
|
|
|
|
argument.mJson = aData;
|
|
|
|
argument.mSync = false;
|
|
|
|
|
|
|
|
JS::Rooted<JSObject*> self(aCx, GetWrapper());
|
|
|
|
JS::Rooted<JSObject*> global(aCx, JS::GetNonCCWObjectGlobal(self));
|
|
|
|
|
|
|
|
// We only need to create a promise if we're dealing with a query here. It
|
|
|
|
// will be resolved or rejected once the listener has been called. Our
|
|
|
|
// listener on this promise will then send the reply.
|
|
|
|
RefPtr<Promise> promise;
|
|
|
|
if (aMetadata.kind() == JSWindowActorMessageKind::Query) {
|
|
|
|
promise = Promise::Create(xpc::NativeGlobal(global), aRv);
|
|
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-12-07 21:59:23 +03:00
|
|
|
RefPtr<QueryHandler> handler = new QueryHandler(this, aMetadata, promise);
|
2019-04-18 22:37:15 +03:00
|
|
|
promise->AppendNativeHandler(handler);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Invoke the actual callback.
|
|
|
|
JS::Rooted<JS::Value> retval(aCx);
|
|
|
|
RefPtr<MessageListener> messageListener =
|
|
|
|
new MessageListener(self, global, nullptr, nullptr);
|
|
|
|
messageListener->ReceiveMessage(argument, &retval, aRv,
|
2020-01-10 00:27:25 +03:00
|
|
|
"JSWindowActor receive message",
|
|
|
|
MessageListener::eRethrowExceptions);
|
2019-04-18 22:37:15 +03:00
|
|
|
|
|
|
|
// If we have a promise, resolve or reject it respectively.
|
|
|
|
if (promise) {
|
|
|
|
if (aRv.Failed()) {
|
2019-12-07 21:44:33 +03:00
|
|
|
if (aRv.IsUncatchableException()) {
|
|
|
|
aRv.SuppressException();
|
2020-02-03 23:37:32 +03:00
|
|
|
promise->MaybeRejectWithTimeoutError(
|
|
|
|
"Message handler threw uncatchable exception");
|
2019-12-07 21:44:33 +03:00
|
|
|
} else {
|
2020-02-14 18:42:30 +03:00
|
|
|
promise->MaybeReject(std::move(aRv));
|
2019-12-07 21:44:33 +03:00
|
|
|
}
|
2019-04-18 22:37:15 +03:00
|
|
|
} else {
|
2019-07-29 17:43:54 +03:00
|
|
|
promise->MaybeResolve(retval);
|
2019-04-18 22:37:15 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void JSWindowActor::ReceiveQueryReply(JSContext* aCx,
|
|
|
|
const JSWindowActorMessageMeta& aMetadata,
|
|
|
|
JS::Handle<JS::Value> aData,
|
|
|
|
ErrorResult& aRv) {
|
|
|
|
if (NS_WARN_IF(aMetadata.actorName() != mName)) {
|
|
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<Promise> promise;
|
|
|
|
if (NS_WARN_IF(!mPendingQueries.Remove(aMetadata.queryId(),
|
|
|
|
getter_AddRefs(promise)))) {
|
|
|
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
Bug 1541557: Part 1 - Use correct globals for JSWindowActors not in the shared JSM global. r=nika
The current JSWindowActor code assumes that all actors will be created in the
shared JSM global. This has a few problems:
1) For actors in other scopes, it enters the wrong compartment before decoding
messages, which leads to a compartment checker error when trying to call
message handlers. That could be fixed by just wrapping the result for the
caller, but that would lead to other problems. Aside from the efficiency
concerns of having to deal with cross-compartment wrappers, SpecialPowers in
particular would not be able to pass the resulting objects to unprivileged
scopes, since only SpecialPowers compartments have permissive CCWs enabled.
2) It also leads to the prototype objects for the actor instances being
created in the shared JSM scope, even when the actors themselves are defined
in other compartments. Again, aside from CCW efficiency issues, this prevents
the SpecialPowers instances from being accessed by the unprivileged scopes
that they're exposed to, since the prototype objects live in privileged scopes
which don't have permissive CCWs enabled.
This patch changes child actors to always create their objects in the global
of their constructors.
The parent objects are still created in the shared JSM global, but they now
wrap values for the appropriate compartment before trying to call message
handlers.
Differential Revision: https://phabricator.services.mozilla.com/D35051
--HG--
extra : rebase_source : 436ce516080812680d1433bf3ecbd12f91d88165
extra : source : 61fa2745733f3631488a3ecccc144823683b7b6d
2019-06-13 02:06:40 +03:00
|
|
|
JSAutoRealm ar(aCx, promise->PromiseObj());
|
|
|
|
JS::RootedValue data(aCx, aData);
|
|
|
|
if (NS_WARN_IF(!JS_WrapValue(aCx, &data))) {
|
|
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-04-18 22:37:15 +03:00
|
|
|
if (aMetadata.kind() == JSWindowActorMessageKind::QueryResolve) {
|
2019-07-29 17:43:54 +03:00
|
|
|
promise->MaybeResolve(data);
|
2019-04-18 22:37:15 +03:00
|
|
|
} else {
|
2019-12-07 21:59:21 +03:00
|
|
|
promise->MaybeReject(data);
|
2019-04-18 22:37:15 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Native handler for our generated promise which is used to handle Queries and
|
|
|
|
// send the reply when their promises have been resolved.
|
|
|
|
JSWindowActor::QueryHandler::QueryHandler(
|
2019-12-07 21:59:23 +03:00
|
|
|
JSWindowActor* aActor, const JSWindowActorMessageMeta& aMetadata,
|
|
|
|
Promise* aPromise)
|
2019-04-18 22:37:15 +03:00
|
|
|
: mActor(aActor),
|
2019-12-07 21:59:23 +03:00
|
|
|
mPromise(aPromise),
|
2019-04-18 22:37:15 +03:00
|
|
|
mMessageName(aMetadata.messageName()),
|
|
|
|
mQueryId(aMetadata.queryId()) {}
|
|
|
|
|
|
|
|
void JSWindowActor::QueryHandler::RejectedCallback(
|
|
|
|
JSContext* aCx, JS::Handle<JS::Value> aValue) {
|
|
|
|
if (!mActor) {
|
2019-12-07 21:59:21 +03:00
|
|
|
// Make sure that this rejection is reported. See comment below.
|
|
|
|
Unused << JS::CallOriginalPromiseReject(aCx, aValue);
|
2019-04-18 22:37:15 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-12-07 21:59:21 +03:00
|
|
|
JS::Rooted<JS::Value> value(aCx, aValue);
|
|
|
|
if (value.isObject()) {
|
|
|
|
JS::Rooted<JSObject*> error(aCx, &value.toObject());
|
|
|
|
if (RefPtr<ClonedErrorHolder> ceh =
|
|
|
|
ClonedErrorHolder::Create(aCx, error, IgnoreErrors())) {
|
|
|
|
JS::RootedObject obj(aCx);
|
|
|
|
// Note: We can't use `ToJSValue` here because ClonedErrorHolder isn't
|
|
|
|
// wrapper cached.
|
|
|
|
if (ceh->WrapObject(aCx, nullptr, &obj)) {
|
|
|
|
value.setObject(*obj);
|
|
|
|
} else {
|
|
|
|
JS_ClearPendingException(aCx);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
JS_ClearPendingException(aCx);
|
|
|
|
}
|
|
|
|
}
|
2019-12-07 08:32:51 +03:00
|
|
|
|
2019-12-07 21:59:21 +03:00
|
|
|
Maybe<ipc::StructuredCloneData> data;
|
|
|
|
data.emplace();
|
|
|
|
IgnoredErrorResult rv;
|
|
|
|
data->Write(aCx, value, rv);
|
|
|
|
if (rv.Failed()) {
|
|
|
|
// Failed to clone the rejection value. Make sure that this rejection is
|
|
|
|
// reported, despite being "handled". This is done by creating a new
|
|
|
|
// promise in the rejected state, and throwing it away. This will be
|
|
|
|
// reported as an unhandled rejected promise.
|
|
|
|
Unused << JS::CallOriginalPromiseReject(aCx, aValue);
|
|
|
|
|
|
|
|
data.reset();
|
|
|
|
data.emplace();
|
|
|
|
data->Write(aCx, JS::UndefinedHandleValue, rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
SendReply(aCx, JSWindowActorMessageKind::QueryReject, std::move(*data));
|
2019-04-18 22:37:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void JSWindowActor::QueryHandler::ResolvedCallback(
|
|
|
|
JSContext* aCx, JS::Handle<JS::Value> aValue) {
|
|
|
|
if (!mActor) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ipc::StructuredCloneData data;
|
|
|
|
data.InitScope(JS::StructuredCloneScope::DifferentProcess);
|
|
|
|
|
|
|
|
IgnoredErrorResult error;
|
|
|
|
data.Write(aCx, aValue, error);
|
|
|
|
if (NS_WARN_IF(error.Failed())) {
|
|
|
|
// We failed to serialize the message over IPC. Report this error to the
|
|
|
|
// console, and send a reject reply.
|
|
|
|
nsAutoString msg;
|
|
|
|
msg.Append(mActor->Name());
|
|
|
|
msg.Append(':');
|
|
|
|
msg.Append(mMessageName);
|
2019-12-07 21:59:21 +03:00
|
|
|
msg.AppendLiteral(": message reply cannot be cloned.");
|
2019-04-18 22:37:15 +03:00
|
|
|
|
2019-12-07 21:59:21 +03:00
|
|
|
auto exc = MakeRefPtr<Exception>(
|
|
|
|
NS_ConvertUTF16toUTF8(msg), NS_ERROR_FAILURE,
|
|
|
|
NS_LITERAL_CSTRING("DataCloneError"), nullptr, nullptr);
|
2019-04-18 22:37:15 +03:00
|
|
|
|
2019-12-07 21:59:21 +03:00
|
|
|
JS::Rooted<JS::Value> val(aCx);
|
|
|
|
if (ToJSValue(aCx, exc, &val)) {
|
|
|
|
RejectedCallback(aCx, val);
|
|
|
|
}
|
2019-04-18 22:37:15 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SendReply(aCx, JSWindowActorMessageKind::QueryResolve, std::move(data));
|
|
|
|
}
|
|
|
|
|
|
|
|
void JSWindowActor::QueryHandler::SendReply(JSContext* aCx,
|
|
|
|
JSWindowActorMessageKind aKind,
|
|
|
|
ipc::StructuredCloneData&& aData) {
|
|
|
|
MOZ_ASSERT(mActor);
|
|
|
|
|
|
|
|
JSWindowActorMessageMeta meta;
|
|
|
|
meta.actorName() = mActor->Name();
|
|
|
|
meta.messageName() = mMessageName;
|
|
|
|
meta.queryId() = mQueryId;
|
|
|
|
meta.kind() = aKind;
|
|
|
|
|
2019-12-07 21:59:23 +03:00
|
|
|
JS::Rooted<JSObject*> promise(aCx, mPromise->PromiseObj());
|
|
|
|
JS::Rooted<JSObject*> stack(aCx, JS::GetPromiseResolutionSite(promise));
|
|
|
|
|
|
|
|
mActor->SendRawMessage(meta, std::move(aData), CloneJSStack(aCx, stack),
|
|
|
|
IgnoreErrors());
|
2019-04-18 22:37:15 +03:00
|
|
|
mActor = nullptr;
|
2019-12-07 21:59:23 +03:00
|
|
|
mPromise = nullptr;
|
2019-04-18 22:37:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSWindowActor::QueryHandler)
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
|
|
NS_INTERFACE_MAP_END
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(JSWindowActor::QueryHandler)
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(JSWindowActor::QueryHandler)
|
|
|
|
|
2019-12-07 21:59:23 +03:00
|
|
|
NS_IMPL_CYCLE_COLLECTION(JSWindowActor::QueryHandler, mActor, mPromise)
|
2019-04-18 22:37:15 +03:00
|
|
|
|
|
|
|
} // namespace dom
|
|
|
|
} // namespace mozilla
|