summaryrefslogtreecommitdiffstats
path: root/dom/base/nsContentPermissionHelper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base/nsContentPermissionHelper.cpp')
-rw-r--r--dom/base/nsContentPermissionHelper.cpp871
1 files changed, 871 insertions, 0 deletions
diff --git a/dom/base/nsContentPermissionHelper.cpp b/dom/base/nsContentPermissionHelper.cpp
new file mode 100644
index 0000000000..7733263f7d
--- /dev/null
+++ b/dom/base/nsContentPermissionHelper.cpp
@@ -0,0 +1,871 @@
+/* -*- 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_IF_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_IF_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;
+}