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/. */
6 
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 "nsJSUtils.h"
28 #include "nsISupportsPrimitives.h"
29 #include "nsServiceManagerUtils.h"
30 #include "mozilla/dom/Document.h"
31 #include "nsIWeakReferenceUtils.h"
32 #include "js/PropertyAndElement.h"  // JS_GetProperty, JS_SetProperty
33 
34 using mozilla::Unused;  // <snicker>
35 using namespace mozilla::dom;
36 using namespace mozilla;
37 using DelegateInfo = PermissionDelegateHandler::PermissionDelegateInfo;
38 
39 namespace mozilla::dom {
40 
41 class ContentPermissionRequestParent : public PContentPermissionRequestParent {
42  public:
43   // @param aIsRequestDelegatedToUnsafeThirdParty see
44   // mIsRequestDelegatedToUnsafeThirdParty.
45   ContentPermissionRequestParent(
46       const nsTArray<PermissionRequest>& aRequests, Element* aElement,
47       nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal,
48       const bool aHasValidTransientUserGestureActivation,
49       const bool aIsRequestDelegatedToUnsafeThirdParty);
50   virtual ~ContentPermissionRequestParent();
51 
52   bool IsBeingDestroyed();
53 
54   nsCOMPtr<nsIPrincipal> mPrincipal;
55   nsCOMPtr<nsIPrincipal> mTopLevelPrincipal;
56   nsCOMPtr<Element> mElement;
57   bool mHasValidTransientUserGestureActivation;
58 
59   // See nsIPermissionDelegateHandler.maybeUnsafePermissionDelegate.
60   bool mIsRequestDelegatedToUnsafeThirdParty;
61 
62   RefPtr<nsContentPermissionRequestProxy> mProxy;
63   nsTArray<PermissionRequest> mRequests;
64 
65  private:
66   // Not MOZ_CAN_RUN_SCRIPT because we can't annotate the thing we override yet.
67   MOZ_CAN_RUN_SCRIPT_BOUNDARY
68   virtual mozilla::ipc::IPCResult Recvprompt() override;
69   virtual mozilla::ipc::IPCResult RecvDestroy() override;
70   virtual void ActorDestroy(ActorDestroyReason why) override;
71 };
72 
ContentPermissionRequestParent(const nsTArray<PermissionRequest> & aRequests,Element * aElement,nsIPrincipal * aPrincipal,nsIPrincipal * aTopLevelPrincipal,const bool aHasValidTransientUserGestureActivation,const bool aIsRequestDelegatedToUnsafeThirdParty)73 ContentPermissionRequestParent::ContentPermissionRequestParent(
74     const nsTArray<PermissionRequest>& aRequests, Element* aElement,
75     nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal,
76     const bool aHasValidTransientUserGestureActivation,
77     const bool aIsRequestDelegatedToUnsafeThirdParty) {
78   MOZ_COUNT_CTOR(ContentPermissionRequestParent);
79 
80   mPrincipal = aPrincipal;
81   mTopLevelPrincipal = aTopLevelPrincipal;
82   mElement = aElement;
83   mRequests = aRequests.Clone();
84   mHasValidTransientUserGestureActivation =
85       aHasValidTransientUserGestureActivation;
86   mIsRequestDelegatedToUnsafeThirdParty = aIsRequestDelegatedToUnsafeThirdParty;
87 }
88 
~ContentPermissionRequestParent()89 ContentPermissionRequestParent::~ContentPermissionRequestParent() {
90   MOZ_COUNT_DTOR(ContentPermissionRequestParent);
91 }
92 
Recvprompt()93 mozilla::ipc::IPCResult ContentPermissionRequestParent::Recvprompt() {
94   mProxy = new nsContentPermissionRequestProxy(this);
95   if (NS_FAILED(mProxy->Init(mRequests))) {
96     RefPtr<nsContentPermissionRequestProxy> proxy(mProxy);
97     proxy->Cancel();
98   }
99   return IPC_OK();
100 }
101 
RecvDestroy()102 mozilla::ipc::IPCResult ContentPermissionRequestParent::RecvDestroy() {
103   Unused << PContentPermissionRequestParent::Send__delete__(this);
104   return IPC_OK();
105 }
106 
ActorDestroy(ActorDestroyReason why)107 void ContentPermissionRequestParent::ActorDestroy(ActorDestroyReason why) {
108   if (mProxy) {
109     mProxy->OnParentDestroyed();
110   }
111 }
112 
IsBeingDestroyed()113 bool ContentPermissionRequestParent::IsBeingDestroyed() {
114   // When ContentParent::MarkAsDead() is called, we are being destroyed.
115   // It's unsafe to send out any message now.
116   ContentParent* contentParent = static_cast<ContentParent*>(Manager());
117   return !contentParent->IsAlive();
118 }
119 
NS_IMPL_ISUPPORTS(ContentPermissionType,nsIContentPermissionType)120 NS_IMPL_ISUPPORTS(ContentPermissionType, nsIContentPermissionType)
121 
122 ContentPermissionType::ContentPermissionType(
123     const nsACString& aType, const nsTArray<nsString>& aOptions) {
124   mType = aType;
125   mOptions = aOptions.Clone();
126 }
127 
128 ContentPermissionType::~ContentPermissionType() = default;
129 
130 NS_IMETHODIMP
GetType(nsACString & aType)131 ContentPermissionType::GetType(nsACString& aType) {
132   aType = mType;
133   return NS_OK;
134 }
135 
136 NS_IMETHODIMP
GetOptions(nsIArray ** aOptions)137 ContentPermissionType::GetOptions(nsIArray** aOptions) {
138   NS_ENSURE_ARG_POINTER(aOptions);
139 
140   *aOptions = nullptr;
141 
142   nsresult rv;
143   nsCOMPtr<nsIMutableArray> options =
144       do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
145   NS_ENSURE_SUCCESS(rv, rv);
146 
147   // copy options into JS array
148   for (uint32_t i = 0; i < mOptions.Length(); ++i) {
149     nsCOMPtr<nsISupportsString> isupportsString =
150         do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
151     NS_ENSURE_SUCCESS(rv, rv);
152 
153     rv = isupportsString->SetData(mOptions[i]);
154     NS_ENSURE_SUCCESS(rv, rv);
155 
156     rv = options->AppendElement(isupportsString);
157     NS_ENSURE_SUCCESS(rv, rv);
158   }
159 
160   options.forget(aOptions);
161   return NS_OK;
162 }
163 
164 // nsContentPermissionUtils
165 
166 /* static */
ConvertPermissionRequestToArray(nsTArray<PermissionRequest> & aSrcArray,nsIMutableArray * aDesArray)167 uint32_t nsContentPermissionUtils::ConvertPermissionRequestToArray(
168     nsTArray<PermissionRequest>& aSrcArray, nsIMutableArray* aDesArray) {
169   uint32_t len = aSrcArray.Length();
170   for (uint32_t i = 0; i < len; i++) {
171     RefPtr<ContentPermissionType> cpt =
172         new ContentPermissionType(aSrcArray[i].type(), aSrcArray[i].options());
173     aDesArray->AppendElement(cpt);
174   }
175   return len;
176 }
177 
178 /* static */
ConvertArrayToPermissionRequest(nsIArray * aSrcArray,nsTArray<PermissionRequest> & aDesArray)179 void nsContentPermissionUtils::ConvertArrayToPermissionRequest(
180     nsIArray* aSrcArray, nsTArray<PermissionRequest>& aDesArray) {
181   uint32_t len = 0;
182   aSrcArray->GetLength(&len);
183   for (uint32_t i = 0; i < len; i++) {
184     nsCOMPtr<nsIContentPermissionType> cpt = do_QueryElementAt(aSrcArray, i);
185     nsAutoCString type;
186     cpt->GetType(type);
187 
188     nsCOMPtr<nsIArray> optionArray;
189     cpt->GetOptions(getter_AddRefs(optionArray));
190     uint32_t optionsLength = 0;
191     if (optionArray) {
192       optionArray->GetLength(&optionsLength);
193     }
194     nsTArray<nsString> options;
195     for (uint32_t j = 0; j < optionsLength; ++j) {
196       nsCOMPtr<nsISupportsString> isupportsString =
197           do_QueryElementAt(optionArray, j);
198       if (isupportsString) {
199         nsString option;
200         isupportsString->GetData(option);
201         options.AppendElement(option);
202       }
203     }
204 
205     aDesArray.AppendElement(PermissionRequest(type, options));
206   }
207 }
208 
209 static std::map<PContentPermissionRequestParent*, TabId>&
ContentPermissionRequestParentMap()210 ContentPermissionRequestParentMap() {
211   MOZ_ASSERT(NS_IsMainThread());
212   static std::map<PContentPermissionRequestParent*, TabId>
213       sPermissionRequestParentMap;
214   return sPermissionRequestParentMap;
215 }
216 
217 static std::map<PContentPermissionRequestChild*, TabId>&
ContentPermissionRequestChildMap()218 ContentPermissionRequestChildMap() {
219   MOZ_ASSERT(NS_IsMainThread());
220   static std::map<PContentPermissionRequestChild*, TabId>
221       sPermissionRequestChildMap;
222   return sPermissionRequestChildMap;
223 }
224 
225 /* static */
CreatePermissionArray(const nsACString & aType,const nsTArray<nsString> & aOptions,nsIArray ** aTypesArray)226 nsresult nsContentPermissionUtils::CreatePermissionArray(
227     const nsACString& aType, const nsTArray<nsString>& aOptions,
228     nsIArray** aTypesArray) {
229   nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
230   RefPtr<ContentPermissionType> permType =
231       new ContentPermissionType(aType, aOptions);
232   types->AppendElement(permType);
233   types.forget(aTypesArray);
234 
235   return NS_OK;
236 }
237 
238 /* static */
239 PContentPermissionRequestParent*
CreateContentPermissionRequestParent(const nsTArray<PermissionRequest> & aRequests,Element * aElement,nsIPrincipal * aPrincipal,nsIPrincipal * aTopLevelPrincipal,const bool aHasValidTransientUserGestureActivation,const bool aIsRequestDelegatedToUnsafeThirdParty,const TabId & aTabId)240 nsContentPermissionUtils::CreateContentPermissionRequestParent(
241     const nsTArray<PermissionRequest>& aRequests, Element* aElement,
242     nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal,
243     const bool aHasValidTransientUserGestureActivation,
244     const bool aIsRequestDelegatedToUnsafeThirdParty, const TabId& aTabId) {
245   PContentPermissionRequestParent* parent = new ContentPermissionRequestParent(
246       aRequests, aElement, aPrincipal, aTopLevelPrincipal,
247       aHasValidTransientUserGestureActivation,
248       aIsRequestDelegatedToUnsafeThirdParty);
249   ContentPermissionRequestParentMap()[parent] = aTabId;
250 
251   return parent;
252 }
253 
254 /* static */
AskPermission(nsIContentPermissionRequest * aRequest,nsPIDOMWindowInner * aWindow)255 nsresult nsContentPermissionUtils::AskPermission(
256     nsIContentPermissionRequest* aRequest, nsPIDOMWindowInner* aWindow) {
257   NS_ENSURE_STATE(aWindow && aWindow->IsCurrentInnerWindow());
258 
259   // for content process
260   if (XRE_IsContentProcess()) {
261     RefPtr<RemotePermissionRequest> req =
262         new RemotePermissionRequest(aRequest, aWindow);
263 
264     MOZ_ASSERT(NS_IsMainThread());  // IPC can only be execute on main thread.
265 
266     BrowserChild* child = BrowserChild::GetFrom(aWindow->GetDocShell());
267     NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
268 
269     nsCOMPtr<nsIArray> typeArray;
270     nsresult rv = aRequest->GetTypes(getter_AddRefs(typeArray));
271     NS_ENSURE_SUCCESS(rv, rv);
272 
273     nsTArray<PermissionRequest> permArray;
274     ConvertArrayToPermissionRequest(typeArray, permArray);
275 
276     nsCOMPtr<nsIPrincipal> principal;
277     rv = aRequest->GetPrincipal(getter_AddRefs(principal));
278     NS_ENSURE_SUCCESS(rv, rv);
279 
280     nsCOMPtr<nsIPrincipal> topLevelPrincipal;
281     rv = aRequest->GetTopLevelPrincipal(getter_AddRefs(topLevelPrincipal));
282     NS_ENSURE_SUCCESS(rv, rv);
283 
284     bool hasValidTransientUserGestureActivation;
285     rv = aRequest->GetHasValidTransientUserGestureActivation(
286         &hasValidTransientUserGestureActivation);
287     NS_ENSURE_SUCCESS(rv, rv);
288 
289     bool isRequestDelegatedToUnsafeThirdParty;
290     rv = aRequest->GetIsRequestDelegatedToUnsafeThirdParty(
291         &isRequestDelegatedToUnsafeThirdParty);
292     NS_ENSURE_SUCCESS(rv, rv);
293 
294     req->IPDLAddRef();
295     ContentChild::GetSingleton()->SendPContentPermissionRequestConstructor(
296         req, permArray, IPC::Principal(principal),
297         IPC::Principal(topLevelPrincipal),
298         hasValidTransientUserGestureActivation,
299         isRequestDelegatedToUnsafeThirdParty, child->GetTabId());
300     ContentPermissionRequestChildMap()[req.get()] = child->GetTabId();
301 
302     req->Sendprompt();
303     return NS_OK;
304   }
305 
306   // for chrome process
307   nsCOMPtr<nsIContentPermissionPrompt> prompt =
308       do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
309   if (prompt) {
310     if (NS_FAILED(prompt->Prompt(aRequest))) {
311       return NS_ERROR_FAILURE;
312     }
313   }
314   return NS_OK;
315 }
316 
317 /* static */
318 nsTArray<PContentPermissionRequestParent*>
GetContentPermissionRequestParentById(const TabId & aTabId)319 nsContentPermissionUtils::GetContentPermissionRequestParentById(
320     const TabId& aTabId) {
321   nsTArray<PContentPermissionRequestParent*> parentArray;
322   for (auto& it : ContentPermissionRequestParentMap()) {
323     if (it.second == aTabId) {
324       parentArray.AppendElement(it.first);
325     }
326   }
327 
328   return parentArray;
329 }
330 
331 /* static */
NotifyRemoveContentPermissionRequestParent(PContentPermissionRequestParent * aParent)332 void nsContentPermissionUtils::NotifyRemoveContentPermissionRequestParent(
333     PContentPermissionRequestParent* aParent) {
334   auto it = ContentPermissionRequestParentMap().find(aParent);
335   MOZ_ASSERT(it != ContentPermissionRequestParentMap().end());
336 
337   ContentPermissionRequestParentMap().erase(it);
338 }
339 
340 /* static */
341 nsTArray<PContentPermissionRequestChild*>
GetContentPermissionRequestChildById(const TabId & aTabId)342 nsContentPermissionUtils::GetContentPermissionRequestChildById(
343     const TabId& aTabId) {
344   nsTArray<PContentPermissionRequestChild*> childArray;
345   for (auto& it : ContentPermissionRequestChildMap()) {
346     if (it.second == aTabId) {
347       childArray.AppendElement(it.first);
348     }
349   }
350 
351   return childArray;
352 }
353 
354 /* static */
NotifyRemoveContentPermissionRequestChild(PContentPermissionRequestChild * aChild)355 void nsContentPermissionUtils::NotifyRemoveContentPermissionRequestChild(
356     PContentPermissionRequestChild* aChild) {
357   auto it = ContentPermissionRequestChildMap().find(aChild);
358   MOZ_ASSERT(it != ContentPermissionRequestChildMap().end());
359 
360   ContentPermissionRequestChildMap().erase(it);
361 }
362 
GetTopLevelPrincipal(nsPIDOMWindowInner * aWindow)363 static nsIPrincipal* GetTopLevelPrincipal(nsPIDOMWindowInner* aWindow) {
364   MOZ_ASSERT(aWindow);
365 
366   BrowsingContext* top = aWindow->GetBrowsingContext()->Top();
367   MOZ_ASSERT(top);
368 
369   nsPIDOMWindowOuter* outer = top->GetDOMWindow();
370   if (!outer) {
371     return nullptr;
372   }
373 
374   nsPIDOMWindowInner* inner = outer->GetCurrentInnerWindow();
375   if (!inner) {
376     return nullptr;
377   }
378 
379   return nsGlobalWindowInner::Cast(inner)->GetPrincipal();
380 }
381 
NS_IMPL_CYCLE_COLLECTION(ContentPermissionRequestBase,mPrincipal,mTopLevelPrincipal,mWindow)382 NS_IMPL_CYCLE_COLLECTION(ContentPermissionRequestBase, mPrincipal,
383                          mTopLevelPrincipal, mWindow)
384 
385 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ContentPermissionRequestBase)
386   NS_INTERFACE_MAP_ENTRY_CONCRETE(nsISupports)
387   NS_INTERFACE_MAP_ENTRY_CONCRETE(nsIContentPermissionRequest)
388 NS_INTERFACE_MAP_END
389 
390 NS_IMPL_CYCLE_COLLECTING_ADDREF(ContentPermissionRequestBase)
391 NS_IMPL_CYCLE_COLLECTING_RELEASE(ContentPermissionRequestBase)
392 
393 ContentPermissionRequestBase::ContentPermissionRequestBase(
394     nsIPrincipal* aPrincipal, nsPIDOMWindowInner* aWindow,
395     const nsACString& aPrefName, const nsACString& aType)
396     : mPrincipal(aPrincipal),
397       mTopLevelPrincipal(aWindow ? ::GetTopLevelPrincipal(aWindow) : nullptr),
398       mWindow(aWindow),
399       mPrefName(aPrefName),
400       mType(aType),
401       mHasValidTransientUserGestureActivation(false),
402       mIsRequestDelegatedToUnsafeThirdParty(false) {
403   if (!aWindow) {
404     return;
405   }
406 
407   Document* doc = aWindow->GetExtantDoc();
408   if (!doc) {
409     return;
410   }
411 
412   mHasValidTransientUserGestureActivation =
413       doc->HasValidTransientUserGestureActivation();
414 
415   mPermissionHandler = doc->GetPermissionDelegateHandler();
416   if (mPermissionHandler) {
417     nsTArray<nsCString> types;
418     types.AppendElement(mType);
419     mPermissionHandler->MaybeUnsafePermissionDelegate(
420         types, &mIsRequestDelegatedToUnsafeThirdParty);
421   }
422 }
423 
424 NS_IMETHODIMP
GetPrincipal(nsIPrincipal ** aRequestingPrincipal)425 ContentPermissionRequestBase::GetPrincipal(
426     nsIPrincipal** aRequestingPrincipal) {
427   NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal);
428   return NS_OK;
429 }
430 
431 NS_IMETHODIMP
GetDelegatePrincipal(const nsACString & aType,nsIPrincipal ** aRequestingPrincipal)432 ContentPermissionRequestBase::GetDelegatePrincipal(
433     const nsACString& aType, nsIPrincipal** aRequestingPrincipal) {
434   return PermissionDelegateHandler::GetDelegatePrincipal(aType, this,
435                                                          aRequestingPrincipal);
436 }
437 
438 NS_IMETHODIMP
GetIsRequestDelegatedToUnsafeThirdParty(bool * aIsRequestDelegatedToUnsafeThirdParty)439 ContentPermissionRequestBase::GetIsRequestDelegatedToUnsafeThirdParty(
440     bool* aIsRequestDelegatedToUnsafeThirdParty) {
441   *aIsRequestDelegatedToUnsafeThirdParty =
442       mIsRequestDelegatedToUnsafeThirdParty;
443   return NS_OK;
444 }
445 
446 NS_IMETHODIMP
GetTopLevelPrincipal(nsIPrincipal ** aRequestingPrincipal)447 ContentPermissionRequestBase::GetTopLevelPrincipal(
448     nsIPrincipal** aRequestingPrincipal) {
449   if (!mTopLevelPrincipal) {
450     *aRequestingPrincipal = nullptr;
451     return NS_OK;
452   }
453 
454   NS_IF_ADDREF(*aRequestingPrincipal = mTopLevelPrincipal);
455   return NS_OK;
456 }
457 
458 NS_IMETHODIMP
GetWindow(mozIDOMWindow ** aRequestingWindow)459 ContentPermissionRequestBase::GetWindow(mozIDOMWindow** aRequestingWindow) {
460   NS_IF_ADDREF(*aRequestingWindow = mWindow);
461   return NS_OK;
462 }
463 
464 NS_IMETHODIMP
GetElement(Element ** aElement)465 ContentPermissionRequestBase::GetElement(Element** aElement) {
466   NS_ENSURE_ARG_POINTER(aElement);
467   *aElement = nullptr;
468   return NS_OK;
469 }
470 
471 NS_IMETHODIMP
GetHasValidTransientUserGestureActivation(bool * aHasValidTransientUserGestureActivation)472 ContentPermissionRequestBase::GetHasValidTransientUserGestureActivation(
473     bool* aHasValidTransientUserGestureActivation) {
474   *aHasValidTransientUserGestureActivation =
475       mHasValidTransientUserGestureActivation;
476   return NS_OK;
477 }
478 
479 NS_IMETHODIMP
GetTypes(nsIArray ** aTypes)480 ContentPermissionRequestBase::GetTypes(nsIArray** aTypes) {
481   nsTArray<nsString> emptyOptions;
482   return nsContentPermissionUtils::CreatePermissionArray(mType, emptyOptions,
483                                                          aTypes);
484 }
485 
486 ContentPermissionRequestBase::PromptResult
CheckPromptPrefs() const487 ContentPermissionRequestBase::CheckPromptPrefs() const {
488   MOZ_ASSERT(!mPrefName.IsEmpty(),
489              "This derived class must support checking pref types");
490 
491   nsAutoCString prefName(mPrefName);
492   prefName.AppendLiteral(".prompt.testing");
493   if (Preferences::GetBool(PromiseFlatCString(prefName).get(), false)) {
494     prefName.AppendLiteral(".allow");
495     if (Preferences::GetBool(PromiseFlatCString(prefName).get(), true)) {
496       return PromptResult::Granted;
497     }
498     return PromptResult::Denied;
499   }
500 
501   return PromptResult::Pending;
502 }
503 
CheckPermissionDelegate() const504 bool ContentPermissionRequestBase::CheckPermissionDelegate() const {
505   // There is case that ContentPermissionRequestBase is constructed without
506   // window, then mPermissionHandler will be null. So we only check permission
507   // delegate if we have non-null mPermissionHandler
508   if (mPermissionHandler &&
509       !mPermissionHandler->HasPermissionDelegated(mType)) {
510     return false;
511   }
512 
513   return true;
514 }
515 
ShowPrompt(ContentPermissionRequestBase::PromptResult & aResult)516 nsresult ContentPermissionRequestBase::ShowPrompt(
517     ContentPermissionRequestBase::PromptResult& aResult) {
518   if (!CheckPermissionDelegate()) {
519     aResult = PromptResult::Denied;
520     return NS_OK;
521   }
522 
523   aResult = CheckPromptPrefs();
524 
525   if (aResult != PromptResult::Pending) {
526     return NS_OK;
527   }
528 
529   return nsContentPermissionUtils::AskPermission(this, mWindow);
530 }
531 
532 class RequestPromptEvent : public Runnable {
533  public:
RequestPromptEvent(ContentPermissionRequestBase * aRequest,nsPIDOMWindowInner * aWindow)534   RequestPromptEvent(ContentPermissionRequestBase* aRequest,
535                      nsPIDOMWindowInner* aWindow)
536       : mozilla::Runnable("RequestPromptEvent"),
537         mRequest(aRequest),
538         mWindow(aWindow) {}
539 
Run()540   NS_IMETHOD Run() override {
541     nsContentPermissionUtils::AskPermission(mRequest, mWindow);
542     return NS_OK;
543   }
544 
545  private:
546   RefPtr<ContentPermissionRequestBase> mRequest;
547   nsCOMPtr<nsPIDOMWindowInner> mWindow;
548 };
549 
550 class RequestAllowEvent : public Runnable {
551  public:
RequestAllowEvent(bool allow,ContentPermissionRequestBase * request)552   RequestAllowEvent(bool allow, ContentPermissionRequestBase* request)
553       : mozilla::Runnable("RequestAllowEvent"),
554         mAllow(allow),
555         mRequest(request) {}
556 
557   // Not MOZ_CAN_RUN_SCRIPT because we can't annotate the thing we override yet.
558   MOZ_CAN_RUN_SCRIPT_BOUNDARY
Run()559   NS_IMETHOD Run() override {
560     // MOZ_KnownLive is OK, because we never drop the ref to mRequest.
561     if (mAllow) {
562       MOZ_KnownLive(mRequest)->Allow(JS::UndefinedHandleValue);
563     } else {
564       MOZ_KnownLive(mRequest)->Cancel();
565     }
566     return NS_OK;
567   }
568 
569  private:
570   bool mAllow;
571   RefPtr<ContentPermissionRequestBase> mRequest;
572 };
573 
RequestDelayedTask(nsIEventTarget * aTarget,ContentPermissionRequestBase::DelayedTaskType aType)574 void ContentPermissionRequestBase::RequestDelayedTask(
575     nsIEventTarget* aTarget,
576     ContentPermissionRequestBase::DelayedTaskType aType) {
577   nsCOMPtr<nsIRunnable> r;
578   switch (aType) {
579     case DelayedTaskType::Allow:
580       r = new RequestAllowEvent(true, this);
581       break;
582     case DelayedTaskType::Deny:
583       r = new RequestAllowEvent(false, this);
584       break;
585     default:
586       r = new RequestPromptEvent(this, mWindow);
587       break;
588   }
589 
590   aTarget->Dispatch(r.forget());
591 }
592 
TranslateChoices(JS::HandleValue aChoices,const nsTArray<PermissionRequest> & aPermissionRequests,nsTArray<PermissionChoice> & aTranslatedChoices)593 nsresult TranslateChoices(
594     JS::HandleValue aChoices,
595     const nsTArray<PermissionRequest>& aPermissionRequests,
596     nsTArray<PermissionChoice>& aTranslatedChoices) {
597   if (aChoices.isNullOrUndefined()) {
598     // No choice is specified.
599   } else if (aChoices.isObject()) {
600     // Iterate through all permission types.
601     for (uint32_t i = 0; i < aPermissionRequests.Length(); ++i) {
602       nsCString type = aPermissionRequests[i].type();
603 
604       JS::Rooted<JSObject*> obj(RootingCx(), &aChoices.toObject());
605       // People really shouldn't be passing WindowProxy or Location
606       // objects for the choices here.
607       obj = js::CheckedUnwrapStatic(obj);
608       if (!obj) {
609         return NS_ERROR_FAILURE;
610       }
611 
612       AutoJSAPI jsapi;
613       jsapi.Init();
614 
615       JSContext* cx = jsapi.cx();
616       JSAutoRealm ar(cx, obj);
617 
618       JS::Rooted<JS::Value> val(cx);
619 
620       if (!JS_GetProperty(cx, obj, type.BeginReading(), &val) ||
621           !val.isString()) {
622         // no setting for the permission type, clear exception and skip it
623         jsapi.ClearException();
624       } else {
625         nsAutoJSString choice;
626         if (!choice.init(cx, val)) {
627           jsapi.ClearException();
628           return NS_ERROR_FAILURE;
629         }
630         aTranslatedChoices.AppendElement(PermissionChoice(type, choice));
631       }
632     }
633   } else {
634     MOZ_ASSERT(false, "SelectedChoices should be undefined or an JS object");
635     return NS_ERROR_FAILURE;
636   }
637 
638   return NS_OK;
639 }
640 
641 }  // namespace mozilla::dom
642 
nsContentPermissionRequestProxy(ContentPermissionRequestParent * parent)643 nsContentPermissionRequestProxy::nsContentPermissionRequestProxy(
644     ContentPermissionRequestParent* parent)
645     : mParent(parent) {
646   NS_ASSERTION(mParent, "null parent");
647 }
648 
649 nsContentPermissionRequestProxy::~nsContentPermissionRequestProxy() = default;
650 
Init(const nsTArray<PermissionRequest> & requests)651 nsresult nsContentPermissionRequestProxy::Init(
652     const nsTArray<PermissionRequest>& requests) {
653   mPermissionRequests = requests.Clone();
654 
655   nsCOMPtr<nsIContentPermissionPrompt> prompt =
656       do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
657   if (!prompt) {
658     return NS_ERROR_FAILURE;
659   }
660 
661   prompt->Prompt(this);
662   return NS_OK;
663 }
664 
OnParentDestroyed()665 void nsContentPermissionRequestProxy::OnParentDestroyed() { mParent = nullptr; }
666 
NS_IMPL_ISUPPORTS(nsContentPermissionRequestProxy,nsIContentPermissionRequest)667 NS_IMPL_ISUPPORTS(nsContentPermissionRequestProxy, nsIContentPermissionRequest)
668 
669 NS_IMETHODIMP
670 nsContentPermissionRequestProxy::GetTypes(nsIArray** aTypes) {
671   nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
672   if (mozilla::dom::nsContentPermissionUtils::ConvertPermissionRequestToArray(
673           mPermissionRequests, types)) {
674     types.forget(aTypes);
675     return NS_OK;
676   }
677   return NS_ERROR_FAILURE;
678 }
679 
680 NS_IMETHODIMP
GetWindow(mozIDOMWindow ** aRequestingWindow)681 nsContentPermissionRequestProxy::GetWindow(mozIDOMWindow** aRequestingWindow) {
682   NS_ENSURE_ARG_POINTER(aRequestingWindow);
683   *aRequestingWindow = nullptr;  // ipc doesn't have a window
684   return NS_OK;
685 }
686 
687 NS_IMETHODIMP
GetPrincipal(nsIPrincipal ** aRequestingPrincipal)688 nsContentPermissionRequestProxy::GetPrincipal(
689     nsIPrincipal** aRequestingPrincipal) {
690   NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
691   if (mParent == nullptr) {
692     return NS_ERROR_FAILURE;
693   }
694 
695   NS_ADDREF(*aRequestingPrincipal = mParent->mPrincipal);
696   return NS_OK;
697 }
698 
699 NS_IMETHODIMP
GetTopLevelPrincipal(nsIPrincipal ** aRequestingPrincipal)700 nsContentPermissionRequestProxy::GetTopLevelPrincipal(
701     nsIPrincipal** aRequestingPrincipal) {
702   NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
703   if (mParent == nullptr) {
704     return NS_ERROR_FAILURE;
705   }
706 
707   if (!mParent->mTopLevelPrincipal) {
708     *aRequestingPrincipal = nullptr;
709     return NS_OK;
710   }
711 
712   NS_ADDREF(*aRequestingPrincipal = mParent->mTopLevelPrincipal);
713   return NS_OK;
714 }
715 
716 NS_IMETHODIMP
GetDelegatePrincipal(const nsACString & aType,nsIPrincipal ** aRequestingPrincipal)717 nsContentPermissionRequestProxy::GetDelegatePrincipal(
718     const nsACString& aType, nsIPrincipal** aRequestingPrincipal) {
719   NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
720   if (mParent == nullptr) {
721     return NS_ERROR_FAILURE;
722   }
723 
724   return PermissionDelegateHandler::GetDelegatePrincipal(aType, this,
725                                                          aRequestingPrincipal);
726 }
727 
728 NS_IMETHODIMP
GetElement(Element ** aRequestingElement)729 nsContentPermissionRequestProxy::GetElement(Element** aRequestingElement) {
730   NS_ENSURE_ARG_POINTER(aRequestingElement);
731   if (mParent == nullptr) {
732     return NS_ERROR_FAILURE;
733   }
734 
735   nsCOMPtr<Element> elem = mParent->mElement;
736   elem.forget(aRequestingElement);
737   return NS_OK;
738 }
739 
740 NS_IMETHODIMP
GetHasValidTransientUserGestureActivation(bool * aHasValidTransientUserGestureActivation)741 nsContentPermissionRequestProxy::GetHasValidTransientUserGestureActivation(
742     bool* aHasValidTransientUserGestureActivation) {
743   NS_ENSURE_ARG_POINTER(aHasValidTransientUserGestureActivation);
744   if (mParent == nullptr) {
745     return NS_ERROR_FAILURE;
746   }
747   *aHasValidTransientUserGestureActivation =
748       mParent->mHasValidTransientUserGestureActivation;
749   return NS_OK;
750 }
751 
752 NS_IMETHODIMP
GetIsRequestDelegatedToUnsafeThirdParty(bool * aIsRequestDelegatedToUnsafeThirdParty)753 nsContentPermissionRequestProxy::GetIsRequestDelegatedToUnsafeThirdParty(
754     bool* aIsRequestDelegatedToUnsafeThirdParty) {
755   NS_ENSURE_ARG_POINTER(aIsRequestDelegatedToUnsafeThirdParty);
756   if (mParent == nullptr) {
757     return NS_ERROR_FAILURE;
758   }
759   *aIsRequestDelegatedToUnsafeThirdParty =
760       mParent->mIsRequestDelegatedToUnsafeThirdParty;
761   return NS_OK;
762 }
763 
764 NS_IMETHODIMP
Cancel()765 nsContentPermissionRequestProxy::Cancel() {
766   if (mParent == nullptr) {
767     return NS_ERROR_FAILURE;
768   }
769 
770   // Don't send out the delete message when the managing protocol (PBrowser) is
771   // being destroyed and PContentPermissionRequest will soon be.
772   if (mParent->IsBeingDestroyed()) {
773     return NS_ERROR_FAILURE;
774   }
775 
776   nsTArray<PermissionChoice> emptyChoices;
777 
778   Unused << mParent->SendNotifyResult(false, emptyChoices);
779   return NS_OK;
780 }
781 
782 NS_IMETHODIMP
Allow(JS::HandleValue aChoices)783 nsContentPermissionRequestProxy::Allow(JS::HandleValue aChoices) {
784   if (mParent == nullptr) {
785     return NS_ERROR_FAILURE;
786   }
787 
788   // Don't send out the delete message when the managing protocol (PBrowser) is
789   // being destroyed and PContentPermissionRequest will soon be.
790   if (mParent->IsBeingDestroyed()) {
791     return NS_ERROR_FAILURE;
792   }
793 
794   nsTArray<PermissionChoice> choices;
795   nsresult rv = TranslateChoices(aChoices, mPermissionRequests, choices);
796   if (NS_FAILED(rv)) {
797     return rv;
798   }
799 
800   Unused << mParent->SendNotifyResult(true, choices);
801   return NS_OK;
802 }
803 
804 // RemotePermissionRequest
805 
RemotePermissionRequest(nsIContentPermissionRequest * aRequest,nsPIDOMWindowInner * aWindow)806 RemotePermissionRequest::RemotePermissionRequest(
807     nsIContentPermissionRequest* aRequest, nsPIDOMWindowInner* aWindow)
808     : mRequest(aRequest),
809       mWindow(aWindow),
810       mIPCOpen(false),
811       mDestroyed(false) {}
812 
~RemotePermissionRequest()813 RemotePermissionRequest::~RemotePermissionRequest() {
814   MOZ_ASSERT(
815       !mIPCOpen,
816       "Protocol must not be open when RemotePermissionRequest is destroyed.");
817 }
818 
DoCancel()819 void RemotePermissionRequest::DoCancel() {
820   NS_ASSERTION(mRequest, "We need a request");
821   nsCOMPtr<nsIContentPermissionRequest> request(mRequest);
822   request->Cancel();
823 }
824 
DoAllow(JS::HandleValue aChoices)825 void RemotePermissionRequest::DoAllow(JS::HandleValue aChoices) {
826   NS_ASSERTION(mRequest, "We need a request");
827   nsCOMPtr<nsIContentPermissionRequest> request(mRequest);
828   request->Allow(aChoices);
829 }
830 
831 // PContentPermissionRequestChild
RecvNotifyResult(const bool & aAllow,nsTArray<PermissionChoice> && aChoices)832 mozilla::ipc::IPCResult RemotePermissionRequest::RecvNotifyResult(
833     const bool& aAllow, nsTArray<PermissionChoice>&& aChoices) {
834   Destroy();
835 
836   if (aAllow && mWindow->IsCurrentInnerWindow()) {
837     // Use 'undefined' if no choice is provided.
838     if (aChoices.IsEmpty()) {
839       DoAllow(JS::UndefinedHandleValue);
840       return IPC_OK();
841     }
842 
843     // Convert choices to a JS val if any.
844     // {"type1": "choice1", "type2": "choiceA"}
845     AutoJSAPI jsapi;
846     if (NS_WARN_IF(!jsapi.Init(mWindow))) {
847       return IPC_OK();  // This is not an IPC error.
848     }
849 
850     JSContext* cx = jsapi.cx();
851     JS::Rooted<JSObject*> obj(cx);
852     obj = JS_NewPlainObject(cx);
853     for (uint32_t i = 0; i < aChoices.Length(); ++i) {
854       const nsString& choice = aChoices[i].choice();
855       const nsCString& type = aChoices[i].type();
856       JS::Rooted<JSString*> jChoice(
857           cx, JS_NewUCStringCopyN(cx, choice.get(), choice.Length()));
858       JS::Rooted<JS::Value> vChoice(cx, StringValue(jChoice));
859       if (!JS_SetProperty(cx, obj, type.get(), vChoice)) {
860         return IPC_FAIL_NO_REASON(this);
861       }
862     }
863     JS::RootedValue val(cx, JS::ObjectValue(*obj));
864     DoAllow(val);
865   } else {
866     DoCancel();
867   }
868   return IPC_OK();
869 }
870 
Destroy()871 void RemotePermissionRequest::Destroy() {
872   if (!IPCOpen()) {
873     return;
874   }
875   Unused << this->SendDestroy();
876   mDestroyed = true;
877 }
878