mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
146a80ecff
Differential Revision: https://phabricator.services.mozilla.com/D154091
877 lines
26 KiB
C++
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, 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::Handle<JS::Value> 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::Handle<JS::Value> 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::Handle<JS::Value> 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::Rooted<JS::Value> val(cx, JS::ObjectValue(*obj));
|
|
DoAllow(val);
|
|
} else {
|
|
DoCancel();
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
void RemotePermissionRequest::Destroy() {
|
|
if (!IPCOpen()) {
|
|
return;
|
|
}
|
|
Unused << this->SendDestroy();
|
|
mDestroyed = true;
|
|
}
|