diff options
Diffstat (limited to 'toolkit/components/extensions/webidl-api/ExtensionAPIRequestForwarder.cpp')
-rw-r--r-- | toolkit/components/extensions/webidl-api/ExtensionAPIRequestForwarder.cpp | 710 |
1 files changed, 710 insertions, 0 deletions
diff --git a/toolkit/components/extensions/webidl-api/ExtensionAPIRequestForwarder.cpp b/toolkit/components/extensions/webidl-api/ExtensionAPIRequestForwarder.cpp new file mode 100644 index 0000000000..81e571bf8e --- /dev/null +++ b/toolkit/components/extensions/webidl-api/ExtensionAPIRequestForwarder.cpp @@ -0,0 +1,710 @@ +/* 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/ClonedErrorHolderBinding.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_ImportModule("resource://gre/modules/ExtensionProcessScript.jsm", + "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(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; + RefPtr<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()); + + mSWDescriptorId = mWorkerPrivate->ServiceWorkerID(); + + auto* workerScope = mWorkerPrivate->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( + mWorkerPrivate, 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(mWorkerPrivate); + auto* baseURI = mWorkerPrivate->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; + RefPtr<dom::ClonedErrorHolder> ceh = + dom::ClonedErrorHolder::Create(aCx, errObj, rv); + if (!rv.Failed() && ceh) { + JS::Rooted<JSObject*> obj(aCx); + // Note: `ToJSValue` cannot be used because ClonedErrorHolder isn't + // wrapper cached. + okSerializedError = ceh->WrapObject(aCx, nullptr, &obj); + aRetval.setObject(*obj); + } else { + okSerializedError = false; + } + } + + if (isExtensionError && !okSerializedError) { + NS_WARNING("Failed to wrap ClonedErrorHolder"); + MOZ_DIAGNOSTIC_ASSERT(false, "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_ASSERT(false, "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_ASSERT(false, "Unexpected API request ResultType"); + return false; +} + +void RequestWorkerRunnable::ReadResult(JSContext* aCx, + JS::MutableHandle<JS::Value> aResult, + ErrorResult& aRv) { + MOZ_ASSERT(mWorkerPrivate->IsOnCurrentThread()); + 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_ASSERT(false, "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()); + + auto* baseURI = mWorkerPrivate->GetBaseURI(); + RefPtr<WebExtensionPolicy> policy = + ExtensionPolicyService::GetSingleton().GetByURL(baseURI); + + RefPtr<ExtensionServiceWorkerInfo> swInfo = new ExtensionServiceWorkerInfo( + *mClientInfo, mWorkerPrivate->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 |