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