/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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 #include "nsCOMPtr.h" #include "nsIPrincipal.h" #include "mozilla/dom/BrowserChild.h" #include "mozilla/dom/BrowserParent.h" #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/Event.h" #include "mozilla/dom/PContentPermission.h" #include "mozilla/dom/PermissionMessageUtils.h" #include "mozilla/dom/PContentPermissionRequestParent.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/Attributes.h" #include "mozilla/Preferences.h" #include "mozilla/Unused.h" #include "nsComponentManagerUtils.h" #include "nsArrayUtils.h" #include "nsIMutableArray.h" #include "nsContentPermissionHelper.h" #include "nsJSUtils.h" #include "nsISupportsPrimitives.h" #include "nsServiceManagerUtils.h" #include "mozilla/dom/Document.h" #include "nsIWeakReferenceUtils.h" #include "js/PropertyAndElement.h" // JS_GetProperty, JS_SetProperty using mozilla::Unused; // using namespace mozilla::dom; using namespace mozilla; using DelegateInfo = PermissionDelegateHandler::PermissionDelegateInfo; namespace mozilla::dom { class ContentPermissionRequestParent : public PContentPermissionRequestParent { public: // @param aIsRequestDelegatedToUnsafeThirdParty see // mIsRequestDelegatedToUnsafeThirdParty. ContentPermissionRequestParent( const nsTArray& aRequests, Element* aElement, nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal, const bool aHasValidTransientUserGestureActivation, const bool aIsRequestDelegatedToUnsafeThirdParty); virtual ~ContentPermissionRequestParent(); bool IsBeingDestroyed(); nsCOMPtr mPrincipal; nsCOMPtr mTopLevelPrincipal; nsCOMPtr mElement; bool mHasValidTransientUserGestureActivation; // See nsIPermissionDelegateHandler.maybeUnsafePermissionDelegate. bool mIsRequestDelegatedToUnsafeThirdParty; RefPtr mProxy; nsTArray mRequests; private: // Not MOZ_CAN_RUN_SCRIPT because we can't annotate the thing we override yet. MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual mozilla::ipc::IPCResult Recvprompt() override; virtual mozilla::ipc::IPCResult RecvDestroy() override; virtual void ActorDestroy(ActorDestroyReason why) override; }; ContentPermissionRequestParent::ContentPermissionRequestParent( const nsTArray& aRequests, Element* aElement, nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal, const bool aHasValidTransientUserGestureActivation, const bool aIsRequestDelegatedToUnsafeThirdParty) { MOZ_COUNT_CTOR(ContentPermissionRequestParent); mPrincipal = aPrincipal; mTopLevelPrincipal = aTopLevelPrincipal; mElement = aElement; mRequests = aRequests.Clone(); mHasValidTransientUserGestureActivation = aHasValidTransientUserGestureActivation; mIsRequestDelegatedToUnsafeThirdParty = aIsRequestDelegatedToUnsafeThirdParty; } ContentPermissionRequestParent::~ContentPermissionRequestParent() { MOZ_COUNT_DTOR(ContentPermissionRequestParent); } mozilla::ipc::IPCResult ContentPermissionRequestParent::Recvprompt() { mProxy = new nsContentPermissionRequestProxy(this); if (NS_FAILED(mProxy->Init(mRequests))) { RefPtr proxy(mProxy); proxy->Cancel(); } return IPC_OK(); } mozilla::ipc::IPCResult ContentPermissionRequestParent::RecvDestroy() { Unused << PContentPermissionRequestParent::Send__delete__(this); return IPC_OK(); } void ContentPermissionRequestParent::ActorDestroy(ActorDestroyReason why) { if (mProxy) { mProxy->OnParentDestroyed(); } } bool ContentPermissionRequestParent::IsBeingDestroyed() { // When ContentParent::MarkAsDead() is called, we are being destroyed. // It's unsafe to send out any message now. ContentParent* contentParent = static_cast(Manager()); return !contentParent->IsAlive(); } NS_IMPL_ISUPPORTS(ContentPermissionType, nsIContentPermissionType) ContentPermissionType::ContentPermissionType( const nsACString& aType, const nsTArray& aOptions) { mType = aType; mOptions = aOptions.Clone(); } ContentPermissionType::~ContentPermissionType() = default; NS_IMETHODIMP ContentPermissionType::GetType(nsACString& aType) { aType = mType; return NS_OK; } NS_IMETHODIMP ContentPermissionType::GetOptions(nsIArray** aOptions) { NS_ENSURE_ARG_POINTER(aOptions); *aOptions = nullptr; nsresult rv; nsCOMPtr options = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); // copy options into JS array for (uint32_t i = 0; i < mOptions.Length(); ++i) { nsCOMPtr isupportsString = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = isupportsString->SetData(mOptions[i]); NS_ENSURE_SUCCESS(rv, rv); rv = options->AppendElement(isupportsString); NS_ENSURE_SUCCESS(rv, rv); } options.forget(aOptions); return NS_OK; } // nsContentPermissionUtils /* static */ uint32_t nsContentPermissionUtils::ConvertPermissionRequestToArray( nsTArray& aSrcArray, nsIMutableArray* aDesArray) { uint32_t len = aSrcArray.Length(); for (uint32_t i = 0; i < len; i++) { RefPtr cpt = new ContentPermissionType(aSrcArray[i].type(), aSrcArray[i].options()); aDesArray->AppendElement(cpt); } return len; } /* static */ void nsContentPermissionUtils::ConvertArrayToPermissionRequest( nsIArray* aSrcArray, nsTArray& aDesArray) { uint32_t len = 0; aSrcArray->GetLength(&len); for (uint32_t i = 0; i < len; i++) { nsCOMPtr cpt = do_QueryElementAt(aSrcArray, i); nsAutoCString type; cpt->GetType(type); nsCOMPtr optionArray; cpt->GetOptions(getter_AddRefs(optionArray)); uint32_t optionsLength = 0; if (optionArray) { optionArray->GetLength(&optionsLength); } nsTArray options; for (uint32_t j = 0; j < optionsLength; ++j) { nsCOMPtr isupportsString = do_QueryElementAt(optionArray, j); if (isupportsString) { nsString option; isupportsString->GetData(option); options.AppendElement(option); } } aDesArray.AppendElement(PermissionRequest(type, options)); } } static std::map& ContentPermissionRequestParentMap() { MOZ_ASSERT(NS_IsMainThread()); static std::map sPermissionRequestParentMap; return sPermissionRequestParentMap; } static std::map& ContentPermissionRequestChildMap() { MOZ_ASSERT(NS_IsMainThread()); static std::map sPermissionRequestChildMap; return sPermissionRequestChildMap; } /* static */ nsresult nsContentPermissionUtils::CreatePermissionArray( const nsACString& aType, const nsTArray& aOptions, nsIArray** aTypesArray) { nsCOMPtr types = do_CreateInstance(NS_ARRAY_CONTRACTID); RefPtr permType = new ContentPermissionType(aType, aOptions); types->AppendElement(permType); types.forget(aTypesArray); return NS_OK; } /* static */ PContentPermissionRequestParent* nsContentPermissionUtils::CreateContentPermissionRequestParent( const nsTArray& aRequests, Element* aElement, nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal, const bool aHasValidTransientUserGestureActivation, const bool aIsRequestDelegatedToUnsafeThirdParty, const TabId& aTabId) { PContentPermissionRequestParent* parent = new ContentPermissionRequestParent( aRequests, aElement, aPrincipal, aTopLevelPrincipal, aHasValidTransientUserGestureActivation, aIsRequestDelegatedToUnsafeThirdParty); ContentPermissionRequestParentMap()[parent] = aTabId; return parent; } /* static */ nsresult nsContentPermissionUtils::AskPermission( nsIContentPermissionRequest* aRequest, nsPIDOMWindowInner* aWindow) { NS_ENSURE_STATE(aWindow && aWindow->IsCurrentInnerWindow()); // for content process if (XRE_IsContentProcess()) { RefPtr req = new RemotePermissionRequest(aRequest, aWindow); MOZ_ASSERT(NS_IsMainThread()); // IPC can only be execute on main thread. BrowserChild* child = BrowserChild::GetFrom(aWindow->GetDocShell()); NS_ENSURE_TRUE(child, NS_ERROR_FAILURE); nsCOMPtr typeArray; nsresult rv = aRequest->GetTypes(getter_AddRefs(typeArray)); NS_ENSURE_SUCCESS(rv, rv); nsTArray permArray; ConvertArrayToPermissionRequest(typeArray, permArray); nsCOMPtr principal; rv = aRequest->GetPrincipal(getter_AddRefs(principal)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr topLevelPrincipal; rv = aRequest->GetTopLevelPrincipal(getter_AddRefs(topLevelPrincipal)); NS_ENSURE_SUCCESS(rv, rv); bool hasValidTransientUserGestureActivation; rv = aRequest->GetHasValidTransientUserGestureActivation( &hasValidTransientUserGestureActivation); NS_ENSURE_SUCCESS(rv, rv); bool isRequestDelegatedToUnsafeThirdParty; rv = aRequest->GetIsRequestDelegatedToUnsafeThirdParty( &isRequestDelegatedToUnsafeThirdParty); NS_ENSURE_SUCCESS(rv, rv); req->IPDLAddRef(); ContentChild::GetSingleton()->SendPContentPermissionRequestConstructor( req, permArray, principal, topLevelPrincipal, hasValidTransientUserGestureActivation, isRequestDelegatedToUnsafeThirdParty, child->GetTabId()); ContentPermissionRequestChildMap()[req.get()] = child->GetTabId(); req->Sendprompt(); return NS_OK; } // for chrome process nsCOMPtr prompt = do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID); if (prompt) { if (NS_FAILED(prompt->Prompt(aRequest))) { return NS_ERROR_FAILURE; } } return NS_OK; } /* static */ nsTArray nsContentPermissionUtils::GetContentPermissionRequestParentById( const TabId& aTabId) { nsTArray parentArray; for (auto& it : ContentPermissionRequestParentMap()) { if (it.second == aTabId) { parentArray.AppendElement(it.first); } } return parentArray; } /* static */ void nsContentPermissionUtils::NotifyRemoveContentPermissionRequestParent( PContentPermissionRequestParent* aParent) { auto it = ContentPermissionRequestParentMap().find(aParent); MOZ_ASSERT(it != ContentPermissionRequestParentMap().end()); ContentPermissionRequestParentMap().erase(it); } /* static */ nsTArray nsContentPermissionUtils::GetContentPermissionRequestChildById( const TabId& aTabId) { nsTArray childArray; for (auto& it : ContentPermissionRequestChildMap()) { if (it.second == aTabId) { childArray.AppendElement(it.first); } } return childArray; } /* static */ void nsContentPermissionUtils::NotifyRemoveContentPermissionRequestChild( PContentPermissionRequestChild* aChild) { auto it = ContentPermissionRequestChildMap().find(aChild); MOZ_ASSERT(it != ContentPermissionRequestChildMap().end()); ContentPermissionRequestChildMap().erase(it); } static nsIPrincipal* GetTopLevelPrincipal(nsPIDOMWindowInner* aWindow) { MOZ_ASSERT(aWindow); BrowsingContext* top = aWindow->GetBrowsingContext()->Top(); MOZ_ASSERT(top); nsPIDOMWindowOuter* outer = top->GetDOMWindow(); if (!outer) { return nullptr; } nsPIDOMWindowInner* inner = outer->GetCurrentInnerWindow(); if (!inner) { return nullptr; } return nsGlobalWindowInner::Cast(inner)->GetPrincipal(); } NS_IMPL_CYCLE_COLLECTION(ContentPermissionRequestBase, mPrincipal, mTopLevelPrincipal, mWindow) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ContentPermissionRequestBase) NS_INTERFACE_MAP_ENTRY_CONCRETE(nsISupports) NS_INTERFACE_MAP_ENTRY_CONCRETE(nsIContentPermissionRequest) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(ContentPermissionRequestBase) NS_IMPL_CYCLE_COLLECTING_RELEASE(ContentPermissionRequestBase) ContentPermissionRequestBase::ContentPermissionRequestBase( nsIPrincipal* aPrincipal, nsPIDOMWindowInner* aWindow, const nsACString& aPrefName, const nsACString& aType) : mPrincipal(aPrincipal), mTopLevelPrincipal(aWindow ? ::GetTopLevelPrincipal(aWindow) : nullptr), mWindow(aWindow), mPrefName(aPrefName), mType(aType), mHasValidTransientUserGestureActivation(false), mIsRequestDelegatedToUnsafeThirdParty(false) { if (!aWindow) { return; } Document* doc = aWindow->GetExtantDoc(); if (!doc) { return; } mHasValidTransientUserGestureActivation = doc->HasValidTransientUserGestureActivation(); mPermissionHandler = doc->GetPermissionDelegateHandler(); if (mPermissionHandler) { nsTArray types; types.AppendElement(mType); mPermissionHandler->MaybeUnsafePermissionDelegate( types, &mIsRequestDelegatedToUnsafeThirdParty); } } NS_IMETHODIMP ContentPermissionRequestBase::GetPrincipal( nsIPrincipal** aRequestingPrincipal) { NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal); return NS_OK; } NS_IMETHODIMP ContentPermissionRequestBase::GetDelegatePrincipal( const nsACString& aType, nsIPrincipal** aRequestingPrincipal) { return PermissionDelegateHandler::GetDelegatePrincipal(aType, this, aRequestingPrincipal); } NS_IMETHODIMP ContentPermissionRequestBase::GetIsRequestDelegatedToUnsafeThirdParty( bool* aIsRequestDelegatedToUnsafeThirdParty) { *aIsRequestDelegatedToUnsafeThirdParty = mIsRequestDelegatedToUnsafeThirdParty; return NS_OK; } NS_IMETHODIMP ContentPermissionRequestBase::GetTopLevelPrincipal( nsIPrincipal** aRequestingPrincipal) { if (!mTopLevelPrincipal) { *aRequestingPrincipal = nullptr; return NS_OK; } NS_IF_ADDREF(*aRequestingPrincipal = mTopLevelPrincipal); return NS_OK; } NS_IMETHODIMP ContentPermissionRequestBase::GetWindow(mozIDOMWindow** aRequestingWindow) { NS_IF_ADDREF(*aRequestingWindow = mWindow); return NS_OK; } NS_IMETHODIMP ContentPermissionRequestBase::GetElement(Element** aElement) { NS_ENSURE_ARG_POINTER(aElement); *aElement = nullptr; return NS_OK; } NS_IMETHODIMP ContentPermissionRequestBase::GetHasValidTransientUserGestureActivation( bool* aHasValidTransientUserGestureActivation) { *aHasValidTransientUserGestureActivation = mHasValidTransientUserGestureActivation; return NS_OK; } NS_IMETHODIMP ContentPermissionRequestBase::GetTypes(nsIArray** aTypes) { nsTArray emptyOptions; return nsContentPermissionUtils::CreatePermissionArray(mType, emptyOptions, aTypes); } ContentPermissionRequestBase::PromptResult ContentPermissionRequestBase::CheckPromptPrefs() const { MOZ_ASSERT(!mPrefName.IsEmpty(), "This derived class must support checking pref types"); nsAutoCString prefName(mPrefName); prefName.AppendLiteral(".prompt.testing"); if (Preferences::GetBool(PromiseFlatCString(prefName).get(), false)) { prefName.AppendLiteral(".allow"); if (Preferences::GetBool(PromiseFlatCString(prefName).get(), true)) { return PromptResult::Granted; } return PromptResult::Denied; } return PromptResult::Pending; } bool ContentPermissionRequestBase::CheckPermissionDelegate() const { // There is case that ContentPermissionRequestBase is constructed without // window, then mPermissionHandler will be null. So we only check permission // delegate if we have non-null mPermissionHandler if (mPermissionHandler && !mPermissionHandler->HasPermissionDelegated(mType)) { return false; } return true; } nsresult ContentPermissionRequestBase::ShowPrompt( ContentPermissionRequestBase::PromptResult& aResult) { if (!CheckPermissionDelegate()) { aResult = PromptResult::Denied; return NS_OK; } aResult = CheckPromptPrefs(); if (aResult != PromptResult::Pending) { return NS_OK; } return nsContentPermissionUtils::AskPermission(this, mWindow); } class RequestPromptEvent : public Runnable { public: RequestPromptEvent(ContentPermissionRequestBase* aRequest, nsPIDOMWindowInner* aWindow) : mozilla::Runnable("RequestPromptEvent"), mRequest(aRequest), mWindow(aWindow) {} NS_IMETHOD Run() override { nsContentPermissionUtils::AskPermission(mRequest, mWindow); return NS_OK; } private: RefPtr mRequest; nsCOMPtr mWindow; }; class RequestAllowEvent : public Runnable { public: RequestAllowEvent(bool allow, ContentPermissionRequestBase* request) : mozilla::Runnable("RequestAllowEvent"), mAllow(allow), mRequest(request) {} // Not MOZ_CAN_RUN_SCRIPT because we can't annotate the thing we override yet. MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override { // MOZ_KnownLive is OK, because we never drop the ref to mRequest. if (mAllow) { MOZ_KnownLive(mRequest)->Allow(JS::UndefinedHandleValue); } else { MOZ_KnownLive(mRequest)->Cancel(); } return NS_OK; } private: bool mAllow; RefPtr mRequest; }; void ContentPermissionRequestBase::RequestDelayedTask( nsIEventTarget* aTarget, ContentPermissionRequestBase::DelayedTaskType aType) { nsCOMPtr r; switch (aType) { case DelayedTaskType::Allow: r = new RequestAllowEvent(true, this); break; case DelayedTaskType::Deny: r = new RequestAllowEvent(false, this); break; default: r = new RequestPromptEvent(this, mWindow); break; } aTarget->Dispatch(r.forget()); } nsresult TranslateChoices( JS::Handle aChoices, const nsTArray& aPermissionRequests, nsTArray& aTranslatedChoices) { if (aChoices.isNullOrUndefined()) { // No choice is specified. } else if (aChoices.isObject()) { // Iterate through all permission types. for (uint32_t i = 0; i < aPermissionRequests.Length(); ++i) { nsCString type = aPermissionRequests[i].type(); JS::Rooted obj(RootingCx(), &aChoices.toObject()); // People really shouldn't be passing WindowProxy or Location // objects for the choices here. obj = js::CheckedUnwrapStatic(obj); if (!obj) { return NS_ERROR_FAILURE; } AutoJSAPI jsapi; jsapi.Init(); JSContext* cx = jsapi.cx(); JSAutoRealm ar(cx, obj); JS::Rooted val(cx); if (!JS_GetProperty(cx, obj, type.BeginReading(), &val) || !val.isString()) { // no setting for the permission type, clear exception and skip it jsapi.ClearException(); } else { nsAutoJSString choice; if (!choice.init(cx, val)) { jsapi.ClearException(); return NS_ERROR_FAILURE; } aTranslatedChoices.AppendElement(PermissionChoice(type, choice)); } } } else { MOZ_ASSERT(false, "SelectedChoices should be undefined or an JS object"); return NS_ERROR_FAILURE; } return NS_OK; } } // namespace mozilla::dom nsContentPermissionRequestProxy::nsContentPermissionRequestProxy( ContentPermissionRequestParent* parent) : mParent(parent) { NS_ASSERTION(mParent, "null parent"); } nsContentPermissionRequestProxy::~nsContentPermissionRequestProxy() = default; nsresult nsContentPermissionRequestProxy::Init( const nsTArray& requests) { mPermissionRequests = requests.Clone(); nsCOMPtr prompt = do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID); if (!prompt) { return NS_ERROR_FAILURE; } prompt->Prompt(this); return NS_OK; } void nsContentPermissionRequestProxy::OnParentDestroyed() { mParent = nullptr; } NS_IMPL_ISUPPORTS(nsContentPermissionRequestProxy, nsIContentPermissionRequest) NS_IMETHODIMP nsContentPermissionRequestProxy::GetTypes(nsIArray** aTypes) { nsCOMPtr types = do_CreateInstance(NS_ARRAY_CONTRACTID); if (mozilla::dom::nsContentPermissionUtils::ConvertPermissionRequestToArray( mPermissionRequests, types)) { types.forget(aTypes); return NS_OK; } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsContentPermissionRequestProxy::GetWindow(mozIDOMWindow** aRequestingWindow) { NS_ENSURE_ARG_POINTER(aRequestingWindow); *aRequestingWindow = nullptr; // ipc doesn't have a window return NS_OK; } NS_IMETHODIMP nsContentPermissionRequestProxy::GetPrincipal( nsIPrincipal** aRequestingPrincipal) { NS_ENSURE_ARG_POINTER(aRequestingPrincipal); if (mParent == nullptr) { return NS_ERROR_FAILURE; } NS_ADDREF(*aRequestingPrincipal = mParent->mPrincipal); return NS_OK; } NS_IMETHODIMP nsContentPermissionRequestProxy::GetTopLevelPrincipal( nsIPrincipal** aRequestingPrincipal) { NS_ENSURE_ARG_POINTER(aRequestingPrincipal); if (mParent == nullptr) { return NS_ERROR_FAILURE; } if (!mParent->mTopLevelPrincipal) { *aRequestingPrincipal = nullptr; return NS_OK; } NS_ADDREF(*aRequestingPrincipal = mParent->mTopLevelPrincipal); return NS_OK; } NS_IMETHODIMP nsContentPermissionRequestProxy::GetDelegatePrincipal( const nsACString& aType, nsIPrincipal** aRequestingPrincipal) { NS_ENSURE_ARG_POINTER(aRequestingPrincipal); if (mParent == nullptr) { return NS_ERROR_FAILURE; } return PermissionDelegateHandler::GetDelegatePrincipal(aType, this, aRequestingPrincipal); } NS_IMETHODIMP nsContentPermissionRequestProxy::GetElement(Element** aRequestingElement) { NS_ENSURE_ARG_POINTER(aRequestingElement); if (mParent == nullptr) { return NS_ERROR_FAILURE; } nsCOMPtr elem = mParent->mElement; elem.forget(aRequestingElement); return NS_OK; } NS_IMETHODIMP nsContentPermissionRequestProxy::GetHasValidTransientUserGestureActivation( bool* aHasValidTransientUserGestureActivation) { NS_ENSURE_ARG_POINTER(aHasValidTransientUserGestureActivation); if (mParent == nullptr) { return NS_ERROR_FAILURE; } *aHasValidTransientUserGestureActivation = mParent->mHasValidTransientUserGestureActivation; return NS_OK; } NS_IMETHODIMP nsContentPermissionRequestProxy::GetIsRequestDelegatedToUnsafeThirdParty( bool* aIsRequestDelegatedToUnsafeThirdParty) { NS_ENSURE_ARG_POINTER(aIsRequestDelegatedToUnsafeThirdParty); if (mParent == nullptr) { return NS_ERROR_FAILURE; } *aIsRequestDelegatedToUnsafeThirdParty = mParent->mIsRequestDelegatedToUnsafeThirdParty; return NS_OK; } NS_IMETHODIMP nsContentPermissionRequestProxy::Cancel() { if (mParent == nullptr) { return NS_ERROR_FAILURE; } // Don't send out the delete message when the managing protocol (PBrowser) is // being destroyed and PContentPermissionRequest will soon be. if (mParent->IsBeingDestroyed()) { return NS_ERROR_FAILURE; } nsTArray emptyChoices; Unused << mParent->SendNotifyResult(false, emptyChoices); return NS_OK; } NS_IMETHODIMP nsContentPermissionRequestProxy::Allow(JS::Handle aChoices) { if (mParent == nullptr) { return NS_ERROR_FAILURE; } // Don't send out the delete message when the managing protocol (PBrowser) is // being destroyed and PContentPermissionRequest will soon be. if (mParent->IsBeingDestroyed()) { return NS_ERROR_FAILURE; } nsTArray choices; nsresult rv = TranslateChoices(aChoices, mPermissionRequests, choices); if (NS_FAILED(rv)) { return rv; } Unused << mParent->SendNotifyResult(true, choices); return NS_OK; } // RemotePermissionRequest RemotePermissionRequest::RemotePermissionRequest( nsIContentPermissionRequest* aRequest, nsPIDOMWindowInner* aWindow) : mRequest(aRequest), mWindow(aWindow), mIPCOpen(false), mDestroyed(false) {} RemotePermissionRequest::~RemotePermissionRequest() { MOZ_ASSERT( !mIPCOpen, "Protocol must not be open when RemotePermissionRequest is destroyed."); } void RemotePermissionRequest::DoCancel() { NS_ASSERTION(mRequest, "We need a request"); nsCOMPtr request(mRequest); request->Cancel(); } void RemotePermissionRequest::DoAllow(JS::Handle aChoices) { NS_ASSERTION(mRequest, "We need a request"); nsCOMPtr request(mRequest); request->Allow(aChoices); } // PContentPermissionRequestChild mozilla::ipc::IPCResult RemotePermissionRequest::RecvNotifyResult( const bool& aAllow, nsTArray&& aChoices) { Destroy(); if (aAllow && mWindow->IsCurrentInnerWindow()) { // Use 'undefined' if no choice is provided. if (aChoices.IsEmpty()) { DoAllow(JS::UndefinedHandleValue); return IPC_OK(); } // Convert choices to a JS val if any. // {"type1": "choice1", "type2": "choiceA"} AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(mWindow))) { return IPC_OK(); // This is not an IPC error. } JSContext* cx = jsapi.cx(); JS::Rooted obj(cx); obj = JS_NewPlainObject(cx); for (uint32_t i = 0; i < aChoices.Length(); ++i) { const nsString& choice = aChoices[i].choice(); const nsCString& type = aChoices[i].type(); JS::Rooted jChoice( cx, JS_NewUCStringCopyN(cx, choice.get(), choice.Length())); JS::Rooted vChoice(cx, StringValue(jChoice)); if (!JS_SetProperty(cx, obj, type.get(), vChoice)) { return IPC_FAIL_NO_REASON(this); } } JS::Rooted val(cx, JS::ObjectValue(*obj)); DoAllow(val); } else { DoCancel(); } return IPC_OK(); } void RemotePermissionRequest::Destroy() { if (!IPCOpen()) { return; } Unused << this->SendDestroy(); mDestroyed = true; }