/* 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 "mozilla/dom/WebIdentityHandler.h" #include "mozilla/dom/IdentityCredential.h" #include "mozilla/dom/WindowGlobalChild.h" #include "nsCycleCollectionParticipant.h" namespace mozilla::dom { NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebIdentityHandler) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTION(WebIdentityHandler, mWindow, mGetPromise) NS_IMPL_CYCLE_COLLECTING_ADDREF(WebIdentityHandler) NS_IMPL_CYCLE_COLLECTING_RELEASE(WebIdentityHandler) WebIdentityHandler::~WebIdentityHandler() { MOZ_ASSERT(NS_IsMainThread()); if (mActor) { if (mGetPromise) { mGetPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); mGetPromise = nullptr; Unfollow(); } mActor->SetHandler(nullptr); mWindow = nullptr; } } bool WebIdentityHandler::MaybeCreateActor() { if (mActor) { return true; } RefPtr actor = new WebIdentityChild(); WindowGlobalChild* windowGlobalChild = mWindow->GetWindowGlobalChild(); if (!windowGlobalChild || !windowGlobalChild->SendPWebIdentityConstructor(actor)) { return false; } mActor = actor; mActor->SetHandler(this); return true; } void WebIdentityHandler::GetCredential(const CredentialRequestOptions& aOptions, bool aSameOriginWithAncestors, const RefPtr& aPromise) { MOZ_ASSERT(XRE_IsContentProcess()); MOZ_ASSERT(mWindow); MOZ_ASSERT(aPromise); MOZ_ASSERT(aOptions.mIdentity.WasPassed()); // Prevent origin confusion by requiring no cross domain iframes // in this one's ancestry if (!aSameOriginWithAncestors) { aPromise->MaybeRejectWithNotAllowedError("Same origin ancestors only."); return; } if (!mActor) { aPromise->MaybeRejectWithUnknownError("Unknown failure"); return; } if (mGetPromise) { aPromise->MaybeRejectWithNotAllowedError( "Concurrent requests not allowed."); return; } mGetPromise = aPromise; RefPtr self(this); mActor ->SendGetIdentityCredential( aOptions.mIdentity.Value(), aOptions.mMediation, mWindow->GetWindowContext() && mWindow->GetWindowContext() ->HasValidTransientUserGestureActivation()) ->Then( GetCurrentSerialEventTarget(), __func__, [self](const WebIdentityChild::GetIdentityCredentialPromise:: ResolveValueType& aResult) { if (self->mGetPromise) { if (aResult.type() == WebIdentityGetCredentialResponse::TIPCIdentityCredential) { const IPCIdentityCredential& result = aResult.get_IPCIdentityCredential(); self->mGetPromise->MaybeResolve( new IdentityCredential(self->mWindow, result)); self->mGetPromise = nullptr; self->Unfollow(); } else { self->mGetPromise->MaybeRejectWithNetworkError( "Failure to gather the credential"); self->mGetPromise = nullptr; self->Unfollow(); } } }, [self](const WebIdentityChild::GetIdentityCredentialPromise:: RejectValueType& aResult) { if (self->mGetPromise) { self->mGetPromise->MaybeRejectWithOperationError(""); self->mGetPromise = nullptr; self->Unfollow(); } }); } void WebIdentityHandler::PreventSilentAccess(const RefPtr& aPromise) { if (!mActor) { aPromise->MaybeRejectWithUnknownError("Unknown failure"); return; } mActor->SendPreventSilentAccess()->Then( GetCurrentSerialEventTarget(), __func__, [aPromise](const WebIdentityChild::PreventSilentAccessPromise:: ResolveOrRejectValue& unused) { aPromise->MaybeResolveWithUndefined(); }); } void WebIdentityHandler::Disconnect( const IdentityCredentialDisconnectOptions& aOptions, const RefPtr& aPromise) { if (!mActor) { aPromise->MaybeRejectWithUnknownError("Unknown failure"); return; } mActor->SendDisconnectIdentityCredential(aOptions)->Then( GetCurrentSerialEventTarget(), __func__, [aPromise](nsresult aResult) { if (aResult == NS_ERROR_DOM_MALFORMED_URI) { aPromise->MaybeRejectWithInvalidStateError( "Error parsing the provided URI"); } else if (NS_FAILED(aResult)) { aPromise->MaybeRejectWithNetworkError( "Error sending disconnect request"); } else { aPromise->MaybeResolveWithUndefined(); } }, [aPromise](mozilla::ipc::ResponseRejectReason aError) { aPromise->MaybeRejectWithUnknownError("Unknown failure"); }); } void WebIdentityHandler::SetLoginStatus(const LoginStatus& aStatus, const RefPtr& aPromise) { const RefPtr& promise = aPromise; if (!mActor) { promise->MaybeRejectWithUnknownError( "navigator.login.setStatus had an unexpected internal error"); return; } mActor->SendSetLoginStatus(aStatus)->Then( GetCurrentSerialEventTarget(), __func__, [promise](const WebIdentityChild::SetLoginStatusPromise::ResolveValueType& aResult) { if (NS_SUCCEEDED(aResult)) { promise->MaybeResolveWithUndefined(); } else { promise->MaybeRejectWithUnknownError( "navigator.login.setStatus had an unexpected internal error"); } }, [promise](const WebIdentityChild::SetLoginStatusPromise::RejectValueType& aResult) { promise->MaybeRejectWithUnknownError( "navigator.login.setStatus had an unexpected internal error"); }); } RefPtr> WebIdentityHandler::ResolveContinuationWindow( const nsACString& aToken, const IdentityResolveOptions& aOptions) { if (!mActor) { return MozPromise::CreateAndReject( NS_ERROR_UNEXPECTED, __func__); } // Tell the parent process that we want to resolve with a given token and // options. The main process will infer what popup we are, and find the // pending promise. RefPtr::Private> promise = new MozPromise::Private(__func__); mActor->SendResolveContinuationWindow(aToken, aOptions) ->Then( GetCurrentSerialEventTarget(), __func__, [promise](const WebIdentityChild::ResolveContinuationWindowPromise:: ResolveValueType& aResult) { // Only resolve on success if (NS_SUCCEEDED(aResult)) { promise->Resolve(aResult, __func__); } else { promise->Reject(aResult, __func__); } }, [promise](const WebIdentityChild::ResolveContinuationWindowPromise:: RejectValueType& aResult) { // Fall back to a not allowed error when IPC fails. promise->Reject(nsresult::NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__); }); return promise.forget(); } RefPtr> WebIdentityHandler::IsContinuationWindow() { if (!mActor) { return MozPromise::CreateAndReject( NS_ERROR_UNEXPECTED, __func__); } RefPtr::Private> promise = new MozPromise::Private(__func__); mActor->SendIsActiveContinuationWindow()->Then( GetCurrentSerialEventTarget(), __func__, [promise](bool result) { promise->Resolve(result, __func__); }, [promise](mozilla::ipc::ResponseRejectReason reject) { promise->Resolve(false, __func__); }); return promise.forget(); } void WebIdentityHandler::ActorDestroyed() { MOZ_ASSERT(NS_IsMainThread()); mActor = nullptr; } void WebIdentityHandler::RunAbortAlgorithm() { if (!mGetPromise) { return; } nsCOMPtr global = do_QueryInterface(mWindow); AutoJSAPI jsapi; if (!jsapi.Init(global)) { mGetPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); mGetPromise = nullptr; Unfollow(); return; } JSContext* cx = jsapi.cx(); JS::Rooted reason(cx); Signal()->GetReason(cx, &reason); mGetPromise->MaybeReject(reason); mGetPromise = nullptr; Unfollow(); } } // namespace mozilla::dom