forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			709 lines
		
	
	
	
		
			23 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			709 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 "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
 |