Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / base / nsContentPermissionHelper.cpp
blob0e1668ec5e3f7e8807fed1a0ebcd34a248d5810d
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include <map>
8 #include "nsCOMPtr.h"
9 #include "nsIPrincipal.h"
10 #include "mozilla/dom/BrowserChild.h"
11 #include "mozilla/dom/BrowserParent.h"
12 #include "mozilla/dom/ContentChild.h"
13 #include "mozilla/dom/ContentParent.h"
14 #include "mozilla/dom/Element.h"
15 #include "mozilla/dom/Event.h"
16 #include "mozilla/dom/PContentPermission.h"
17 #include "mozilla/dom/PermissionMessageUtils.h"
18 #include "mozilla/dom/PContentPermissionRequestParent.h"
19 #include "mozilla/dom/ScriptSettings.h"
20 #include "mozilla/Attributes.h"
21 #include "mozilla/Preferences.h"
22 #include "mozilla/Unused.h"
23 #include "nsComponentManagerUtils.h"
24 #include "nsArrayUtils.h"
25 #include "nsIMutableArray.h"
26 #include "nsContentPermissionHelper.h"
27 #include "nsGlobalWindowInner.h"
28 #include "nsJSUtils.h"
29 #include "nsISupportsPrimitives.h"
30 #include "nsServiceManagerUtils.h"
31 #include "mozilla/dom/Document.h"
32 #include "nsIWeakReferenceUtils.h"
33 #include "js/PropertyAndElement.h" // JS_GetProperty, JS_SetProperty
35 using mozilla::Unused; // <snicker>
36 using namespace mozilla::dom;
37 using namespace mozilla;
38 using DelegateInfo = PermissionDelegateHandler::PermissionDelegateInfo;
40 namespace mozilla::dom {
42 class ContentPermissionRequestParent : public PContentPermissionRequestParent {
43 public:
44 // @param aIsRequestDelegatedToUnsafeThirdParty see
45 // mIsRequestDelegatedToUnsafeThirdParty.
46 ContentPermissionRequestParent(
47 const nsTArray<PermissionRequest>& aRequests, Element* aElement,
48 nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal,
49 const bool aHasValidTransientUserGestureActivation,
50 const bool aIsRequestDelegatedToUnsafeThirdParty);
51 virtual ~ContentPermissionRequestParent();
53 bool IsBeingDestroyed();
55 nsCOMPtr<nsIPrincipal> mPrincipal;
56 nsCOMPtr<nsIPrincipal> mTopLevelPrincipal;
57 nsCOMPtr<Element> mElement;
58 bool mHasValidTransientUserGestureActivation;
60 // See nsIPermissionDelegateHandler.maybeUnsafePermissionDelegate.
61 bool mIsRequestDelegatedToUnsafeThirdParty;
63 RefPtr<nsContentPermissionRequestProxy> mProxy;
64 nsTArray<PermissionRequest> mRequests;
66 private:
67 // Not MOZ_CAN_RUN_SCRIPT because we can't annotate the thing we override yet.
68 MOZ_CAN_RUN_SCRIPT_BOUNDARY
69 virtual mozilla::ipc::IPCResult Recvprompt() override;
70 virtual mozilla::ipc::IPCResult RecvDestroy() override;
71 virtual void ActorDestroy(ActorDestroyReason why) override;
74 ContentPermissionRequestParent::ContentPermissionRequestParent(
75 const nsTArray<PermissionRequest>& aRequests, Element* aElement,
76 nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal,
77 const bool aHasValidTransientUserGestureActivation,
78 const bool aIsRequestDelegatedToUnsafeThirdParty) {
79 MOZ_COUNT_CTOR(ContentPermissionRequestParent);
81 mPrincipal = aPrincipal;
82 mTopLevelPrincipal = aTopLevelPrincipal;
83 mElement = aElement;
84 mRequests = aRequests.Clone();
85 mHasValidTransientUserGestureActivation =
86 aHasValidTransientUserGestureActivation;
87 mIsRequestDelegatedToUnsafeThirdParty = aIsRequestDelegatedToUnsafeThirdParty;
90 ContentPermissionRequestParent::~ContentPermissionRequestParent() {
91 MOZ_COUNT_DTOR(ContentPermissionRequestParent);
94 mozilla::ipc::IPCResult ContentPermissionRequestParent::Recvprompt() {
95 mProxy = new nsContentPermissionRequestProxy(this);
96 if (NS_FAILED(mProxy->Init(mRequests))) {
97 RefPtr<nsContentPermissionRequestProxy> proxy(mProxy);
98 proxy->Cancel();
100 return IPC_OK();
103 mozilla::ipc::IPCResult ContentPermissionRequestParent::RecvDestroy() {
104 Unused << PContentPermissionRequestParent::Send__delete__(this);
105 return IPC_OK();
108 void ContentPermissionRequestParent::ActorDestroy(ActorDestroyReason why) {
109 if (mProxy) {
110 mProxy->OnParentDestroyed();
114 bool ContentPermissionRequestParent::IsBeingDestroyed() {
115 // When ContentParent::MarkAsDead() is called, we are being destroyed.
116 // It's unsafe to send out any message now.
117 ContentParent* contentParent = static_cast<ContentParent*>(Manager());
118 return !contentParent->IsAlive();
121 NS_IMPL_ISUPPORTS(ContentPermissionType, nsIContentPermissionType)
123 ContentPermissionType::ContentPermissionType(
124 const nsACString& aType, const nsTArray<nsString>& aOptions) {
125 mType = aType;
126 mOptions = aOptions.Clone();
129 ContentPermissionType::~ContentPermissionType() = default;
131 NS_IMETHODIMP
132 ContentPermissionType::GetType(nsACString& aType) {
133 aType = mType;
134 return NS_OK;
137 NS_IMETHODIMP
138 ContentPermissionType::GetOptions(nsIArray** aOptions) {
139 NS_ENSURE_ARG_POINTER(aOptions);
141 *aOptions = nullptr;
143 nsresult rv;
144 nsCOMPtr<nsIMutableArray> options =
145 do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
146 NS_ENSURE_SUCCESS(rv, rv);
148 // copy options into JS array
149 for (uint32_t i = 0; i < mOptions.Length(); ++i) {
150 nsCOMPtr<nsISupportsString> isupportsString =
151 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
152 NS_ENSURE_SUCCESS(rv, rv);
154 rv = isupportsString->SetData(mOptions[i]);
155 NS_ENSURE_SUCCESS(rv, rv);
157 rv = options->AppendElement(isupportsString);
158 NS_ENSURE_SUCCESS(rv, rv);
161 options.forget(aOptions);
162 return NS_OK;
165 // nsContentPermissionUtils
167 /* static */
168 uint32_t nsContentPermissionUtils::ConvertPermissionRequestToArray(
169 nsTArray<PermissionRequest>& aSrcArray, nsIMutableArray* aDesArray) {
170 uint32_t len = aSrcArray.Length();
171 for (uint32_t i = 0; i < len; i++) {
172 RefPtr<ContentPermissionType> cpt =
173 new ContentPermissionType(aSrcArray[i].type(), aSrcArray[i].options());
174 aDesArray->AppendElement(cpt);
176 return len;
179 /* static */
180 void nsContentPermissionUtils::ConvertArrayToPermissionRequest(
181 nsIArray* aSrcArray, nsTArray<PermissionRequest>& aDesArray) {
182 uint32_t len = 0;
183 aSrcArray->GetLength(&len);
184 for (uint32_t i = 0; i < len; i++) {
185 nsCOMPtr<nsIContentPermissionType> cpt = do_QueryElementAt(aSrcArray, i);
186 nsAutoCString type;
187 cpt->GetType(type);
189 nsCOMPtr<nsIArray> optionArray;
190 cpt->GetOptions(getter_AddRefs(optionArray));
191 uint32_t optionsLength = 0;
192 if (optionArray) {
193 optionArray->GetLength(&optionsLength);
195 nsTArray<nsString> options;
196 for (uint32_t j = 0; j < optionsLength; ++j) {
197 nsCOMPtr<nsISupportsString> isupportsString =
198 do_QueryElementAt(optionArray, j);
199 if (isupportsString) {
200 nsString option;
201 isupportsString->GetData(option);
202 options.AppendElement(option);
206 aDesArray.AppendElement(PermissionRequest(type, options));
210 static std::map<PContentPermissionRequestParent*, TabId>&
211 ContentPermissionRequestParentMap() {
212 MOZ_ASSERT(NS_IsMainThread());
213 static std::map<PContentPermissionRequestParent*, TabId>
214 sPermissionRequestParentMap;
215 return sPermissionRequestParentMap;
218 static std::map<PContentPermissionRequestChild*, TabId>&
219 ContentPermissionRequestChildMap() {
220 MOZ_ASSERT(NS_IsMainThread());
221 static std::map<PContentPermissionRequestChild*, TabId>
222 sPermissionRequestChildMap;
223 return sPermissionRequestChildMap;
226 /* static */
227 nsresult nsContentPermissionUtils::CreatePermissionArray(
228 const nsACString& aType, const nsTArray<nsString>& aOptions,
229 nsIArray** aTypesArray) {
230 nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
231 RefPtr<ContentPermissionType> permType =
232 new ContentPermissionType(aType, aOptions);
233 types->AppendElement(permType);
234 types.forget(aTypesArray);
236 return NS_OK;
239 /* static */
240 PContentPermissionRequestParent*
241 nsContentPermissionUtils::CreateContentPermissionRequestParent(
242 const nsTArray<PermissionRequest>& aRequests, Element* aElement,
243 nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal,
244 const bool aHasValidTransientUserGestureActivation,
245 const bool aIsRequestDelegatedToUnsafeThirdParty, const TabId& aTabId) {
246 PContentPermissionRequestParent* parent = new ContentPermissionRequestParent(
247 aRequests, aElement, aPrincipal, aTopLevelPrincipal,
248 aHasValidTransientUserGestureActivation,
249 aIsRequestDelegatedToUnsafeThirdParty);
250 ContentPermissionRequestParentMap()[parent] = aTabId;
252 return parent;
255 /* static */
256 nsresult nsContentPermissionUtils::AskPermission(
257 nsIContentPermissionRequest* aRequest, nsPIDOMWindowInner* aWindow) {
258 NS_ENSURE_STATE(aWindow && aWindow->IsCurrentInnerWindow());
260 // for content process
261 if (XRE_IsContentProcess()) {
262 RefPtr<RemotePermissionRequest> req =
263 new RemotePermissionRequest(aRequest, aWindow);
265 MOZ_ASSERT(NS_IsMainThread()); // IPC can only be execute on main thread.
267 BrowserChild* child = BrowserChild::GetFrom(aWindow->GetDocShell());
268 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
270 nsCOMPtr<nsIArray> typeArray;
271 nsresult rv = aRequest->GetTypes(getter_AddRefs(typeArray));
272 NS_ENSURE_SUCCESS(rv, rv);
274 nsTArray<PermissionRequest> permArray;
275 ConvertArrayToPermissionRequest(typeArray, permArray);
277 nsCOMPtr<nsIPrincipal> principal;
278 rv = aRequest->GetPrincipal(getter_AddRefs(principal));
279 NS_ENSURE_SUCCESS(rv, rv);
281 nsCOMPtr<nsIPrincipal> topLevelPrincipal;
282 rv = aRequest->GetTopLevelPrincipal(getter_AddRefs(topLevelPrincipal));
283 NS_ENSURE_SUCCESS(rv, rv);
285 bool hasValidTransientUserGestureActivation;
286 rv = aRequest->GetHasValidTransientUserGestureActivation(
287 &hasValidTransientUserGestureActivation);
288 NS_ENSURE_SUCCESS(rv, rv);
290 bool isRequestDelegatedToUnsafeThirdParty;
291 rv = aRequest->GetIsRequestDelegatedToUnsafeThirdParty(
292 &isRequestDelegatedToUnsafeThirdParty);
293 NS_ENSURE_SUCCESS(rv, rv);
295 req->IPDLAddRef();
296 if (!ContentChild::GetSingleton()->SendPContentPermissionRequestConstructor(
297 req, permArray, principal, topLevelPrincipal,
298 hasValidTransientUserGestureActivation,
299 isRequestDelegatedToUnsafeThirdParty, child->GetTabId())) {
300 return NS_ERROR_FAILURE;
302 ContentPermissionRequestChildMap()[req.get()] = child->GetTabId();
304 req->Sendprompt();
305 return NS_OK;
308 // for chrome process
309 nsCOMPtr<nsIContentPermissionPrompt> prompt =
310 do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
311 if (prompt) {
312 if (NS_FAILED(prompt->Prompt(aRequest))) {
313 return NS_ERROR_FAILURE;
316 return NS_OK;
319 /* static */
320 nsTArray<PContentPermissionRequestParent*>
321 nsContentPermissionUtils::GetContentPermissionRequestParentById(
322 const TabId& aTabId) {
323 nsTArray<PContentPermissionRequestParent*> parentArray;
324 for (auto& it : ContentPermissionRequestParentMap()) {
325 if (it.second == aTabId) {
326 parentArray.AppendElement(it.first);
330 return parentArray;
333 /* static */
334 void nsContentPermissionUtils::NotifyRemoveContentPermissionRequestParent(
335 PContentPermissionRequestParent* aParent) {
336 auto it = ContentPermissionRequestParentMap().find(aParent);
337 MOZ_ASSERT(it != ContentPermissionRequestParentMap().end());
339 ContentPermissionRequestParentMap().erase(it);
342 /* static */
343 nsTArray<PContentPermissionRequestChild*>
344 nsContentPermissionUtils::GetContentPermissionRequestChildById(
345 const TabId& aTabId) {
346 nsTArray<PContentPermissionRequestChild*> childArray;
347 for (auto& it : ContentPermissionRequestChildMap()) {
348 if (it.second == aTabId) {
349 childArray.AppendElement(it.first);
353 return childArray;
356 /* static */
357 void nsContentPermissionUtils::NotifyRemoveContentPermissionRequestChild(
358 PContentPermissionRequestChild* aChild) {
359 auto it = ContentPermissionRequestChildMap().find(aChild);
360 MOZ_ASSERT(it != ContentPermissionRequestChildMap().end());
362 ContentPermissionRequestChildMap().erase(it);
365 static nsIPrincipal* GetTopLevelPrincipal(nsPIDOMWindowInner* aWindow) {
366 MOZ_ASSERT(aWindow);
368 BrowsingContext* top = aWindow->GetBrowsingContext()->Top();
369 MOZ_ASSERT(top);
371 nsPIDOMWindowOuter* outer = top->GetDOMWindow();
372 if (!outer) {
373 return nullptr;
376 nsPIDOMWindowInner* inner = outer->GetCurrentInnerWindow();
377 if (!inner) {
378 return nullptr;
381 return nsGlobalWindowInner::Cast(inner)->GetPrincipal();
384 NS_IMPL_CYCLE_COLLECTION(ContentPermissionRequestBase, mPrincipal,
385 mTopLevelPrincipal, mWindow)
387 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ContentPermissionRequestBase)
388 NS_INTERFACE_MAP_ENTRY_CONCRETE(nsISupports)
389 NS_INTERFACE_MAP_ENTRY_CONCRETE(nsIContentPermissionRequest)
390 NS_INTERFACE_MAP_END
392 NS_IMPL_CYCLE_COLLECTING_ADDREF(ContentPermissionRequestBase)
393 NS_IMPL_CYCLE_COLLECTING_RELEASE(ContentPermissionRequestBase)
395 ContentPermissionRequestBase::ContentPermissionRequestBase(
396 nsIPrincipal* aPrincipal, nsPIDOMWindowInner* aWindow,
397 const nsACString& aPrefName, const nsACString& aType)
398 : mPrincipal(aPrincipal),
399 mTopLevelPrincipal(aWindow ? ::GetTopLevelPrincipal(aWindow) : nullptr),
400 mWindow(aWindow),
401 mPrefName(aPrefName),
402 mType(aType),
403 mHasValidTransientUserGestureActivation(false),
404 mIsRequestDelegatedToUnsafeThirdParty(false) {
405 if (!aWindow) {
406 return;
409 Document* doc = aWindow->GetExtantDoc();
410 if (!doc) {
411 return;
414 mHasValidTransientUserGestureActivation =
415 doc->HasValidTransientUserGestureActivation();
417 mPermissionHandler = doc->GetPermissionDelegateHandler();
418 if (mPermissionHandler) {
419 nsTArray<nsCString> types;
420 types.AppendElement(mType);
421 mPermissionHandler->MaybeUnsafePermissionDelegate(
422 types, &mIsRequestDelegatedToUnsafeThirdParty);
426 NS_IMETHODIMP
427 ContentPermissionRequestBase::GetPrincipal(
428 nsIPrincipal** aRequestingPrincipal) {
429 NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal);
430 return NS_OK;
433 NS_IMETHODIMP
434 ContentPermissionRequestBase::GetDelegatePrincipal(
435 const nsACString& aType, nsIPrincipal** aRequestingPrincipal) {
436 return PermissionDelegateHandler::GetDelegatePrincipal(aType, this,
437 aRequestingPrincipal);
440 NS_IMETHODIMP
441 ContentPermissionRequestBase::GetIsRequestDelegatedToUnsafeThirdParty(
442 bool* aIsRequestDelegatedToUnsafeThirdParty) {
443 *aIsRequestDelegatedToUnsafeThirdParty =
444 mIsRequestDelegatedToUnsafeThirdParty;
445 return NS_OK;
448 NS_IMETHODIMP
449 ContentPermissionRequestBase::GetTopLevelPrincipal(
450 nsIPrincipal** aRequestingPrincipal) {
451 if (!mTopLevelPrincipal) {
452 *aRequestingPrincipal = nullptr;
453 return NS_OK;
456 NS_IF_ADDREF(*aRequestingPrincipal = mTopLevelPrincipal);
457 return NS_OK;
460 NS_IMETHODIMP
461 ContentPermissionRequestBase::GetWindow(mozIDOMWindow** aRequestingWindow) {
462 NS_IF_ADDREF(*aRequestingWindow = mWindow);
463 return NS_OK;
466 NS_IMETHODIMP
467 ContentPermissionRequestBase::GetElement(Element** aElement) {
468 NS_ENSURE_ARG_POINTER(aElement);
469 *aElement = nullptr;
470 return NS_OK;
473 NS_IMETHODIMP
474 ContentPermissionRequestBase::GetHasValidTransientUserGestureActivation(
475 bool* aHasValidTransientUserGestureActivation) {
476 *aHasValidTransientUserGestureActivation =
477 mHasValidTransientUserGestureActivation;
478 return NS_OK;
481 NS_IMETHODIMP
482 ContentPermissionRequestBase::GetTypes(nsIArray** aTypes) {
483 nsTArray<nsString> emptyOptions;
484 return nsContentPermissionUtils::CreatePermissionArray(mType, emptyOptions,
485 aTypes);
488 ContentPermissionRequestBase::PromptResult
489 ContentPermissionRequestBase::CheckPromptPrefs() const {
490 MOZ_ASSERT(!mPrefName.IsEmpty(),
491 "This derived class must support checking pref types");
493 nsAutoCString prefName(mPrefName);
494 prefName.AppendLiteral(".prompt.testing");
495 if (Preferences::GetBool(PromiseFlatCString(prefName).get(), false)) {
496 prefName.AppendLiteral(".allow");
497 if (Preferences::GetBool(PromiseFlatCString(prefName).get(), true)) {
498 return PromptResult::Granted;
500 return PromptResult::Denied;
503 return PromptResult::Pending;
506 bool ContentPermissionRequestBase::CheckPermissionDelegate() const {
507 // There is case that ContentPermissionRequestBase is constructed without
508 // window, then mPermissionHandler will be null. So we only check permission
509 // delegate if we have non-null mPermissionHandler
510 if (mPermissionHandler &&
511 !mPermissionHandler->HasPermissionDelegated(mType)) {
512 return false;
515 return true;
518 nsresult ContentPermissionRequestBase::ShowPrompt(
519 ContentPermissionRequestBase::PromptResult& aResult) {
520 if (!CheckPermissionDelegate()) {
521 aResult = PromptResult::Denied;
522 return NS_OK;
525 aResult = CheckPromptPrefs();
527 if (aResult != PromptResult::Pending) {
528 return NS_OK;
531 return nsContentPermissionUtils::AskPermission(this, mWindow);
534 class RequestPromptEvent : public Runnable {
535 public:
536 RequestPromptEvent(ContentPermissionRequestBase* aRequest,
537 nsPIDOMWindowInner* aWindow)
538 : mozilla::Runnable("RequestPromptEvent"),
539 mRequest(aRequest),
540 mWindow(aWindow) {}
542 NS_IMETHOD Run() override {
543 nsContentPermissionUtils::AskPermission(mRequest, mWindow);
544 return NS_OK;
547 private:
548 RefPtr<ContentPermissionRequestBase> mRequest;
549 nsCOMPtr<nsPIDOMWindowInner> mWindow;
552 class RequestAllowEvent : public Runnable {
553 public:
554 RequestAllowEvent(bool allow, ContentPermissionRequestBase* request)
555 : mozilla::Runnable("RequestAllowEvent"),
556 mAllow(allow),
557 mRequest(request) {}
559 // Not MOZ_CAN_RUN_SCRIPT because we can't annotate the thing we override yet.
560 MOZ_CAN_RUN_SCRIPT_BOUNDARY
561 NS_IMETHOD Run() override {
562 // MOZ_KnownLive is OK, because we never drop the ref to mRequest.
563 if (mAllow) {
564 MOZ_KnownLive(mRequest)->Allow(JS::UndefinedHandleValue);
565 } else {
566 MOZ_KnownLive(mRequest)->Cancel();
568 return NS_OK;
571 private:
572 bool mAllow;
573 RefPtr<ContentPermissionRequestBase> mRequest;
576 void ContentPermissionRequestBase::RequestDelayedTask(
577 nsIEventTarget* aTarget,
578 ContentPermissionRequestBase::DelayedTaskType aType) {
579 nsCOMPtr<nsIRunnable> r;
580 switch (aType) {
581 case DelayedTaskType::Allow:
582 r = new RequestAllowEvent(true, this);
583 break;
584 case DelayedTaskType::Deny:
585 r = new RequestAllowEvent(false, this);
586 break;
587 default:
588 r = new RequestPromptEvent(this, mWindow);
589 break;
592 aTarget->Dispatch(r.forget());
595 nsresult TranslateChoices(
596 JS::Handle<JS::Value> aChoices,
597 const nsTArray<PermissionRequest>& aPermissionRequests,
598 nsTArray<PermissionChoice>& aTranslatedChoices) {
599 if (aChoices.isNullOrUndefined()) {
600 // No choice is specified.
601 } else if (aChoices.isObject()) {
602 // Iterate through all permission types.
603 for (uint32_t i = 0; i < aPermissionRequests.Length(); ++i) {
604 nsCString type = aPermissionRequests[i].type();
606 JS::Rooted<JSObject*> obj(RootingCx(), &aChoices.toObject());
607 // People really shouldn't be passing WindowProxy or Location
608 // objects for the choices here.
609 obj = js::CheckedUnwrapStatic(obj);
610 if (!obj) {
611 return NS_ERROR_FAILURE;
614 AutoJSAPI jsapi;
615 jsapi.Init();
617 JSContext* cx = jsapi.cx();
618 JSAutoRealm ar(cx, obj);
620 JS::Rooted<JS::Value> val(cx);
622 if (!JS_GetProperty(cx, obj, type.BeginReading(), &val) ||
623 !val.isString()) {
624 // no setting for the permission type, clear exception and skip it
625 jsapi.ClearException();
626 } else {
627 nsAutoJSString choice;
628 if (!choice.init(cx, val)) {
629 jsapi.ClearException();
630 return NS_ERROR_FAILURE;
632 aTranslatedChoices.AppendElement(PermissionChoice(type, choice));
635 } else {
636 MOZ_ASSERT(false, "SelectedChoices should be undefined or an JS object");
637 return NS_ERROR_FAILURE;
640 return NS_OK;
643 } // namespace mozilla::dom
645 nsContentPermissionRequestProxy::nsContentPermissionRequestProxy(
646 ContentPermissionRequestParent* parent)
647 : mParent(parent) {
648 NS_ASSERTION(mParent, "null parent");
651 nsContentPermissionRequestProxy::~nsContentPermissionRequestProxy() = default;
653 nsresult nsContentPermissionRequestProxy::Init(
654 const nsTArray<PermissionRequest>& requests) {
655 mPermissionRequests = requests.Clone();
657 nsCOMPtr<nsIContentPermissionPrompt> prompt =
658 do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
659 if (!prompt) {
660 return NS_ERROR_FAILURE;
663 prompt->Prompt(this);
664 return NS_OK;
667 void nsContentPermissionRequestProxy::OnParentDestroyed() { mParent = nullptr; }
669 NS_IMPL_ISUPPORTS(nsContentPermissionRequestProxy, nsIContentPermissionRequest)
671 NS_IMETHODIMP
672 nsContentPermissionRequestProxy::GetTypes(nsIArray** aTypes) {
673 nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
674 if (mozilla::dom::nsContentPermissionUtils::ConvertPermissionRequestToArray(
675 mPermissionRequests, types)) {
676 types.forget(aTypes);
677 return NS_OK;
679 return NS_ERROR_FAILURE;
682 NS_IMETHODIMP
683 nsContentPermissionRequestProxy::GetWindow(mozIDOMWindow** aRequestingWindow) {
684 NS_ENSURE_ARG_POINTER(aRequestingWindow);
685 *aRequestingWindow = nullptr; // ipc doesn't have a window
686 return NS_OK;
689 NS_IMETHODIMP
690 nsContentPermissionRequestProxy::GetPrincipal(
691 nsIPrincipal** aRequestingPrincipal) {
692 NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
693 if (mParent == nullptr) {
694 return NS_ERROR_FAILURE;
697 NS_IF_ADDREF(*aRequestingPrincipal = mParent->mPrincipal);
698 return NS_OK;
701 NS_IMETHODIMP
702 nsContentPermissionRequestProxy::GetTopLevelPrincipal(
703 nsIPrincipal** aRequestingPrincipal) {
704 NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
705 if (mParent == nullptr) {
706 return NS_ERROR_FAILURE;
709 NS_IF_ADDREF(*aRequestingPrincipal = mParent->mTopLevelPrincipal);
710 return NS_OK;
713 NS_IMETHODIMP
714 nsContentPermissionRequestProxy::GetDelegatePrincipal(
715 const nsACString& aType, nsIPrincipal** aRequestingPrincipal) {
716 NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
717 if (mParent == nullptr) {
718 return NS_ERROR_FAILURE;
721 return PermissionDelegateHandler::GetDelegatePrincipal(aType, this,
722 aRequestingPrincipal);
725 NS_IMETHODIMP
726 nsContentPermissionRequestProxy::GetElement(Element** aRequestingElement) {
727 NS_ENSURE_ARG_POINTER(aRequestingElement);
728 if (mParent == nullptr) {
729 return NS_ERROR_FAILURE;
732 nsCOMPtr<Element> elem = mParent->mElement;
733 elem.forget(aRequestingElement);
734 return NS_OK;
737 NS_IMETHODIMP
738 nsContentPermissionRequestProxy::GetHasValidTransientUserGestureActivation(
739 bool* aHasValidTransientUserGestureActivation) {
740 NS_ENSURE_ARG_POINTER(aHasValidTransientUserGestureActivation);
741 if (mParent == nullptr) {
742 return NS_ERROR_FAILURE;
744 *aHasValidTransientUserGestureActivation =
745 mParent->mHasValidTransientUserGestureActivation;
746 return NS_OK;
749 NS_IMETHODIMP
750 nsContentPermissionRequestProxy::GetIsRequestDelegatedToUnsafeThirdParty(
751 bool* aIsRequestDelegatedToUnsafeThirdParty) {
752 NS_ENSURE_ARG_POINTER(aIsRequestDelegatedToUnsafeThirdParty);
753 if (mParent == nullptr) {
754 return NS_ERROR_FAILURE;
756 *aIsRequestDelegatedToUnsafeThirdParty =
757 mParent->mIsRequestDelegatedToUnsafeThirdParty;
758 return NS_OK;
761 NS_IMETHODIMP
762 nsContentPermissionRequestProxy::Cancel() {
763 if (mParent == nullptr) {
764 return NS_ERROR_FAILURE;
767 // Don't send out the delete message when the managing protocol (PBrowser) is
768 // being destroyed and PContentPermissionRequest will soon be.
769 if (mParent->IsBeingDestroyed()) {
770 return NS_ERROR_FAILURE;
773 nsTArray<PermissionChoice> emptyChoices;
775 Unused << mParent->SendNotifyResult(false, emptyChoices);
776 return NS_OK;
779 NS_IMETHODIMP
780 nsContentPermissionRequestProxy::Allow(JS::Handle<JS::Value> aChoices) {
781 if (mParent == nullptr) {
782 return NS_ERROR_FAILURE;
785 // Don't send out the delete message when the managing protocol (PBrowser) is
786 // being destroyed and PContentPermissionRequest will soon be.
787 if (mParent->IsBeingDestroyed()) {
788 return NS_ERROR_FAILURE;
791 nsTArray<PermissionChoice> choices;
792 nsresult rv = TranslateChoices(aChoices, mPermissionRequests, choices);
793 if (NS_FAILED(rv)) {
794 return rv;
797 Unused << mParent->SendNotifyResult(true, choices);
798 return NS_OK;
801 // RemotePermissionRequest
803 RemotePermissionRequest::RemotePermissionRequest(
804 nsIContentPermissionRequest* aRequest, nsPIDOMWindowInner* aWindow)
805 : mRequest(aRequest),
806 mWindow(aWindow),
807 mIPCOpen(false),
808 mDestroyed(false) {}
810 RemotePermissionRequest::~RemotePermissionRequest() {
811 MOZ_ASSERT(
812 !mIPCOpen,
813 "Protocol must not be open when RemotePermissionRequest is destroyed.");
816 void RemotePermissionRequest::DoCancel() {
817 NS_ASSERTION(mRequest, "We need a request");
818 nsCOMPtr<nsIContentPermissionRequest> request(mRequest);
819 request->Cancel();
822 void RemotePermissionRequest::DoAllow(JS::Handle<JS::Value> aChoices) {
823 NS_ASSERTION(mRequest, "We need a request");
824 nsCOMPtr<nsIContentPermissionRequest> request(mRequest);
825 request->Allow(aChoices);
828 // PContentPermissionRequestChild
829 mozilla::ipc::IPCResult RemotePermissionRequest::RecvNotifyResult(
830 const bool& aAllow, nsTArray<PermissionChoice>&& aChoices) {
831 Destroy();
833 if (aAllow && mWindow->IsCurrentInnerWindow()) {
834 // Use 'undefined' if no choice is provided.
835 if (aChoices.IsEmpty()) {
836 DoAllow(JS::UndefinedHandleValue);
837 return IPC_OK();
840 // Convert choices to a JS val if any.
841 // {"type1": "choice1", "type2": "choiceA"}
842 AutoJSAPI jsapi;
843 if (NS_WARN_IF(!jsapi.Init(mWindow))) {
844 return IPC_OK(); // This is not an IPC error.
847 JSContext* cx = jsapi.cx();
848 JS::Rooted<JSObject*> obj(cx);
849 obj = JS_NewPlainObject(cx);
850 for (uint32_t i = 0; i < aChoices.Length(); ++i) {
851 const nsString& choice = aChoices[i].choice();
852 const nsCString& type = aChoices[i].type();
853 JS::Rooted<JSString*> jChoice(
854 cx, JS_NewUCStringCopyN(cx, choice.get(), choice.Length()));
855 JS::Rooted<JS::Value> vChoice(cx, StringValue(jChoice));
856 if (!JS_SetProperty(cx, obj, type.get(), vChoice)) {
857 return IPC_FAIL_NO_REASON(this);
860 JS::Rooted<JS::Value> val(cx, JS::ObjectValue(*obj));
861 DoAllow(val);
862 } else {
863 DoCancel();
865 return IPC_OK();
868 void RemotePermissionRequest::Destroy() {
869 if (!IPCOpen()) {
870 return;
872 Unused << this->SendDestroy();
873 mDestroyed = true;