fune/dom/base/nsContentPermissionHelper.cpp
Nika Layzell 09d88e5fd2 Bug 1749059 - Remove Quantum DOM support from IPDL, r=ipc-reviewers,mccr8
This is no longer necessary as the Quantum DOM project is no longer
happening, and removing support simplifies various components inside of
IPDL.

As some code used the support to get a `nsISerialEventTarget` for an
actor's worker thread, that method was replaced with a method which
instead pulls the nsISerialEventTarget from the MessageChannel and
should work on all actors.

Differential Revision: https://phabricator.services.mozilla.com/D135411
2022-01-25 20:29:46 +00:00

877 lines
26 KiB
C++

/* -*- 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 <map>
#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; // <snicker>
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<PermissionRequest>& aRequests, Element* aElement,
nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal,
const bool aHasValidTransientUserGestureActivation,
const bool aIsRequestDelegatedToUnsafeThirdParty);
virtual ~ContentPermissionRequestParent();
bool IsBeingDestroyed();
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCOMPtr<nsIPrincipal> mTopLevelPrincipal;
nsCOMPtr<Element> mElement;
bool mHasValidTransientUserGestureActivation;
// See nsIPermissionDelegateHandler.maybeUnsafePermissionDelegate.
bool mIsRequestDelegatedToUnsafeThirdParty;
RefPtr<nsContentPermissionRequestProxy> mProxy;
nsTArray<PermissionRequest> 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<PermissionRequest>& 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<nsContentPermissionRequestProxy> 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<ContentParent*>(Manager());
return !contentParent->IsAlive();
}
NS_IMPL_ISUPPORTS(ContentPermissionType, nsIContentPermissionType)
ContentPermissionType::ContentPermissionType(
const nsACString& aType, const nsTArray<nsString>& 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<nsIMutableArray> 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<nsISupportsString> 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<PermissionRequest>& aSrcArray, nsIMutableArray* aDesArray) {
uint32_t len = aSrcArray.Length();
for (uint32_t i = 0; i < len; i++) {
RefPtr<ContentPermissionType> cpt =
new ContentPermissionType(aSrcArray[i].type(), aSrcArray[i].options());
aDesArray->AppendElement(cpt);
}
return len;
}
/* static */
void nsContentPermissionUtils::ConvertArrayToPermissionRequest(
nsIArray* aSrcArray, nsTArray<PermissionRequest>& aDesArray) {
uint32_t len = 0;
aSrcArray->GetLength(&len);
for (uint32_t i = 0; i < len; i++) {
nsCOMPtr<nsIContentPermissionType> cpt = do_QueryElementAt(aSrcArray, i);
nsAutoCString type;
cpt->GetType(type);
nsCOMPtr<nsIArray> optionArray;
cpt->GetOptions(getter_AddRefs(optionArray));
uint32_t optionsLength = 0;
if (optionArray) {
optionArray->GetLength(&optionsLength);
}
nsTArray<nsString> options;
for (uint32_t j = 0; j < optionsLength; ++j) {
nsCOMPtr<nsISupportsString> isupportsString =
do_QueryElementAt(optionArray, j);
if (isupportsString) {
nsString option;
isupportsString->GetData(option);
options.AppendElement(option);
}
}
aDesArray.AppendElement(PermissionRequest(type, options));
}
}
static std::map<PContentPermissionRequestParent*, TabId>&
ContentPermissionRequestParentMap() {
MOZ_ASSERT(NS_IsMainThread());
static std::map<PContentPermissionRequestParent*, TabId>
sPermissionRequestParentMap;
return sPermissionRequestParentMap;
}
static std::map<PContentPermissionRequestChild*, TabId>&
ContentPermissionRequestChildMap() {
MOZ_ASSERT(NS_IsMainThread());
static std::map<PContentPermissionRequestChild*, TabId>
sPermissionRequestChildMap;
return sPermissionRequestChildMap;
}
/* static */
nsresult nsContentPermissionUtils::CreatePermissionArray(
const nsACString& aType, const nsTArray<nsString>& aOptions,
nsIArray** aTypesArray) {
nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
RefPtr<ContentPermissionType> permType =
new ContentPermissionType(aType, aOptions);
types->AppendElement(permType);
types.forget(aTypesArray);
return NS_OK;
}
/* static */
PContentPermissionRequestParent*
nsContentPermissionUtils::CreateContentPermissionRequestParent(
const nsTArray<PermissionRequest>& 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<RemotePermissionRequest> 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<nsIArray> typeArray;
nsresult rv = aRequest->GetTypes(getter_AddRefs(typeArray));
NS_ENSURE_SUCCESS(rv, rv);
nsTArray<PermissionRequest> permArray;
ConvertArrayToPermissionRequest(typeArray, permArray);
nsCOMPtr<nsIPrincipal> principal;
rv = aRequest->GetPrincipal(getter_AddRefs(principal));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPrincipal> 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, IPC::Principal(principal),
IPC::Principal(topLevelPrincipal),
hasValidTransientUserGestureActivation,
isRequestDelegatedToUnsafeThirdParty, child->GetTabId());
ContentPermissionRequestChildMap()[req.get()] = child->GetTabId();
req->Sendprompt();
return NS_OK;
}
// for chrome process
nsCOMPtr<nsIContentPermissionPrompt> 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<PContentPermissionRequestParent*>
nsContentPermissionUtils::GetContentPermissionRequestParentById(
const TabId& aTabId) {
nsTArray<PContentPermissionRequestParent*> 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<PContentPermissionRequestChild*>
nsContentPermissionUtils::GetContentPermissionRequestChildById(
const TabId& aTabId) {
nsTArray<PContentPermissionRequestChild*> 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<nsCString> 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<nsString> 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<ContentPermissionRequestBase> mRequest;
nsCOMPtr<nsPIDOMWindowInner> 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<ContentPermissionRequestBase> mRequest;
};
void ContentPermissionRequestBase::RequestDelayedTask(
nsIEventTarget* aTarget,
ContentPermissionRequestBase::DelayedTaskType aType) {
nsCOMPtr<nsIRunnable> 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::HandleValue aChoices,
const nsTArray<PermissionRequest>& aPermissionRequests,
nsTArray<PermissionChoice>& 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<JSObject*> 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<JS::Value> 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<PermissionRequest>& requests) {
mPermissionRequests = requests.Clone();
nsCOMPtr<nsIContentPermissionPrompt> 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<nsIMutableArray> 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<Element> 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<PermissionChoice> emptyChoices;
Unused << mParent->SendNotifyResult(false, emptyChoices);
return NS_OK;
}
NS_IMETHODIMP
nsContentPermissionRequestProxy::Allow(JS::HandleValue 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<PermissionChoice> 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<nsIContentPermissionRequest> request(mRequest);
request->Cancel();
}
void RemotePermissionRequest::DoAllow(JS::HandleValue aChoices) {
NS_ASSERTION(mRequest, "We need a request");
nsCOMPtr<nsIContentPermissionRequest> request(mRequest);
request->Allow(aChoices);
}
// PContentPermissionRequestChild
mozilla::ipc::IPCResult RemotePermissionRequest::RecvNotifyResult(
const bool& aAllow, nsTArray<PermissionChoice>&& 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<JSObject*> 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<JSString*> jChoice(
cx, JS_NewUCStringCopyN(cx, choice.get(), choice.Length()));
JS::Rooted<JS::Value> vChoice(cx, StringValue(jChoice));
if (!JS_SetProperty(cx, obj, type.get(), vChoice)) {
return IPC_FAIL_NO_REASON(this);
}
}
JS::RootedValue val(cx, JS::ObjectValue(*obj));
DoAllow(val);
} else {
DoCancel();
}
return IPC_OK();
}
void RemotePermissionRequest::Destroy() {
if (!IPCOpen()) {
return;
}
Unused << this->SendDestroy();
mDestroyed = true;
}