/* 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 "ExtensionAPIBase.h" #include "ExtensionAPIRequestForwarder.h" #include "ExtensionAPIAddRemoveListener.h" #include "ExtensionAPICallAsyncFunction.h" #include "ExtensionAPICallFunctionNoReturn.h" #include "ExtensionAPICallSyncFunction.h" #include "ExtensionAPIGetProperty.h" #include "ExtensionEventManager.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/SerializedStackHolder.h" #include "mozilla/dom/FunctionBinding.h" namespace mozilla { namespace extensions { // ChromeCompatCallbackHandler NS_IMPL_ISUPPORTS0(ChromeCompatCallbackHandler) // static void ChromeCompatCallbackHandler::Create( dom::Promise* aPromise, const RefPtr& aCallback) { MOZ_ASSERT(aPromise); MOZ_ASSERT(aCallback); RefPtr handler = new ChromeCompatCallbackHandler(aCallback); aPromise->AppendNativeHandler(handler); } void ChromeCompatCallbackHandler::ResolvedCallback( JSContext* aCx, JS::Handle aValue) { JS::RootedValue retval(aCx); IgnoredErrorResult rv; MOZ_KnownLive(mCallback)->Call({aValue}, &retval, rv); } void ChromeCompatCallbackHandler::RejectedCallback( JSContext* aCx, JS::Handle aValue) { JS::RootedValue retval(aCx); IgnoredErrorResult rv; // Call the chrome-compatible callback without any parameter, the errors // isn't passed to the callback as a parameter but the extension will be // able to retrieve it from chrome.runtime.lastError. MOZ_KnownLive(mCallback)->Call({}, &retval, rv); } // WebExtensionStub methods shared between multiple API namespaces. void ExtensionAPIBase::CallWebExtMethodNotImplementedNoReturn( JSContext* aCx, const nsAString& aApiMethod, const dom::Sequence& aArgs, ErrorResult& aRv) { aRv.ThrowNotSupportedError("Not implemented"); } void ExtensionAPIBase::CallWebExtMethodNotImplementedAsync( JSContext* aCx, const nsAString& aApiMethod, const dom::Sequence& aArgs, const dom::Optional>& aCallback, JS::MutableHandle aRetval, ErrorResult& aRv) { CallWebExtMethodNotImplementedNoReturn(aCx, aApiMethod, aArgs, aRv); } void ExtensionAPIBase::CallWebExtMethodNotImplemented( JSContext* aCx, const nsAString& aApiMethod, const dom::Sequence& aArgs, JS::MutableHandle aRetval, ErrorResult& aRv) { CallWebExtMethodNotImplementedNoReturn(aCx, aApiMethod, aArgs, aRv); } void ExtensionAPIBase::CallWebExtMethodNoReturn( JSContext* aCx, const nsAString& aApiMethod, const dom::Sequence& aArgs, ErrorResult& aRv) { auto request = CallFunctionNoReturn(aApiMethod); request->Run(GetGlobalObject(), aCx, aArgs, aRv); if (aRv.Failed()) { return; } } void ExtensionAPIBase::CallWebExtMethod(JSContext* aCx, const nsAString& aApiMethod, const dom::Sequence& aArgs, JS::MutableHandle aRetVal, ErrorResult& aRv) { auto request = CallSyncFunction(aApiMethod); request->Run(GetGlobalObject(), aCx, aArgs, aRetVal, aRv); if (aRv.Failed()) { return; } } void ExtensionAPIBase::CallWebExtMethodAsyncInternal( JSContext* aCx, const nsAString& aApiMethod, const dom::Sequence& aArgs, const RefPtr& aCallback, JS::MutableHandle aRetval, ErrorResult& aRv) { auto* global = GetGlobalObject(); IgnoredErrorResult erv; RefPtr domPromise = dom::Promise::Create(global, erv); if (NS_WARN_IF(erv.Failed())) { ThrowUnexpectedError(aCx, aRv); return; } MOZ_ASSERT(domPromise); auto request = CallAsyncFunction(aApiMethod); request->Run(global, aCx, aArgs, domPromise, aRv); if (aRv.Failed()) { return; } // The async method has been called with the chrome-compatible callback // convention. if (aCallback) { ChromeCompatCallbackHandler::Create(domPromise, aCallback); return; } if (NS_WARN_IF(!ToJSValue(aCx, domPromise, aRetval))) { ThrowUnexpectedError(aCx, aRv); return; } } void ExtensionAPIBase::CallWebExtMethodAsync( JSContext* aCx, const nsAString& aApiMethod, const dom::Sequence& aArgs, const dom::Optional>& aCallback, JS::MutableHandle aRetval, ErrorResult& aRv) { RefPtr callback = nullptr; if (aCallback.WasPassed()) { callback = &aCallback.Value(); } CallWebExtMethodAsyncInternal(aCx, aApiMethod, aArgs, callback, aRetval, aRv); } void ExtensionAPIBase::CallWebExtMethodAsyncAmbiguous( JSContext* aCx, const nsAString& aApiMethod, const dom::Sequence& aArgs, JS::MutableHandle aRetval, ErrorResult& aRv) { RefPtr chromeCompatCb; auto lastElement = aArgs.IsEmpty() ? JS::UndefinedValue() : aArgs.LastElement(); dom::Sequence callArgs(aArgs); if (lastElement.isObject() && JS::IsCallable(&lastElement.toObject())) { JS::Rooted tempRoot(aCx, &lastElement.toObject()); JS::Rooted tempGlobalRoot(aCx, JS::CurrentGlobalOrNull(aCx)); chromeCompatCb = new dom::Function(aCx, tempRoot, tempGlobalRoot, dom::GetIncumbentGlobal()); Unused << callArgs.PopLastElement(); } CallWebExtMethodAsyncInternal(aCx, aApiMethod, callArgs, chromeCompatCb, aRetval, aRv); } // ExtensionAPIBase - API Request helpers already_AddRefed ExtensionAPIBase::CreateEventManager( const nsAString& aEventName) { RefPtr eventMgr = new ExtensionEventManager( GetGlobalObject(), GetAPINamespace(), aEventName, GetAPIObjectType(), GetAPIObjectId()); return eventMgr.forget(); } RefPtr ExtensionAPIBase::CallFunctionNoReturn( const nsAString& aApiMethod) { return new ExtensionAPICallFunctionNoReturn( GetAPINamespace(), aApiMethod, GetAPIObjectType(), GetAPIObjectId()); } RefPtr ExtensionAPIBase::CallSyncFunction( const nsAString& aApiMethod) { return new ExtensionAPICallSyncFunction(GetAPINamespace(), aApiMethod, GetAPIObjectType(), GetAPIObjectId()); } RefPtr ExtensionAPIBase::CallAsyncFunction( const nsAString& aApiMethod) { return new ExtensionAPICallAsyncFunction( GetAPINamespace(), aApiMethod, GetAPIObjectType(), GetAPIObjectId()); } RefPtr ExtensionAPIBase::GetProperty( const nsAString& aApiProperty) { return new ExtensionAPIGetProperty(GetAPINamespace(), aApiProperty, GetAPIObjectType(), GetAPIObjectId()); } RefPtr ExtensionAPIBase::SendAddListener( const nsAString& aEventName) { using EType = ExtensionAPIAddRemoveListener::EType; return new ExtensionAPIAddRemoveListener( EType::eAddListener, GetAPINamespace(), aEventName, GetAPIObjectType(), GetAPIObjectId()); } RefPtr ExtensionAPIBase::SendRemoveListener( const nsAString& aEventName) { using EType = ExtensionAPIAddRemoveListener::EType; return new ExtensionAPIAddRemoveListener( EType::eRemoveListener, GetAPINamespace(), aEventName, GetAPIObjectType(), GetAPIObjectId()); } // static void ExtensionAPIBase::ThrowUnexpectedError(JSContext* aCx, ErrorResult& aRv) { ExtensionAPIRequestForwarder::ThrowUnexpectedError(aCx, aRv); } } // namespace extensions } // namespace mozilla