summaryrefslogtreecommitdiff
path: root/dom/base/nsContentPermissionHelper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base/nsContentPermissionHelper.cpp')
-rw-r--r--dom/base/nsContentPermissionHelper.cpp858
1 files changed, 858 insertions, 0 deletions
diff --git a/dom/base/nsContentPermissionHelper.cpp b/dom/base/nsContentPermissionHelper.cpp
new file mode 100644
index 0000000000..fd04b10132
--- /dev/null
+++ b/dom/base/nsContentPermissionHelper.cpp
@@ -0,0 +1,858 @@
+/* -*- 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>
+#ifdef MOZ_WIDGET_GONK
+#include "GonkPermission.h"
+#endif // MOZ_WIDGET_GONK
+#include "nsCOMPtr.h"
+#include "nsIDOMElement.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/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 "nsIDOMEvent.h"
+#include "nsWeakPtr.h"
+#include "ScriptSettings.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(nsIDOMEvent* aEvent)
+{
+ nsAutoString type;
+ aEvent->GetType(type);
+ if (!type.EqualsLiteral(kVisibilityChange)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIDocument> doc =
+ do_QueryInterface(aEvent->InternalDOMEvent()->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 = do_QueryInterface(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* element,
+ const IPC::Principal& principal);
+ virtual ~ContentPermissionRequestParent();
+
+ bool IsBeingDestroyed();
+
+ nsCOMPtr<nsIPrincipal> mPrincipal;
+ nsCOMPtr<Element> mElement;
+ RefPtr<nsContentPermissionRequestProxy> mProxy;
+ nsTArray<PermissionRequest> mRequests;
+
+ private:
+ virtual bool Recvprompt();
+ virtual bool RecvNotifyVisibility(const bool& aIsVisible);
+ virtual bool RecvDestroy();
+ virtual void ActorDestroy(ActorDestroyReason why);
+};
+
+ContentPermissionRequestParent::ContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
+ Element* aElement,
+ const IPC::Principal& aPrincipal)
+{
+ MOZ_COUNT_CTOR(ContentPermissionRequestParent);
+
+ mPrincipal = aPrincipal;
+ mElement = aElement;
+ mRequests = aRequests;
+}
+
+ContentPermissionRequestParent::~ContentPermissionRequestParent()
+{
+ MOZ_COUNT_DTOR(ContentPermissionRequestParent);
+}
+
+bool
+ContentPermissionRequestParent::Recvprompt()
+{
+ mProxy = new nsContentPermissionRequestProxy();
+ if (NS_FAILED(mProxy->Init(mRequests, this))) {
+ mProxy->Cancel();
+ }
+ return true;
+}
+
+bool
+ContentPermissionRequestParent::RecvNotifyVisibility(const bool& aIsVisible)
+{
+ if (!mProxy) {
+ return false;
+ }
+ mProxy->NotifyVisibility(aIsVisible);
+ return true;
+}
+
+bool
+ContentPermissionRequestParent::RecvDestroy()
+{
+ Unused << PContentPermissionRequestParent::Send__delete__(this);
+ return true;
+}
+
+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 nsACString& aAccess,
+ const nsTArray<nsString>& aOptions)
+{
+ mType = aType;
+ mAccess = aAccess;
+ mOptions = aOptions;
+}
+
+ContentPermissionType::~ContentPermissionType()
+{
+}
+
+NS_IMETHODIMP
+ContentPermissionType::GetType(nsACString& aType)
+{
+ aType = mType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ContentPermissionType::GetAccess(nsACString& aAccess)
+{
+ aAccess = mAccess;
+ 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, false);
+ 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].access(),
+ aSrcArray[i].options());
+ aDesArray->AppendElement(cpt, false);
+ }
+ 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;
+ nsAutoCString access;
+ cpt->GetType(type);
+ cpt->GetAccess(access);
+
+ 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, access, 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 nsACString& aAccess,
+ const nsTArray<nsString>& aOptions,
+ nsIArray** aTypesArray)
+{
+ nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
+ RefPtr<ContentPermissionType> permType = new ContentPermissionType(aType,
+ aAccess,
+ aOptions);
+ types->AppendElement(permType, false);
+ types.forget(aTypesArray);
+
+ return NS_OK;
+}
+
+/* static */ PContentPermissionRequestParent*
+nsContentPermissionUtils::CreateContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
+ Element* element,
+ const IPC::Principal& principal,
+ const TabId& aTabId)
+{
+ PContentPermissionRequestParent* parent =
+ new ContentPermissionRequestParent(aRequests, element, principal);
+ ContentPermissionRequestParentMap()[parent] = aTabId;
+
+ return parent;
+}
+
+/* static */ nsresult
+nsContentPermissionUtils::AskPermission(nsIContentPermissionRequest* aRequest,
+ nsPIDOMWindowInner* aWindow)
+{
+ MOZ_ASSERT(!aWindow || aWindow->IsInnerWindow());
+ 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);
+
+ req->IPDLAddRef();
+ ContentChild::GetSingleton()->SendPContentPermissionRequestConstructor(
+ req,
+ permArray,
+ IPC::Principal(principal),
+ 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 Move(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 Move(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;
+}
+
+} // 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()
+{
+ MOZ_COUNT_CTOR(nsContentPermissionRequestProxy);
+}
+
+nsContentPermissionRequestProxy::~nsContentPermissionRequestProxy()
+{
+ MOZ_COUNT_DTOR(nsContentPermissionRequestProxy);
+}
+
+nsresult
+nsContentPermissionRequestProxy::Init(const nsTArray<PermissionRequest>& requests,
+ ContentPermissionRequestParent* parent)
+{
+ NS_ASSERTION(parent, "null parent");
+ mParent = parent;
+ 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::GetElement(nsIDOMElement * *aRequestingElement)
+{
+ NS_ENSURE_ARG_POINTER(aRequestingElement);
+ if (mParent == nullptr) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(mParent->mElement);
+ elem.forget(aRequestingElement);
+ 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);
+ mParent = nullptr;
+ 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;
+ if (aChoices.isNullOrUndefined()) {
+ // No choice is specified.
+ } else if (aChoices.isObject()) {
+ // Iterate through all permission types.
+ for (uint32_t i = 0; i < mPermissionRequests.Length(); ++i) {
+ nsCString type = mPermissionRequests[i].type();
+
+ AutoJSAPI jsapi;
+ jsapi.Init();
+
+ JSContext* cx = jsapi.cx();
+ JS::Rooted<JSObject*> obj(cx, &aChoices.toObject());
+ JSAutoCompartment ac(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;
+ }
+ choices.AppendElement(PermissionChoice(type, choice));
+ }
+ }
+ } else {
+ MOZ_ASSERT(false, "SelectedChoices should be undefined or an JS object");
+ return NS_ERROR_FAILURE;
+ }
+
+ Unused << mParent->SendNotifyResult(true, choices);
+ mParent = nullptr;
+ 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
+bool
+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 true;
+ }
+
+ // Convert choices to a JS val if any.
+ // {"type1": "choice1", "type2": "choiceA"}
+ AutoJSAPI jsapi;
+ if (NS_WARN_IF(!jsapi.Init(mWindow))) {
+ return true; // 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 false;
+ }
+ }
+ JS::RootedValue val(cx, JS::ObjectValue(*obj));
+ DoAllow(val);
+ } else {
+ DoCancel();
+ }
+ return true;
+}
+
+bool
+RemotePermissionRequest::RecvGetVisibility()
+{
+ nsCOMPtr<nsIDocShell> docshell = mWindow->GetDocShell();
+ if (!docshell) {
+ return false;
+ }
+
+ bool isActive = false;
+ docshell->GetIsActive(&isActive);
+ Unused << SendNotifyVisibility(isActive);
+ return true;
+}
+
+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;
+}