710 lines
23 KiB
C++
710 lines
23 KiB
C++
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* 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 "ExtensionAPIRequestForwarder.h"
|
|
#include "ExtensionEventListener.h"
|
|
#include "ExtensionAPIBase.h"
|
|
|
|
#include "js/Promise.h"
|
|
#include "js/PropertyAndElement.h" // JS_GetElement
|
|
#include "mozilla/dom/Client.h"
|
|
#include "mozilla/ClearOnShutdown.h"
|
|
#include "mozilla/dom/ClonedErrorHolder.h"
|
|
#include "mozilla/dom/ExtensionBrowserBinding.h"
|
|
#include "mozilla/dom/FunctionBinding.h"
|
|
#include "mozilla/dom/WorkerScope.h"
|
|
#include "mozilla/dom/SerializedStackHolder.h"
|
|
#include "mozilla/dom/ServiceWorkerInfo.h"
|
|
#include "mozilla/dom/ServiceWorkerManager.h"
|
|
#include "mozilla/dom/ServiceWorkerRegistrationInfo.h"
|
|
#include "mozilla/dom/StructuredCloneTags.h"
|
|
#include "mozilla/ExtensionPolicyService.h"
|
|
#include "nsIGlobalObject.h"
|
|
#include "nsImportModule.h"
|
|
#include "nsIXPConnect.h"
|
|
|
|
namespace mozilla {
|
|
namespace extensions {
|
|
|
|
// ExtensionAPIRequestForwarder
|
|
|
|
// static
|
|
void ExtensionAPIRequestForwarder::ThrowUnexpectedError(JSContext* aCx,
|
|
ErrorResult& aRv) {
|
|
aRv.MightThrowJSException();
|
|
JS_ReportErrorASCII(aCx, "An unexpected error occurred");
|
|
aRv.StealExceptionFromJSContext(aCx);
|
|
}
|
|
|
|
ExtensionAPIRequestForwarder::ExtensionAPIRequestForwarder(
|
|
const mozIExtensionAPIRequest::RequestType aRequestType,
|
|
const nsAString& aApiNamespace, const nsAString& aApiMethod,
|
|
const nsAString& aApiObjectType, const nsAString& aApiObjectId) {
|
|
mRequestType = aRequestType;
|
|
mRequestTarget.mNamespace = aApiNamespace;
|
|
mRequestTarget.mMethod = aApiMethod;
|
|
mRequestTarget.mObjectType = aApiObjectType;
|
|
mRequestTarget.mObjectId = aApiObjectId;
|
|
}
|
|
|
|
// static
|
|
nsresult ExtensionAPIRequestForwarder::JSArrayToSequence(
|
|
JSContext* aCx, JS::Handle<JS::Value> aJSValue,
|
|
dom::Sequence<JS::Value>& aResult) {
|
|
bool isArray;
|
|
JS::Rooted<JSObject*> obj(aCx, aJSValue.toObjectOrNull());
|
|
|
|
if (NS_WARN_IF(!obj || !JS::IsArrayObject(aCx, obj, &isArray))) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
if (isArray) {
|
|
uint32_t len;
|
|
if (NS_WARN_IF(!JS::GetArrayLength(aCx, obj, &len))) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < len; i++) {
|
|
JS::Rooted<JS::Value> v(aCx);
|
|
JS_GetElement(aCx, obj, i, &v);
|
|
if (NS_WARN_IF(!aResult.AppendElement(v, fallible))) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
} else if (NS_WARN_IF(!aResult.AppendElement(aJSValue, fallible))) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
/* static */
|
|
mozIExtensionAPIRequestHandler&
|
|
ExtensionAPIRequestForwarder::APIRequestHandler() {
|
|
static nsCOMPtr<mozIExtensionAPIRequestHandler> sAPIRequestHandler;
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
if (MOZ_UNLIKELY(!sAPIRequestHandler)) {
|
|
sAPIRequestHandler = do_ImportESModule(
|
|
"resource://gre/modules/ExtensionProcessScript.sys.mjs",
|
|
"ExtensionAPIRequestHandler");
|
|
MOZ_RELEASE_ASSERT(sAPIRequestHandler);
|
|
ClearOnShutdown(&sAPIRequestHandler);
|
|
}
|
|
return *sAPIRequestHandler;
|
|
}
|
|
|
|
void ExtensionAPIRequestForwarder::SetSerializedCallerStack(
|
|
UniquePtr<dom::SerializedStackHolder> aCallerStack) {
|
|
MOZ_ASSERT(dom::IsCurrentThreadRunningWorker());
|
|
MOZ_ASSERT(mStackHolder.isNothing());
|
|
mStackHolder = Some(std::move(aCallerStack));
|
|
}
|
|
|
|
void ExtensionAPIRequestForwarder::Run(nsIGlobalObject* aGlobal, JSContext* aCx,
|
|
const dom::Sequence<JS::Value>& aArgs,
|
|
ExtensionEventListener* aListener,
|
|
JS::MutableHandle<JS::Value> aRetVal,
|
|
ErrorResult& aRv) {
|
|
MOZ_ASSERT(dom::IsCurrentThreadRunningWorker());
|
|
|
|
dom::WorkerPrivate* workerPrivate = dom::GetCurrentThreadWorkerPrivate();
|
|
MOZ_ASSERT(workerPrivate);
|
|
|
|
RefPtr<RequestWorkerRunnable> runnable =
|
|
new RequestWorkerRunnable(workerPrivate, this);
|
|
|
|
if (mStackHolder.isSome()) {
|
|
runnable->SetSerializedCallerStack(mStackHolder.extract());
|
|
}
|
|
|
|
RefPtr<dom::Promise> domPromise;
|
|
|
|
IgnoredErrorResult rv;
|
|
|
|
switch (mRequestType) {
|
|
case APIRequestType::CALL_FUNCTION_ASYNC:
|
|
domPromise = dom::Promise::Create(aGlobal, rv);
|
|
if (NS_WARN_IF(rv.Failed())) {
|
|
ThrowUnexpectedError(aCx, aRv);
|
|
return;
|
|
}
|
|
|
|
runnable->Init(aGlobal, aCx, aArgs, domPromise, rv);
|
|
break;
|
|
|
|
case APIRequestType::ADD_LISTENER:
|
|
[[fallthrough]];
|
|
case APIRequestType::REMOVE_LISTENER:
|
|
runnable->Init(aGlobal, aCx, aArgs, aListener, aRv);
|
|
break;
|
|
|
|
default:
|
|
runnable->Init(aGlobal, aCx, aArgs, rv);
|
|
}
|
|
|
|
if (NS_WARN_IF(rv.Failed())) {
|
|
ThrowUnexpectedError(aCx, aRv);
|
|
return;
|
|
}
|
|
|
|
runnable->Dispatch(workerPrivate, dom::WorkerStatus::Canceling, rv);
|
|
if (NS_WARN_IF(rv.Failed())) {
|
|
ThrowUnexpectedError(aCx, aRv);
|
|
return;
|
|
}
|
|
|
|
auto resultType = runnable->GetResultType();
|
|
if (resultType.isNothing()) {
|
|
if (NS_WARN_IF(ExtensionAPIRequest::ShouldHaveResult(mRequestType))) {
|
|
ThrowUnexpectedError(aCx, aRv);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Read and throw the extension error if needed.
|
|
if (resultType.isSome() && *resultType == APIResultType::EXTENSION_ERROR) {
|
|
JS::Rooted<JS::Value> ignoredResultValue(aCx);
|
|
runnable->ReadResult(aCx, &ignoredResultValue, aRv);
|
|
// When the result type is an error aRv is expected to be
|
|
// failed, if it is not throw the generic
|
|
// "An unexpected error occurred".
|
|
if (NS_WARN_IF(!aRv.Failed())) {
|
|
ThrowUnexpectedError(aCx, aRv);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (mRequestType == APIRequestType::CALL_FUNCTION_ASYNC) {
|
|
MOZ_ASSERT(domPromise);
|
|
if (NS_WARN_IF(!ToJSValue(aCx, domPromise, aRetVal))) {
|
|
ThrowUnexpectedError(aCx, aRv);
|
|
}
|
|
return;
|
|
}
|
|
|
|
JS::Rooted<JS::Value> resultValue(aCx);
|
|
runnable->ReadResult(aCx, &resultValue, rv);
|
|
if (NS_WARN_IF(rv.Failed())) {
|
|
ThrowUnexpectedError(aCx, aRv);
|
|
return;
|
|
}
|
|
|
|
aRetVal.set(resultValue);
|
|
}
|
|
|
|
void ExtensionAPIRequestForwarder::Run(nsIGlobalObject* aGlobal, JSContext* aCx,
|
|
const dom::Sequence<JS::Value>& aArgs,
|
|
JS::MutableHandle<JS::Value> aRetVal,
|
|
ErrorResult& aRv) {
|
|
Run(aGlobal, aCx, aArgs, nullptr, aRetVal, aRv);
|
|
}
|
|
|
|
void ExtensionAPIRequestForwarder::Run(nsIGlobalObject* aGlobal, JSContext* aCx,
|
|
const dom::Sequence<JS::Value>& aArgs,
|
|
ErrorResult& aRv) {
|
|
JS::Rooted<JS::Value> ignoredRetval(aCx);
|
|
Run(aGlobal, aCx, aArgs, nullptr, &ignoredRetval, aRv);
|
|
}
|
|
|
|
void ExtensionAPIRequestForwarder::Run(nsIGlobalObject* aGlobal, JSContext* aCx,
|
|
const dom::Sequence<JS::Value>& aArgs,
|
|
ExtensionEventListener* aListener,
|
|
ErrorResult& aRv) {
|
|
MOZ_ASSERT(aListener);
|
|
JS::Rooted<JS::Value> ignoredRetval(aCx);
|
|
Run(aGlobal, aCx, aArgs, aListener, &ignoredRetval, aRv);
|
|
}
|
|
|
|
void ExtensionAPIRequestForwarder::Run(
|
|
nsIGlobalObject* aGlobal, JSContext* aCx,
|
|
const dom::Sequence<JS::Value>& aArgs,
|
|
const RefPtr<dom::Promise>& aPromiseRetval, ErrorResult& aRv) {
|
|
MOZ_ASSERT(aPromiseRetval);
|
|
JS::Rooted<JS::Value> promisedRetval(aCx);
|
|
Run(aGlobal, aCx, aArgs, &promisedRetval, aRv);
|
|
if (aRv.Failed()) {
|
|
return;
|
|
}
|
|
aPromiseRetval->MaybeResolve(promisedRetval);
|
|
}
|
|
|
|
void ExtensionAPIRequestForwarder::Run(nsIGlobalObject* aGlobal, JSContext* aCx,
|
|
JS::MutableHandle<JS::Value> aRetVal,
|
|
ErrorResult& aRv) {
|
|
Run(aGlobal, aCx, {}, aRetVal, aRv);
|
|
}
|
|
|
|
namespace {
|
|
|
|
// Custom PromiseWorkerProxy callback to deserialize error objects
|
|
// from ClonedErrorHolder structured clone data.
|
|
JSObject* ExtensionAPIRequestStructuredCloneRead(
|
|
JSContext* aCx, JSStructuredCloneReader* aReader,
|
|
const dom::PromiseWorkerProxy* aProxy, uint32_t aTag, uint32_t aData) {
|
|
// Deserialize ClonedErrorHolder that may have been structured cloned
|
|
// as a result of a resolved/rejected promise.
|
|
if (aTag == dom::SCTAG_DOM_CLONED_ERROR_OBJECT) {
|
|
return dom::ClonedErrorHolder::ReadStructuredClone(aCx, aReader, nullptr);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// Custom PromiseWorkerProxy callback to serialize error objects into
|
|
// ClonedErrorHolder structured clone data.
|
|
bool ExtensionAPIRequestStructuredCloneWrite(JSContext* aCx,
|
|
JSStructuredCloneWriter* aWriter,
|
|
dom::PromiseWorkerProxy* aProxy,
|
|
JS::Handle<JSObject*> aObj) {
|
|
// Try to serialize the object as a CloneErrorHolder, if it fails then
|
|
// the object wasn't an error.
|
|
IgnoredErrorResult rv;
|
|
UniquePtr<dom::ClonedErrorHolder> ceh =
|
|
dom::ClonedErrorHolder::Create(aCx, aObj, rv);
|
|
if (NS_WARN_IF(rv.Failed()) || !ceh) {
|
|
return false;
|
|
}
|
|
|
|
return ceh->WriteStructuredClone(aCx, aWriter, nullptr);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
RequestWorkerRunnable::RequestWorkerRunnable(
|
|
dom::WorkerPrivate* aWorkerPrivate,
|
|
ExtensionAPIRequestForwarder* aOuterAPIRequest)
|
|
: WorkerMainThreadRunnable(aWorkerPrivate,
|
|
"ExtensionAPIRequest :: WorkerRunnable"_ns) {
|
|
MOZ_ASSERT(dom::IsCurrentThreadRunningWorker());
|
|
|
|
MOZ_ASSERT(aOuterAPIRequest);
|
|
mOuterRequest = aOuterAPIRequest;
|
|
}
|
|
|
|
void RequestWorkerRunnable::Init(nsIGlobalObject* aGlobal, JSContext* aCx,
|
|
const dom::Sequence<JS::Value>& aArgs,
|
|
ExtensionEventListener* aListener,
|
|
ErrorResult& aRv) {
|
|
MOZ_ASSERT(dom::IsCurrentThreadRunningWorker());
|
|
|
|
dom::WorkerPrivate* workerPrivate = dom::GetCurrentThreadWorkerPrivate();
|
|
|
|
mSWDescriptorId = workerPrivate->ServiceWorkerID();
|
|
|
|
auto* workerScope = workerPrivate->GlobalScope();
|
|
if (NS_WARN_IF(!workerScope)) {
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
return;
|
|
}
|
|
|
|
mClientInfo = workerScope->GetClientInfo();
|
|
if (mClientInfo.isNothing()) {
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
return;
|
|
}
|
|
|
|
IgnoredErrorResult rv;
|
|
SerializeArgs(aCx, aArgs, rv);
|
|
if (NS_WARN_IF(rv.Failed())) {
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
return;
|
|
}
|
|
|
|
if (!mStackHolder.isSome()) {
|
|
SerializeCallerStack(aCx);
|
|
}
|
|
|
|
mEventListener = aListener;
|
|
}
|
|
|
|
void RequestWorkerRunnable::Init(nsIGlobalObject* aGlobal, JSContext* aCx,
|
|
const dom::Sequence<JS::Value>& aArgs,
|
|
const RefPtr<dom::Promise>& aPromiseRetval,
|
|
ErrorResult& aRv) {
|
|
// Custom callbacks needed to make the PromiseWorkerProxy instance to
|
|
// be able to write and read errors using CloneErrorHolder.
|
|
static const dom::PromiseWorkerProxy::
|
|
PromiseWorkerProxyStructuredCloneCallbacks
|
|
kExtensionAPIRequestStructuredCloneCallbacks = {
|
|
ExtensionAPIRequestStructuredCloneRead,
|
|
ExtensionAPIRequestStructuredCloneWrite,
|
|
};
|
|
|
|
Init(aGlobal, aCx, aArgs, /* aListener */ nullptr, aRv);
|
|
if (aRv.Failed()) {
|
|
return;
|
|
}
|
|
|
|
RefPtr<dom::PromiseWorkerProxy> promiseProxy =
|
|
dom::PromiseWorkerProxy::Create(
|
|
dom::GetCurrentThreadWorkerPrivate(), aPromiseRetval,
|
|
&kExtensionAPIRequestStructuredCloneCallbacks);
|
|
if (!promiseProxy) {
|
|
aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
|
|
return;
|
|
}
|
|
mPromiseProxy = promiseProxy.forget();
|
|
}
|
|
|
|
void RequestWorkerRunnable::SetSerializedCallerStack(
|
|
UniquePtr<dom::SerializedStackHolder> aCallerStack) {
|
|
MOZ_ASSERT(dom::IsCurrentThreadRunningWorker());
|
|
MOZ_ASSERT(mStackHolder.isNothing());
|
|
mStackHolder = Some(std::move(aCallerStack));
|
|
}
|
|
|
|
void RequestWorkerRunnable::SerializeCallerStack(JSContext* aCx) {
|
|
MOZ_ASSERT(dom::IsCurrentThreadRunningWorker());
|
|
MOZ_ASSERT(mStackHolder.isNothing());
|
|
mStackHolder = Some(dom::GetCurrentStack(aCx));
|
|
}
|
|
|
|
void RequestWorkerRunnable::DeserializeCallerStack(
|
|
JSContext* aCx, JS::MutableHandle<JS::Value> aRetval) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
if (mStackHolder.isSome()) {
|
|
JS::Rooted<JSObject*> savedFrame(aCx, mStackHolder->get()->ReadStack(aCx));
|
|
MOZ_ASSERT(savedFrame);
|
|
aRetval.set(JS::ObjectValue(*savedFrame));
|
|
mStackHolder = Nothing();
|
|
}
|
|
}
|
|
|
|
void RequestWorkerRunnable::SerializeArgs(JSContext* aCx,
|
|
const dom::Sequence<JS::Value>& aArgs,
|
|
ErrorResult& aRv) {
|
|
MOZ_ASSERT(dom::IsCurrentThreadRunningWorker());
|
|
MOZ_ASSERT(!mArgsHolder);
|
|
|
|
JS::Rooted<JS::Value> jsval(aCx);
|
|
if (NS_WARN_IF(!ToJSValue(aCx, aArgs, &jsval))) {
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
return;
|
|
}
|
|
|
|
mArgsHolder = Some(MakeUnique<dom::StructuredCloneHolder>(
|
|
dom::StructuredCloneHolder::CloningSupported,
|
|
dom::StructuredCloneHolder::TransferringNotSupported,
|
|
JS::StructuredCloneScope::SameProcess));
|
|
mArgsHolder->get()->Write(aCx, jsval, aRv);
|
|
}
|
|
|
|
nsresult RequestWorkerRunnable::DeserializeArgs(
|
|
JSContext* aCx, JS::MutableHandle<JS::Value> aArgs) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
if (mArgsHolder.isSome() && mArgsHolder->get()->HasData()) {
|
|
IgnoredErrorResult rv;
|
|
|
|
JS::Rooted<JS::Value> jsvalue(aCx);
|
|
mArgsHolder->get()->Read(xpc::CurrentNativeGlobal(aCx), aCx, &jsvalue, rv);
|
|
if (NS_WARN_IF(rv.Failed())) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
aArgs.set(jsvalue);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
bool RequestWorkerRunnable::MainThreadRun() {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
nsCOMPtr<mozIExtensionAPIRequestHandler> handler =
|
|
&ExtensionAPIRequestForwarder::APIRequestHandler();
|
|
nsCOMPtr<nsIXPConnectWrappedJS> wrapped = do_QueryInterface(handler);
|
|
dom::AutoJSAPI jsapi;
|
|
if (!jsapi.Init(wrapped->GetJSObjectGlobal())) {
|
|
return false;
|
|
}
|
|
|
|
auto* cx = jsapi.cx();
|
|
JS::Rooted<JS::Value> retval(cx);
|
|
return HandleAPIRequest(cx, &retval);
|
|
}
|
|
|
|
already_AddRefed<ExtensionAPIRequest> RequestWorkerRunnable::CreateAPIRequest(
|
|
JSContext* aCx) {
|
|
JS::Rooted<JS::Value> callArgs(aCx);
|
|
JS::Rooted<JS::Value> callerStackValue(aCx);
|
|
|
|
DeserializeArgs(aCx, &callArgs);
|
|
DeserializeCallerStack(aCx, &callerStackValue);
|
|
|
|
RefPtr<ExtensionAPIRequest> request = new ExtensionAPIRequest(
|
|
mOuterRequest->GetRequestType(), *mOuterRequest->GetRequestTarget());
|
|
request->Init(mClientInfo, mSWDescriptorId, callArgs, callerStackValue);
|
|
|
|
if (mEventListener) {
|
|
request->SetEventListener(mEventListener.forget());
|
|
}
|
|
|
|
return request.forget();
|
|
}
|
|
|
|
already_AddRefed<WebExtensionPolicy>
|
|
RequestWorkerRunnable::GetWebExtensionPolicy() {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(mWorkerRef);
|
|
auto* baseURI = mWorkerRef->Private()->GetBaseURI();
|
|
RefPtr<WebExtensionPolicy> policy =
|
|
ExtensionPolicyService::GetSingleton().GetByURL(baseURI);
|
|
return policy.forget();
|
|
}
|
|
|
|
bool RequestWorkerRunnable::HandleAPIRequest(
|
|
JSContext* aCx, JS::MutableHandle<JS::Value> aRetval) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
RefPtr<WebExtensionPolicy> policy = GetWebExtensionPolicy();
|
|
if (NS_WARN_IF(!policy || !policy->Active())) {
|
|
// Fails if no extension policy object has been found, or if the
|
|
// extension is not active.
|
|
return false;
|
|
}
|
|
|
|
nsresult rv;
|
|
|
|
RefPtr<ExtensionAPIRequest> request = CreateAPIRequest(aCx);
|
|
|
|
nsCOMPtr<mozIExtensionAPIRequestHandler> handler =
|
|
&ExtensionAPIRequestForwarder::APIRequestHandler();
|
|
RefPtr<mozIExtensionAPIRequestResult> apiResult;
|
|
rv = handler->HandleAPIRequest(policy, request, getter_AddRefs(apiResult));
|
|
|
|
if (NS_FAILED(rv)) {
|
|
return false;
|
|
}
|
|
|
|
// A missing apiResult is expected for some request types
|
|
// (e.g. CALL_FUNCTION_NO_RETURN/ADD_LISTENER/REMOVE_LISTENER).
|
|
// If the apiResult is missing for a request type that expects
|
|
// to have one, consider the request as failed with an unknown error.
|
|
if (!apiResult) {
|
|
return !request->ShouldHaveResult();
|
|
}
|
|
|
|
mozIExtensionAPIRequestResult::ResultType resultType;
|
|
apiResult->GetType(&resultType);
|
|
apiResult->GetValue(aRetval);
|
|
|
|
mResultType = Some(resultType);
|
|
|
|
bool isExtensionError =
|
|
resultType == mozIExtensionAPIRequestResult::ResultType::EXTENSION_ERROR;
|
|
bool okSerializedError = false;
|
|
|
|
if (aRetval.isObject()) {
|
|
// Try to serialize the result as an ClonedErrorHolder
|
|
// (because all API requests could receive one for EXTENSION_ERROR
|
|
// result types, and some also as a RETURN_VALUE result, e.g.
|
|
// runtime.lastError).
|
|
JS::Rooted<JSObject*> errObj(aCx, &aRetval.toObject());
|
|
IgnoredErrorResult rv;
|
|
UniquePtr<dom::ClonedErrorHolder> ceh =
|
|
dom::ClonedErrorHolder::Create(aCx, errObj, rv);
|
|
if (!rv.Failed() && ceh) {
|
|
okSerializedError = ToJSValue(aCx, std::move(ceh), aRetval);
|
|
} else {
|
|
okSerializedError = false;
|
|
}
|
|
}
|
|
|
|
if (isExtensionError && !okSerializedError) {
|
|
NS_WARNING("Failed to wrap ClonedErrorHolder");
|
|
MOZ_DIAGNOSTIC_CRASH("Failed to wrap ClonedErrorHolder");
|
|
return false;
|
|
}
|
|
|
|
if (isExtensionError && !aRetval.isObject()) {
|
|
NS_WARNING("Unexpected non-object error");
|
|
return false;
|
|
}
|
|
|
|
switch (resultType) {
|
|
case mozIExtensionAPIRequestResult::ResultType::RETURN_VALUE:
|
|
return ProcessHandlerResult(aCx, aRetval);
|
|
case mozIExtensionAPIRequestResult::ResultType::EXTENSION_ERROR:
|
|
if (!aRetval.isObject()) {
|
|
return false;
|
|
}
|
|
return ProcessHandlerResult(aCx, aRetval);
|
|
}
|
|
|
|
MOZ_DIAGNOSTIC_CRASH("Unexpected API request ResultType");
|
|
return false;
|
|
}
|
|
|
|
bool RequestWorkerRunnable::ProcessHandlerResult(
|
|
JSContext* aCx, JS::MutableHandle<JS::Value> aRetval) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
if (mOuterRequest->GetRequestType() == APIRequestType::CALL_FUNCTION_ASYNC) {
|
|
if (NS_WARN_IF(mResultType.isNothing())) {
|
|
return false;
|
|
}
|
|
|
|
if (*mResultType == APIResultType::RETURN_VALUE) {
|
|
// For an Async API method we expect a promise object to be set
|
|
// as the value to return, if it is not we return earlier here
|
|
// (and then throw a generic unexpected error to the caller).
|
|
if (NS_WARN_IF(!aRetval.isObject())) {
|
|
return false;
|
|
}
|
|
JS::Rooted<JSObject*> obj(aCx, &aRetval.toObject());
|
|
if (NS_WARN_IF(!JS::IsPromiseObject(obj))) {
|
|
return false;
|
|
}
|
|
|
|
ErrorResult rv;
|
|
nsIGlobalObject* glob = xpc::CurrentNativeGlobal(aCx);
|
|
RefPtr<dom::Promise> retPromise =
|
|
dom::Promise::Resolve(glob, aCx, aRetval, rv);
|
|
if (rv.Failed()) {
|
|
return false;
|
|
}
|
|
retPromise->AppendNativeHandler(mPromiseProxy);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
switch (*mResultType) {
|
|
case APIResultType::RETURN_VALUE:
|
|
[[fallthrough]];
|
|
case APIResultType::EXTENSION_ERROR: {
|
|
// In all other case we expect the result to be:
|
|
// - a structured clonable result
|
|
// - an extension error (e.g. due to the API call params validation
|
|
// errors),
|
|
// previously converted into a CloneErrorHolder
|
|
IgnoredErrorResult rv;
|
|
mResultHolder = Some(MakeUnique<dom::StructuredCloneHolder>(
|
|
dom::StructuredCloneHolder::CloningSupported,
|
|
dom::StructuredCloneHolder::TransferringNotSupported,
|
|
JS::StructuredCloneScope::SameProcess));
|
|
mResultHolder->get()->Write(aCx, aRetval, rv);
|
|
return !rv.Failed();
|
|
}
|
|
}
|
|
|
|
MOZ_DIAGNOSTIC_CRASH("Unexpected API request ResultType");
|
|
return false;
|
|
}
|
|
|
|
void RequestWorkerRunnable::ReadResult(JSContext* aCx,
|
|
JS::MutableHandle<JS::Value> aResult,
|
|
ErrorResult& aRv) {
|
|
MOZ_ASSERT(dom::IsCurrentThreadRunningWorker());
|
|
if (mResultHolder.isNothing() || !mResultHolder->get()->HasData()) {
|
|
return;
|
|
}
|
|
|
|
if (NS_WARN_IF(mResultType.isNothing())) {
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
return;
|
|
}
|
|
|
|
switch (*mResultType) {
|
|
case mozIExtensionAPIRequestResult::ResultType::RETURN_VALUE:
|
|
mResultHolder->get()->Read(xpc::CurrentNativeGlobal(aCx), aCx, aResult,
|
|
aRv);
|
|
return;
|
|
case mozIExtensionAPIRequestResult::ResultType::EXTENSION_ERROR:
|
|
JS::Rooted<JS::Value> exn(aCx);
|
|
IgnoredErrorResult rv;
|
|
mResultHolder->get()->Read(xpc::CurrentNativeGlobal(aCx), aCx, &exn, rv);
|
|
if (rv.Failed()) {
|
|
NS_WARNING("Failed to deserialize extension error");
|
|
ExtensionAPIBase::ThrowUnexpectedError(aCx, aRv);
|
|
return;
|
|
}
|
|
|
|
aRv.MightThrowJSException();
|
|
aRv.ThrowJSException(aCx, exn);
|
|
return;
|
|
}
|
|
|
|
MOZ_DIAGNOSTIC_CRASH("Unexpected API request ResultType");
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
}
|
|
|
|
// RequestInitWorkerContextRunnable
|
|
|
|
RequestInitWorkerRunnable::RequestInitWorkerRunnable(
|
|
dom::WorkerPrivate* aWorkerPrivate, Maybe<dom::ClientInfo>& aSWClientInfo)
|
|
: WorkerMainThreadRunnable(aWorkerPrivate,
|
|
"extensions::RequestInitWorkerRunnable"_ns) {
|
|
MOZ_ASSERT(dom::IsCurrentThreadRunningWorker());
|
|
MOZ_ASSERT(aSWClientInfo.isSome());
|
|
mClientInfo = aSWClientInfo;
|
|
}
|
|
|
|
bool RequestInitWorkerRunnable::MainThreadRun() {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(mWorkerRef);
|
|
|
|
dom::WorkerPrivate* workerPrivate = mWorkerRef->Private();
|
|
|
|
auto* baseURI = workerPrivate->GetBaseURI();
|
|
RefPtr<WebExtensionPolicy> policy =
|
|
ExtensionPolicyService::GetSingleton().GetByURL(baseURI);
|
|
|
|
RefPtr<ExtensionServiceWorkerInfo> swInfo = new ExtensionServiceWorkerInfo(
|
|
*mClientInfo, workerPrivate->ServiceWorkerID());
|
|
|
|
nsCOMPtr<mozIExtensionAPIRequestHandler> handler =
|
|
&ExtensionAPIRequestForwarder::APIRequestHandler();
|
|
MOZ_ASSERT(handler);
|
|
|
|
if (NS_FAILED(handler->InitExtensionWorker(policy, swInfo))) {
|
|
NS_WARNING("nsIExtensionAPIRequestHandler.initExtensionWorker call failed");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// NotifyWorkerLoadedRunnable
|
|
|
|
nsresult NotifyWorkerLoadedRunnable::Run() {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
RefPtr<WebExtensionPolicy> policy =
|
|
ExtensionPolicyService::GetSingleton().GetByURL(mSWBaseURI.get());
|
|
|
|
nsCOMPtr<mozIExtensionAPIRequestHandler> handler =
|
|
&ExtensionAPIRequestForwarder::APIRequestHandler();
|
|
MOZ_ASSERT(handler);
|
|
|
|
if (NS_FAILED(handler->OnExtensionWorkerLoaded(policy, mSWDescriptorId))) {
|
|
NS_WARNING(
|
|
"nsIExtensionAPIRequestHandler.onExtensionWorkerLoaded call failed");
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// NotifyWorkerDestroyedRunnable
|
|
|
|
nsresult NotifyWorkerDestroyedRunnable::Run() {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
RefPtr<WebExtensionPolicy> policy =
|
|
ExtensionPolicyService::GetSingleton().GetByURL(mSWBaseURI.get());
|
|
|
|
nsCOMPtr<mozIExtensionAPIRequestHandler> handler =
|
|
&ExtensionAPIRequestForwarder::APIRequestHandler();
|
|
MOZ_ASSERT(handler);
|
|
|
|
if (NS_FAILED(handler->OnExtensionWorkerDestroyed(policy, mSWDescriptorId))) {
|
|
NS_WARNING(
|
|
"nsIExtensionAPIRequestHandler.onExtensionWorkerDestroyed call failed");
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
} // namespace extensions
|
|
} // namespace mozilla
|