gecko-dev/dom/base/nsContentPermissionHelper.cpp
arthur.iakab af07f9b9d4 Backed out 13 changesets (bug 1500948, bug 1509362, bug 1509591, bug 1448426, bug 1500949, bug 1487249, bug 1509930, bug 1500950, bug 1500944) for causing crashes and assertion failures on PBackgroundParent.cpp:696 CLOSED TREE
Backed out changeset c3fe435e473a (bug 1509362)
Backed out changeset 6494840edc17 (bug 1509591)
Backed out changeset 123b5d5a3637 (bug 1509591)
Backed out changeset bce195f98895 (bug 1509930)
Backed out changeset 66db13126408 (bug 1500950)
Backed out changeset 3b5fffae2d2b (bug 1500949)
Backed out changeset 71c3b3677db8 (bug 1500948)
Backed out changeset 784b3b56e2ab (bug 1500944)
Backed out changeset 0bad27401ddb (bug 1500944)
Backed out changeset e0f95dd96d54 (bug 1448426)
Backed out changeset c60fa11538db (bug 1487249)
Backed out changeset 0190d4ffa54f (bug 1487249)
Backed out changeset 3cd10aba9130 (bug 1487249)
2018-11-28 02:57:32 +02:00

1098 lines
30 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/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/dom/TabChild.h"
#include "mozilla/dom/TabParent.h"
#include "mozilla/EventStateManager.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 "nsIDocument.h"
#include "nsIWeakReferenceUtils.h"
using mozilla::Unused; // <snicker>
using namespace mozilla::dom;
using namespace mozilla;
#define kVisibilityChange "visibilitychange"
class VisibilityChangeListener final : public nsIDOMEventListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMEVENTLISTENER
explicit VisibilityChangeListener(nsPIDOMWindowInner* aWindow);
void RemoveListener();
void SetCallback(nsIContentPermissionRequestCallback* aCallback);
already_AddRefed<nsIContentPermissionRequestCallback> GetCallback();
private:
virtual ~VisibilityChangeListener() {}
nsWeakPtr mWindow;
nsCOMPtr<nsIContentPermissionRequestCallback> mCallback;
};
NS_IMPL_ISUPPORTS(VisibilityChangeListener, nsIDOMEventListener)
VisibilityChangeListener::VisibilityChangeListener(nsPIDOMWindowInner* aWindow)
{
MOZ_ASSERT(aWindow);
mWindow = do_GetWeakReference(aWindow);
nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
if (doc) {
doc->AddSystemEventListener(NS_LITERAL_STRING(kVisibilityChange),
/* listener */ this,
/* use capture */ true,
/* wants untrusted */ false);
}
}
NS_IMETHODIMP
VisibilityChangeListener::HandleEvent(Event* aEvent)
{
nsAutoString type;
aEvent->GetType(type);
if (!type.EqualsLiteral(kVisibilityChange)) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDocument> doc = do_QueryInterface(aEvent->GetTarget());
MOZ_ASSERT(doc);
if (mCallback) {
mCallback->NotifyVisibility(!doc->Hidden());
}
return NS_OK;
}
void
VisibilityChangeListener::RemoveListener()
{
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow);
if (!window) {
return;
}
nsCOMPtr<EventTarget> target = window->GetExtantDoc();
if (target) {
target->RemoveSystemEventListener(NS_LITERAL_STRING(kVisibilityChange),
/* listener */ this,
/* use capture */ true);
}
}
void
VisibilityChangeListener::SetCallback(nsIContentPermissionRequestCallback *aCallback)
{
mCallback = aCallback;
}
already_AddRefed<nsIContentPermissionRequestCallback>
VisibilityChangeListener::GetCallback()
{
nsCOMPtr<nsIContentPermissionRequestCallback> callback = mCallback;
return callback.forget();
}
namespace mozilla {
namespace dom {
class ContentPermissionRequestParent : public PContentPermissionRequestParent
{
public:
ContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
Element* aElement,
const IPC::Principal& aPrincipal,
const IPC::Principal& aTopLevelPrincipal,
const bool aIsHandlingUserInput);
virtual ~ContentPermissionRequestParent();
bool IsBeingDestroyed();
nsCOMPtr<nsIPrincipal> mPrincipal;
nsCOMPtr<nsIPrincipal> mTopLevelPrincipal;
nsCOMPtr<Element> mElement;
bool mIsHandlingUserInput;
RefPtr<nsContentPermissionRequestProxy> mProxy;
nsTArray<PermissionRequest> mRequests;
private:
virtual mozilla::ipc::IPCResult Recvprompt() override;
virtual mozilla::ipc::IPCResult RecvNotifyVisibility(const bool& aIsVisible) override;
virtual mozilla::ipc::IPCResult RecvDestroy() override;
virtual void ActorDestroy(ActorDestroyReason why) override;
};
ContentPermissionRequestParent::ContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
Element* aElement,
const IPC::Principal& aPrincipal,
const IPC::Principal& aTopLevelPrincipal,
const bool aIsHandlingUserInput)
{
MOZ_COUNT_CTOR(ContentPermissionRequestParent);
mPrincipal = aPrincipal;
mTopLevelPrincipal = aTopLevelPrincipal;
mElement = aElement;
mRequests = aRequests;
mIsHandlingUserInput = aIsHandlingUserInput;
}
ContentPermissionRequestParent::~ContentPermissionRequestParent()
{
MOZ_COUNT_DTOR(ContentPermissionRequestParent);
}
mozilla::ipc::IPCResult
ContentPermissionRequestParent::Recvprompt()
{
mProxy = new nsContentPermissionRequestProxy(this);
if (NS_FAILED(mProxy->Init(mRequests))) {
mProxy->Cancel();
}
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentPermissionRequestParent::RecvNotifyVisibility(const bool& aIsVisible)
{
if (!mProxy) {
return IPC_FAIL_NO_REASON(this);
}
mProxy->NotifyVisibility(aIsVisible);
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;
}
ContentPermissionType::~ContentPermissionType()
{
}
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 */ uint32_t
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));
}
return len;
}
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,
const IPC::Principal& aPrincipal,
const IPC::Principal& aTopLevelPrincipal,
const bool aIsHandlingUserInput,
const TabId& aTabId)
{
PContentPermissionRequestParent* parent =
new ContentPermissionRequestParent(aRequests, aElement, aPrincipal, aTopLevelPrincipal,
aIsHandlingUserInput);
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.
TabChild* child = TabChild::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 isHandlingUserInput;
rv = aRequest->GetIsHandlingUserInput(&isHandlingUserInput);
NS_ENSURE_SUCCESS(rv, rv);
ContentChild::GetSingleton()->SetEventTargetForActor(
req, aWindow->EventTargetFor(TaskCategory::Other));
req->IPDLAddRef();
ContentChild::GetSingleton()->SendPContentPermissionRequestConstructor(
req,
permArray,
IPC::Principal(principal),
IPC::Principal(topLevelPrincipal),
isHandlingUserInput,
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);
}
NS_IMPL_ISUPPORTS(nsContentPermissionRequester, nsIContentPermissionRequester)
nsContentPermissionRequester::nsContentPermissionRequester(nsPIDOMWindowInner* aWindow)
: mWindow(do_GetWeakReference(aWindow))
, mListener(new VisibilityChangeListener(aWindow))
{
}
nsContentPermissionRequester::~nsContentPermissionRequester()
{
mListener->RemoveListener();
mListener = nullptr;
}
NS_IMETHODIMP
nsContentPermissionRequester::GetVisibility(nsIContentPermissionRequestCallback* aCallback)
{
NS_ENSURE_ARG_POINTER(aCallback);
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow);
if (!window) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDocShell> docshell = window->GetDocShell();
if (!docshell) {
return NS_ERROR_FAILURE;
}
bool isActive = false;
docshell->GetIsActive(&isActive);
aCallback->NotifyVisibility(isActive);
return NS_OK;
}
NS_IMETHODIMP
nsContentPermissionRequester::SetOnVisibilityChange(nsIContentPermissionRequestCallback* aCallback)
{
mListener->SetCallback(aCallback);
if (!aCallback) {
mListener->RemoveListener();
}
return NS_OK;
}
NS_IMETHODIMP
nsContentPermissionRequester::GetOnVisibilityChange(nsIContentPermissionRequestCallback** aCallback)
{
NS_ENSURE_ARG_POINTER(aCallback);
nsCOMPtr<nsIContentPermissionRequestCallback> callback = mListener->GetCallback();
callback.forget(aCallback);
return NS_OK;
}
static
nsIPrincipal*
GetTopLevelPrincipal(nsPIDOMWindowInner* aWindow)
{
MOZ_ASSERT(aWindow);
nsPIDOMWindowOuter* top = aWindow->GetScriptableTop();
if (!top) {
return nullptr;
}
nsPIDOMWindowInner* inner = top->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,
bool aIsHandlingUserInput,
nsPIDOMWindowInner* aWindow,
const nsACString& aPrefName,
const nsACString& aType)
: mPrincipal(aPrincipal)
, mTopLevelPrincipal(aWindow ?
::GetTopLevelPrincipal(aWindow) :
nullptr)
, mWindow(aWindow)
, mRequester(aWindow ?
new nsContentPermissionRequester(aWindow) :
nullptr)
, mPrefName(aPrefName)
, mType(aType)
, mIsHandlingUserInput(aIsHandlingUserInput)
{
}
NS_IMETHODIMP
ContentPermissionRequestBase::GetPrincipal(nsIPrincipal** aRequestingPrincipal)
{
NS_ADDREF(*aRequestingPrincipal = mPrincipal);
return NS_OK;
}
NS_IMETHODIMP
ContentPermissionRequestBase::GetTopLevelPrincipal(nsIPrincipal** aRequestingPrincipal)
{
NS_ADDREF(*aRequestingPrincipal = mTopLevelPrincipal);
return NS_OK;
}
NS_IMETHODIMP
ContentPermissionRequestBase::GetWindow(mozIDOMWindow** aRequestingWindow)
{
NS_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::GetIsHandlingUserInput(bool* aIsHandlingUserInput)
{
*aIsHandlingUserInput = mIsHandlingUserInput;
return NS_OK;
}
NS_IMETHODIMP
ContentPermissionRequestBase::GetRequester(nsIContentPermissionRequester** aRequester)
{
NS_ENSURE_ARG_POINTER(aRequester);
nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
requester.forget(aRequester);
return NS_OK;
}
NS_IMETHODIMP
ContentPermissionRequestBase::GetTypes(nsIArray** aTypes)
{
nsTArray<nsString> emptyOptions;
return nsContentPermissionUtils::CreatePermissionArray(mType,
emptyOptions,
aTypes);
}
ContentPermissionRequestBase::PromptResult
ContentPermissionRequestBase::CheckPromptPrefs()
{
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;
}
nsresult
ContentPermissionRequestBase::ShowPrompt(
ContentPermissionRequestBase::PromptResult& aResult)
{
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)
{
}
NS_IMETHOD Run() override {
if (mAllow) {
mRequest->Allow(JS::UndefinedHandleValue);
} else {
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());
obj = CheckedUnwrap(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 dom
} // namespace mozilla
NS_IMPL_ISUPPORTS(nsContentPermissionRequestProxy::nsContentPermissionRequesterProxy,
nsIContentPermissionRequester)
NS_IMETHODIMP
nsContentPermissionRequestProxy::nsContentPermissionRequesterProxy
::GetVisibility(nsIContentPermissionRequestCallback* aCallback)
{
NS_ENSURE_ARG_POINTER(aCallback);
mGetCallback = aCallback;
mWaitGettingResult = true;
Unused << mParent->SendGetVisibility();
return NS_OK;
}
NS_IMETHODIMP
nsContentPermissionRequestProxy::nsContentPermissionRequesterProxy
::SetOnVisibilityChange(nsIContentPermissionRequestCallback* aCallback)
{
mOnChangeCallback = aCallback;
return NS_OK;
}
NS_IMETHODIMP
nsContentPermissionRequestProxy::nsContentPermissionRequesterProxy
::GetOnVisibilityChange(nsIContentPermissionRequestCallback** aCallback)
{
NS_ENSURE_ARG_POINTER(aCallback);
nsCOMPtr<nsIContentPermissionRequestCallback> callback = mOnChangeCallback;
callback.forget(aCallback);
return NS_OK;
}
void
nsContentPermissionRequestProxy::nsContentPermissionRequesterProxy
::NotifyVisibilityResult(const bool& aIsVisible)
{
if (mWaitGettingResult) {
MOZ_ASSERT(mGetCallback);
mWaitGettingResult = false;
mGetCallback->NotifyVisibility(aIsVisible);
return;
}
if (mOnChangeCallback) {
mOnChangeCallback->NotifyVisibility(aIsVisible);
}
}
nsContentPermissionRequestProxy::nsContentPermissionRequestProxy(ContentPermissionRequestParent* parent)
: mParent(parent)
{
NS_ASSERTION(mParent, "null parent");
}
nsContentPermissionRequestProxy::~nsContentPermissionRequestProxy() = default;
nsresult
nsContentPermissionRequestProxy::Init(const nsTArray<PermissionRequest>& requests)
{
mPermissionRequests = requests;
mRequester = new nsContentPermissionRequesterProxy(mParent);
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()
{
mRequester = nullptr;
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;
}
NS_ADDREF(*aRequestingPrincipal = mParent->mTopLevelPrincipal);
return NS_OK;
}
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::GetIsHandlingUserInput(bool* aIsHandlingUserInput)
{
NS_ENSURE_ARG_POINTER(aIsHandlingUserInput);
if (mParent == nullptr) {
return NS_ERROR_FAILURE;
}
*aIsHandlingUserInput = mParent->mIsHandlingUserInput;
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;
}
void
nsContentPermissionRequestProxy::NotifyVisibility(const bool& aIsVisible)
{
MOZ_ASSERT(mRequester);
mRequester->NotifyVisibilityResult(aIsVisible);
}
NS_IMETHODIMP
nsContentPermissionRequestProxy::GetRequester(nsIContentPermissionRequester** aRequester)
{
NS_ENSURE_ARG_POINTER(aRequester);
RefPtr<nsContentPermissionRequesterProxy> requester = mRequester;
requester.forget(aRequester);
return NS_OK;
}
// RemotePermissionRequest
NS_IMPL_ISUPPORTS(RemotePermissionRequest, nsIContentPermissionRequestCallback);
RemotePermissionRequest::RemotePermissionRequest(
nsIContentPermissionRequest* aRequest,
nsPIDOMWindowInner* aWindow)
: mRequest(aRequest)
, mWindow(aWindow)
, mIPCOpen(false)
, mDestroyed(false)
{
mListener = new VisibilityChangeListener(mWindow);
mListener->SetCallback(this);
}
RemotePermissionRequest::~RemotePermissionRequest()
{
MOZ_ASSERT(!mIPCOpen, "Protocol must not be open when RemotePermissionRequest is destroyed.");
}
void
RemotePermissionRequest::DoCancel()
{
NS_ASSERTION(mRequest, "We need a request");
mRequest->Cancel();
}
void
RemotePermissionRequest::DoAllow(JS::HandleValue aChoices)
{
NS_ASSERTION(mRequest, "We need a request");
mRequest->Allow(aChoices);
}
// PContentPermissionRequestChild
mozilla::ipc::IPCResult
RemotePermissionRequest::RecvNotifyResult(const bool& aAllow,
InfallibleTArray<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();
}
mozilla::ipc::IPCResult
RemotePermissionRequest::RecvGetVisibility()
{
nsCOMPtr<nsIDocShell> docshell = mWindow->GetDocShell();
if (!docshell) {
return IPC_FAIL_NO_REASON(this);
}
bool isActive = false;
docshell->GetIsActive(&isActive);
Unused << SendNotifyVisibility(isActive);
return IPC_OK();
}
void
RemotePermissionRequest::Destroy()
{
if (!IPCOpen()) {
return;
}
Unused << this->SendDestroy();
mListener->RemoveListener();
mListener = nullptr;
mDestroyed = true;
}
NS_IMETHODIMP
RemotePermissionRequest::NotifyVisibility(bool isVisible)
{
if (!IPCOpen()) {
return NS_OK;
}
Unused << SendNotifyVisibility(isVisible);
return NS_OK;
}