/* -*- 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 "nsThreadUtils.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/RootedDictionary.h" #include "mozilla/dom/UniFFICall.h" namespace mozilla::uniffi { using dom::GlobalObject; using dom::RootedDictionary; using dom::Sequence; using dom::UniFFIScaffoldingCallResult; using dom::UniFFIScaffoldingValue; void UniffiHandlerBase::CallSync( UniquePtr aHandler, const GlobalObject& aGlobal, const Sequence& aArgs, RootedDictionary& aReturnValue, ErrorResult& aError) { aHandler->PrepareRustArgs(aArgs, aError); if (aError.Failed()) { return; } aHandler->MakeRustCall(); aHandler->ExtractCallResult(aGlobal.Context(), aReturnValue, aError); } already_AddRefed UniffiHandlerBase::CallAsync( UniquePtr aHandler, const dom::GlobalObject& aGlobal, const dom::Sequence& aArgs, ErrorResult& aError) { aHandler->PrepareRustArgs(aArgs, aError); if (aError.Failed()) { return nullptr; } // Create the promise that we return to JS nsCOMPtr xpcomGlobal = do_QueryInterface(aGlobal.GetAsSupports()); RefPtr returnPromise = dom::Promise::Create(xpcomGlobal, aError); if (aError.Failed()) { return nullptr; } // Create a second promise that gets resolved by a background task that // calls the scaffolding function RefPtr taskPromise = new MozPromise, nsresult, true>::Private( __func__); nsresult dispatchResult = NS_DispatchBackgroundTask( NS_NewRunnableFunction( __func__, [handler = std::move(aHandler), taskPromise]() mutable { handler->MakeRustCall(); taskPromise->Resolve(std::move(handler), __func__); }), NS_DISPATCH_EVENT_MAY_BLOCK); if (NS_FAILED(dispatchResult)) { taskPromise->Reject(dispatchResult, __func__); } // When the background task promise completes, resolve the JS promise taskPromise->Then( GetCurrentSerialEventTarget(), __func__, [xpcomGlobal, returnPromise]( typename MozPromise, nsresult, true>::ResolveOrRejectValue&& aResult) { if (!aResult.IsResolve()) { returnPromise->MaybeRejectWithUnknownError(__func__); return; } auto handler = std::move(aResult.ResolveValue()); dom::AutoEntryScript aes(xpcomGlobal, __func__); dom::RootedDictionary returnValue( aes.cx()); ErrorResult error; handler->ExtractCallResult(aes.cx(), returnValue, error); error.WouldReportJSException(); if (error.Failed()) { returnPromise->MaybeReject(std::move(error)); } else { returnPromise->MaybeResolve(returnValue); } }); // Return the JS promise, using forget() to convert it to already_AddRefed return returnPromise.forget(); } void UniffiHandlerBase::ExtractCallResult( JSContext* aCx, dom::RootedDictionary& aDest, ErrorResult& aError) { switch (mUniffiCallStatusCode) { case RUST_CALL_SUCCESS: { aDest.mCode = dom::UniFFIScaffoldingCallCode::Success; ExtractSuccessfulCallResult(aCx, aDest.mData, aError); break; } case RUST_CALL_ERROR: { // Rust Err() value. Populate data with the `RustBuffer` containing the // error aDest.mCode = dom::UniFFIScaffoldingCallCode::Error; JS::Rooted obj(aCx); mUniffiCallStatusErrorBuf.IntoArrayBuffer(aCx, &obj, aError); if (aError.Failed()) { break; } aDest.mData.Construct().SetAsArrayBuffer().Init(obj); break; } default: { // This indicates a RustError, which should rarely happen in practice. // The normal case is a Rust panic, but FF sets panic=abort. aDest.mCode = dom::UniFFIScaffoldingCallCode::Internal_error; if (mUniffiCallStatusErrorBuf.IsValid()) { JS::Rooted obj(aCx); mUniffiCallStatusErrorBuf.IntoArrayBuffer(aCx, &obj, aError); if (aError.Failed()) { break; } aDest.mData.Construct().SetAsArrayBuffer().Init(obj); } break; } } } } // namespace mozilla::uniffi