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 "nsIDOMElement.h"
10 #include "nsIPrincipal.h"
11 #include "mozilla/dom/ContentChild.h"
12 #include "mozilla/dom/ContentParent.h"
13 #include "mozilla/dom/Element.h"
14 #include "mozilla/dom/Event.h"
15 #include "mozilla/dom/PContentPermission.h"
16 #include "mozilla/dom/PermissionMessageUtils.h"
17 #include "mozilla/dom/PContentPermissionRequestParent.h"
18 #include "mozilla/dom/ScriptSettings.h"
19 #include "mozilla/dom/TabChild.h"
20 #include "mozilla/dom/TabParent.h"
21 #include "mozilla/EventStateManager.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 "nsIDocument.h"
31 #include "nsIDOMEvent.h"
32 #include "nsWeakPtr.h"
33
34 using mozilla::Unused; // <snicker>
35 using namespace mozilla::dom;
36 using namespace mozilla;
37
38 #define kVisibilityChange "visibilitychange"
39
40 class VisibilityChangeListener final : public nsIDOMEventListener {
41 public:
42 NS_DECL_ISUPPORTS
43 NS_DECL_NSIDOMEVENTLISTENER
44
45 explicit VisibilityChangeListener(nsPIDOMWindowInner* aWindow);
46
47 void RemoveListener();
48 void SetCallback(nsIContentPermissionRequestCallback* aCallback);
49 already_AddRefed<nsIContentPermissionRequestCallback> GetCallback();
50
51 private:
~VisibilityChangeListener()52 virtual ~VisibilityChangeListener() {}
53
54 nsWeakPtr mWindow;
55 nsCOMPtr<nsIContentPermissionRequestCallback> mCallback;
56 };
57
NS_IMPL_ISUPPORTS(VisibilityChangeListener,nsIDOMEventListener)58 NS_IMPL_ISUPPORTS(VisibilityChangeListener, nsIDOMEventListener)
59
60 VisibilityChangeListener::VisibilityChangeListener(
61 nsPIDOMWindowInner* aWindow) {
62 MOZ_ASSERT(aWindow);
63
64 mWindow = do_GetWeakReference(aWindow);
65 nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
66 if (doc) {
67 doc->AddSystemEventListener(NS_LITERAL_STRING(kVisibilityChange),
68 /* listener */ this,
69 /* use capture */ true,
70 /* wants untrusted */ false);
71 }
72 }
73
74 NS_IMETHODIMP
HandleEvent(nsIDOMEvent * aEvent)75 VisibilityChangeListener::HandleEvent(nsIDOMEvent* aEvent) {
76 nsAutoString type;
77 aEvent->GetType(type);
78 if (!type.EqualsLiteral(kVisibilityChange)) {
79 return NS_ERROR_FAILURE;
80 }
81
82 nsCOMPtr<nsIDocument> doc =
83 do_QueryInterface(aEvent->InternalDOMEvent()->GetTarget());
84 MOZ_ASSERT(doc);
85
86 if (mCallback) {
87 mCallback->NotifyVisibility(!doc->Hidden());
88 }
89
90 return NS_OK;
91 }
92
RemoveListener()93 void VisibilityChangeListener::RemoveListener() {
94 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow);
95 if (!window) {
96 return;
97 }
98
99 nsCOMPtr<EventTarget> target = do_QueryInterface(window->GetExtantDoc());
100 if (target) {
101 target->RemoveSystemEventListener(NS_LITERAL_STRING(kVisibilityChange),
102 /* listener */ this,
103 /* use capture */ true);
104 }
105 }
106
SetCallback(nsIContentPermissionRequestCallback * aCallback)107 void VisibilityChangeListener::SetCallback(
108 nsIContentPermissionRequestCallback* aCallback) {
109 mCallback = aCallback;
110 }
111
112 already_AddRefed<nsIContentPermissionRequestCallback>
GetCallback()113 VisibilityChangeListener::GetCallback() {
114 nsCOMPtr<nsIContentPermissionRequestCallback> callback = mCallback;
115 return callback.forget();
116 }
117
118 namespace mozilla {
119 namespace dom {
120
121 class ContentPermissionRequestParent : public PContentPermissionRequestParent {
122 public:
123 ContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
124 Element* aElement,
125 const IPC::Principal& aPrincipal,
126 const bool aIsHandlingUserInput);
127 virtual ~ContentPermissionRequestParent();
128
129 bool IsBeingDestroyed();
130
131 nsCOMPtr<nsIPrincipal> mPrincipal;
132 nsCOMPtr<Element> mElement;
133 bool mIsHandlingUserInput;
134 RefPtr<nsContentPermissionRequestProxy> mProxy;
135 nsTArray<PermissionRequest> mRequests;
136
137 private:
138 virtual mozilla::ipc::IPCResult Recvprompt() override;
139 virtual mozilla::ipc::IPCResult RecvNotifyVisibility(
140 const bool& aIsVisible) override;
141 virtual mozilla::ipc::IPCResult RecvDestroy() override;
142 virtual void ActorDestroy(ActorDestroyReason why) override;
143 };
144
ContentPermissionRequestParent(const nsTArray<PermissionRequest> & aRequests,Element * aElement,const IPC::Principal & aPrincipal,const bool aIsHandlingUserInput)145 ContentPermissionRequestParent::ContentPermissionRequestParent(
146 const nsTArray<PermissionRequest>& aRequests, Element* aElement,
147 const IPC::Principal& aPrincipal, const bool aIsHandlingUserInput) {
148 MOZ_COUNT_CTOR(ContentPermissionRequestParent);
149
150 mPrincipal = aPrincipal;
151 mElement = aElement;
152 mRequests = aRequests;
153 mIsHandlingUserInput = aIsHandlingUserInput;
154 }
155
~ContentPermissionRequestParent()156 ContentPermissionRequestParent::~ContentPermissionRequestParent() {
157 MOZ_COUNT_DTOR(ContentPermissionRequestParent);
158 }
159
Recvprompt()160 mozilla::ipc::IPCResult ContentPermissionRequestParent::Recvprompt() {
161 mProxy = new nsContentPermissionRequestProxy();
162 if (NS_FAILED(mProxy->Init(mRequests, this))) {
163 mProxy->Cancel();
164 }
165 return IPC_OK();
166 }
167
RecvNotifyVisibility(const bool & aIsVisible)168 mozilla::ipc::IPCResult ContentPermissionRequestParent::RecvNotifyVisibility(
169 const bool& aIsVisible) {
170 if (!mProxy) {
171 return IPC_FAIL_NO_REASON(this);
172 }
173 mProxy->NotifyVisibility(aIsVisible);
174 return IPC_OK();
175 }
176
RecvDestroy()177 mozilla::ipc::IPCResult ContentPermissionRequestParent::RecvDestroy() {
178 Unused << PContentPermissionRequestParent::Send__delete__(this);
179 return IPC_OK();
180 }
181
ActorDestroy(ActorDestroyReason why)182 void ContentPermissionRequestParent::ActorDestroy(ActorDestroyReason why) {
183 if (mProxy) {
184 mProxy->OnParentDestroyed();
185 }
186 }
187
IsBeingDestroyed()188 bool ContentPermissionRequestParent::IsBeingDestroyed() {
189 // When ContentParent::MarkAsDead() is called, we are being destroyed.
190 // It's unsafe to send out any message now.
191 ContentParent* contentParent = static_cast<ContentParent*>(Manager());
192 return !contentParent->IsAlive();
193 }
194
NS_IMPL_ISUPPORTS(ContentPermissionType,nsIContentPermissionType)195 NS_IMPL_ISUPPORTS(ContentPermissionType, nsIContentPermissionType)
196
197 ContentPermissionType::ContentPermissionType(
198 const nsACString& aType, const nsACString& aAccess,
199 const nsTArray<nsString>& aOptions) {
200 mType = aType;
201 mAccess = aAccess;
202 mOptions = aOptions;
203 }
204
~ContentPermissionType()205 ContentPermissionType::~ContentPermissionType() {}
206
207 NS_IMETHODIMP
GetType(nsACString & aType)208 ContentPermissionType::GetType(nsACString& aType) {
209 aType = mType;
210 return NS_OK;
211 }
212
213 NS_IMETHODIMP
GetAccess(nsACString & aAccess)214 ContentPermissionType::GetAccess(nsACString& aAccess) {
215 aAccess = mAccess;
216 return NS_OK;
217 }
218
219 NS_IMETHODIMP
GetOptions(nsIArray ** aOptions)220 ContentPermissionType::GetOptions(nsIArray** aOptions) {
221 NS_ENSURE_ARG_POINTER(aOptions);
222
223 *aOptions = nullptr;
224
225 nsresult rv;
226 nsCOMPtr<nsIMutableArray> options =
227 do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
228 NS_ENSURE_SUCCESS(rv, rv);
229
230 // copy options into JS array
231 for (uint32_t i = 0; i < mOptions.Length(); ++i) {
232 nsCOMPtr<nsISupportsString> isupportsString =
233 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
234 NS_ENSURE_SUCCESS(rv, rv);
235
236 rv = isupportsString->SetData(mOptions[i]);
237 NS_ENSURE_SUCCESS(rv, rv);
238
239 rv = options->AppendElement(isupportsString);
240 NS_ENSURE_SUCCESS(rv, rv);
241 }
242
243 options.forget(aOptions);
244 return NS_OK;
245 }
246
247 // nsContentPermissionUtils
248
ConvertPermissionRequestToArray(nsTArray<PermissionRequest> & aSrcArray,nsIMutableArray * aDesArray)249 /* static */ uint32_t nsContentPermissionUtils::ConvertPermissionRequestToArray(
250 nsTArray<PermissionRequest>& aSrcArray, nsIMutableArray* aDesArray) {
251 uint32_t len = aSrcArray.Length();
252 for (uint32_t i = 0; i < len; i++) {
253 RefPtr<ContentPermissionType> cpt = new ContentPermissionType(
254 aSrcArray[i].type(), aSrcArray[i].access(), aSrcArray[i].options());
255 aDesArray->AppendElement(cpt);
256 }
257 return len;
258 }
259
ConvertArrayToPermissionRequest(nsIArray * aSrcArray,nsTArray<PermissionRequest> & aDesArray)260 /* static */ uint32_t nsContentPermissionUtils::ConvertArrayToPermissionRequest(
261 nsIArray* aSrcArray, nsTArray<PermissionRequest>& aDesArray) {
262 uint32_t len = 0;
263 aSrcArray->GetLength(&len);
264 for (uint32_t i = 0; i < len; i++) {
265 nsCOMPtr<nsIContentPermissionType> cpt = do_QueryElementAt(aSrcArray, i);
266 nsAutoCString type;
267 nsAutoCString access;
268 cpt->GetType(type);
269 cpt->GetAccess(access);
270
271 nsCOMPtr<nsIArray> optionArray;
272 cpt->GetOptions(getter_AddRefs(optionArray));
273 uint32_t optionsLength = 0;
274 if (optionArray) {
275 optionArray->GetLength(&optionsLength);
276 }
277 nsTArray<nsString> options;
278 for (uint32_t j = 0; j < optionsLength; ++j) {
279 nsCOMPtr<nsISupportsString> isupportsString =
280 do_QueryElementAt(optionArray, j);
281 if (isupportsString) {
282 nsString option;
283 isupportsString->GetData(option);
284 options.AppendElement(option);
285 }
286 }
287
288 aDesArray.AppendElement(PermissionRequest(type, access, options));
289 }
290 return len;
291 }
292
293 static std::map<PContentPermissionRequestParent*, TabId>&
ContentPermissionRequestParentMap()294 ContentPermissionRequestParentMap() {
295 MOZ_ASSERT(NS_IsMainThread());
296 static std::map<PContentPermissionRequestParent*, TabId>
297 sPermissionRequestParentMap;
298 return sPermissionRequestParentMap;
299 }
300
301 static std::map<PContentPermissionRequestChild*, TabId>&
ContentPermissionRequestChildMap()302 ContentPermissionRequestChildMap() {
303 MOZ_ASSERT(NS_IsMainThread());
304 static std::map<PContentPermissionRequestChild*, TabId>
305 sPermissionRequestChildMap;
306 return sPermissionRequestChildMap;
307 }
308
CreatePermissionArray(const nsACString & aType,const nsACString & aAccess,const nsTArray<nsString> & aOptions,nsIArray ** aTypesArray)309 /* static */ nsresult nsContentPermissionUtils::CreatePermissionArray(
310 const nsACString& aType, const nsACString& aAccess,
311 const nsTArray<nsString>& aOptions, nsIArray** aTypesArray) {
312 nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
313 RefPtr<ContentPermissionType> permType =
314 new ContentPermissionType(aType, aAccess, aOptions);
315 types->AppendElement(permType);
316 types.forget(aTypesArray);
317
318 return NS_OK;
319 }
320
321 /* static */ PContentPermissionRequestParent*
CreateContentPermissionRequestParent(const nsTArray<PermissionRequest> & aRequests,Element * aElement,const IPC::Principal & aPrincipal,const bool aIsHandlingUserInput,const TabId & aTabId)322 nsContentPermissionUtils::CreateContentPermissionRequestParent(
323 const nsTArray<PermissionRequest>& aRequests, Element* aElement,
324 const IPC::Principal& aPrincipal, const bool aIsHandlingUserInput,
325 const TabId& aTabId) {
326 PContentPermissionRequestParent* parent = new ContentPermissionRequestParent(
327 aRequests, aElement, aPrincipal, aIsHandlingUserInput);
328 ContentPermissionRequestParentMap()[parent] = aTabId;
329
330 return parent;
331 }
332
AskPermission(nsIContentPermissionRequest * aRequest,nsPIDOMWindowInner * aWindow)333 /* static */ nsresult nsContentPermissionUtils::AskPermission(
334 nsIContentPermissionRequest* aRequest, nsPIDOMWindowInner* aWindow) {
335 NS_ENSURE_STATE(aWindow && aWindow->IsCurrentInnerWindow());
336
337 // for content process
338 if (XRE_IsContentProcess()) {
339 RefPtr<RemotePermissionRequest> req =
340 new RemotePermissionRequest(aRequest, aWindow);
341
342 MOZ_ASSERT(NS_IsMainThread()); // IPC can only be execute on main thread.
343
344 TabChild* child = TabChild::GetFrom(aWindow->GetDocShell());
345 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
346
347 nsCOMPtr<nsIArray> typeArray;
348 nsresult rv = aRequest->GetTypes(getter_AddRefs(typeArray));
349 NS_ENSURE_SUCCESS(rv, rv);
350
351 nsTArray<PermissionRequest> permArray;
352 ConvertArrayToPermissionRequest(typeArray, permArray);
353
354 nsCOMPtr<nsIPrincipal> principal;
355 rv = aRequest->GetPrincipal(getter_AddRefs(principal));
356 NS_ENSURE_SUCCESS(rv, rv);
357
358 bool isHandlingUserInput;
359 rv = aRequest->GetIsHandlingUserInput(&isHandlingUserInput);
360 NS_ENSURE_SUCCESS(rv, rv);
361
362 ContentChild::GetSingleton()->SetEventTargetForActor(
363 req, aWindow->EventTargetFor(TaskCategory::Other));
364
365 req->IPDLAddRef();
366 ContentChild::GetSingleton()->SendPContentPermissionRequestConstructor(
367 req, permArray, IPC::Principal(principal), isHandlingUserInput,
368 child->GetTabId());
369 ContentPermissionRequestChildMap()[req.get()] = child->GetTabId();
370
371 req->Sendprompt();
372 return NS_OK;
373 }
374
375 // for chrome process
376 nsCOMPtr<nsIContentPermissionPrompt> prompt =
377 do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
378 if (prompt) {
379 if (NS_FAILED(prompt->Prompt(aRequest))) {
380 return NS_ERROR_FAILURE;
381 }
382 }
383 return NS_OK;
384 }
385
386 /* static */ nsTArray<PContentPermissionRequestParent*>
GetContentPermissionRequestParentById(const TabId & aTabId)387 nsContentPermissionUtils::GetContentPermissionRequestParentById(
388 const TabId& aTabId) {
389 nsTArray<PContentPermissionRequestParent*> parentArray;
390 for (auto& it : ContentPermissionRequestParentMap()) {
391 if (it.second == aTabId) {
392 parentArray.AppendElement(it.first);
393 }
394 }
395
396 return Move(parentArray);
397 }
398
399 /* static */ void
NotifyRemoveContentPermissionRequestParent(PContentPermissionRequestParent * aParent)400 nsContentPermissionUtils::NotifyRemoveContentPermissionRequestParent(
401 PContentPermissionRequestParent* aParent) {
402 auto it = ContentPermissionRequestParentMap().find(aParent);
403 MOZ_ASSERT(it != ContentPermissionRequestParentMap().end());
404
405 ContentPermissionRequestParentMap().erase(it);
406 }
407
408 /* static */ nsTArray<PContentPermissionRequestChild*>
GetContentPermissionRequestChildById(const TabId & aTabId)409 nsContentPermissionUtils::GetContentPermissionRequestChildById(
410 const TabId& aTabId) {
411 nsTArray<PContentPermissionRequestChild*> childArray;
412 for (auto& it : ContentPermissionRequestChildMap()) {
413 if (it.second == aTabId) {
414 childArray.AppendElement(it.first);
415 }
416 }
417
418 return Move(childArray);
419 }
420
421 /* static */ void
NotifyRemoveContentPermissionRequestChild(PContentPermissionRequestChild * aChild)422 nsContentPermissionUtils::NotifyRemoveContentPermissionRequestChild(
423 PContentPermissionRequestChild* aChild) {
424 auto it = ContentPermissionRequestChildMap().find(aChild);
425 MOZ_ASSERT(it != ContentPermissionRequestChildMap().end());
426
427 ContentPermissionRequestChildMap().erase(it);
428 }
429
NS_IMPL_ISUPPORTS(nsContentPermissionRequester,nsIContentPermissionRequester)430 NS_IMPL_ISUPPORTS(nsContentPermissionRequester, nsIContentPermissionRequester)
431
432 nsContentPermissionRequester::nsContentPermissionRequester(
433 nsPIDOMWindowInner* aWindow)
434 : mWindow(do_GetWeakReference(aWindow)),
435 mListener(new VisibilityChangeListener(aWindow)) {}
436
~nsContentPermissionRequester()437 nsContentPermissionRequester::~nsContentPermissionRequester() {
438 mListener->RemoveListener();
439 mListener = nullptr;
440 }
441
442 NS_IMETHODIMP
GetVisibility(nsIContentPermissionRequestCallback * aCallback)443 nsContentPermissionRequester::GetVisibility(
444 nsIContentPermissionRequestCallback* aCallback) {
445 NS_ENSURE_ARG_POINTER(aCallback);
446
447 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow);
448 if (!window) {
449 return NS_ERROR_FAILURE;
450 }
451
452 nsCOMPtr<nsIDocShell> docshell = window->GetDocShell();
453 if (!docshell) {
454 return NS_ERROR_FAILURE;
455 }
456
457 bool isActive = false;
458 docshell->GetIsActive(&isActive);
459 aCallback->NotifyVisibility(isActive);
460 return NS_OK;
461 }
462
463 NS_IMETHODIMP
SetOnVisibilityChange(nsIContentPermissionRequestCallback * aCallback)464 nsContentPermissionRequester::SetOnVisibilityChange(
465 nsIContentPermissionRequestCallback* aCallback) {
466 mListener->SetCallback(aCallback);
467
468 if (!aCallback) {
469 mListener->RemoveListener();
470 }
471
472 return NS_OK;
473 }
474
475 NS_IMETHODIMP
GetOnVisibilityChange(nsIContentPermissionRequestCallback ** aCallback)476 nsContentPermissionRequester::GetOnVisibilityChange(
477 nsIContentPermissionRequestCallback** aCallback) {
478 NS_ENSURE_ARG_POINTER(aCallback);
479
480 nsCOMPtr<nsIContentPermissionRequestCallback> callback =
481 mListener->GetCallback();
482 callback.forget(aCallback);
483 return NS_OK;
484 }
485
486 } // namespace dom
487 } // namespace mozilla
488
NS_IMPL_ISUPPORTS(nsContentPermissionRequestProxy::nsContentPermissionRequesterProxy,nsIContentPermissionRequester)489 NS_IMPL_ISUPPORTS(
490 nsContentPermissionRequestProxy::nsContentPermissionRequesterProxy,
491 nsIContentPermissionRequester)
492
493 NS_IMETHODIMP
494 nsContentPermissionRequestProxy::nsContentPermissionRequesterProxy ::
495 GetVisibility(nsIContentPermissionRequestCallback* aCallback) {
496 NS_ENSURE_ARG_POINTER(aCallback);
497
498 mGetCallback = aCallback;
499 mWaitGettingResult = true;
500 Unused << mParent->SendGetVisibility();
501 return NS_OK;
502 }
503
504 NS_IMETHODIMP
505 nsContentPermissionRequestProxy::nsContentPermissionRequesterProxy ::
SetOnVisibilityChange(nsIContentPermissionRequestCallback * aCallback)506 SetOnVisibilityChange(nsIContentPermissionRequestCallback* aCallback) {
507 mOnChangeCallback = aCallback;
508 return NS_OK;
509 }
510
511 NS_IMETHODIMP
512 nsContentPermissionRequestProxy::nsContentPermissionRequesterProxy ::
GetOnVisibilityChange(nsIContentPermissionRequestCallback ** aCallback)513 GetOnVisibilityChange(nsIContentPermissionRequestCallback** aCallback) {
514 NS_ENSURE_ARG_POINTER(aCallback);
515
516 nsCOMPtr<nsIContentPermissionRequestCallback> callback = mOnChangeCallback;
517 callback.forget(aCallback);
518 return NS_OK;
519 }
520
521 void nsContentPermissionRequestProxy::nsContentPermissionRequesterProxy ::
NotifyVisibilityResult(const bool & aIsVisible)522 NotifyVisibilityResult(const bool& aIsVisible) {
523 if (mWaitGettingResult) {
524 MOZ_ASSERT(mGetCallback);
525 mWaitGettingResult = false;
526 mGetCallback->NotifyVisibility(aIsVisible);
527 return;
528 }
529
530 if (mOnChangeCallback) {
531 mOnChangeCallback->NotifyVisibility(aIsVisible);
532 }
533 }
534
nsContentPermissionRequestProxy()535 nsContentPermissionRequestProxy::nsContentPermissionRequestProxy() {}
536
~nsContentPermissionRequestProxy()537 nsContentPermissionRequestProxy::~nsContentPermissionRequestProxy() {}
538
Init(const nsTArray<PermissionRequest> & requests,ContentPermissionRequestParent * parent)539 nsresult nsContentPermissionRequestProxy::Init(
540 const nsTArray<PermissionRequest>& requests,
541 ContentPermissionRequestParent* parent) {
542 NS_ASSERTION(parent, "null parent");
543 mParent = parent;
544 mPermissionRequests = requests;
545 mRequester = new nsContentPermissionRequesterProxy(mParent);
546
547 nsCOMPtr<nsIContentPermissionPrompt> prompt =
548 do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
549 if (!prompt) {
550 return NS_ERROR_FAILURE;
551 }
552
553 prompt->Prompt(this);
554 return NS_OK;
555 }
556
OnParentDestroyed()557 void nsContentPermissionRequestProxy::OnParentDestroyed() {
558 mRequester = nullptr;
559 mParent = nullptr;
560 }
561
NS_IMPL_ISUPPORTS(nsContentPermissionRequestProxy,nsIContentPermissionRequest)562 NS_IMPL_ISUPPORTS(nsContentPermissionRequestProxy, nsIContentPermissionRequest)
563
564 NS_IMETHODIMP
565 nsContentPermissionRequestProxy::GetTypes(nsIArray** aTypes) {
566 nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID);
567 if (mozilla::dom::nsContentPermissionUtils::ConvertPermissionRequestToArray(
568 mPermissionRequests, types)) {
569 types.forget(aTypes);
570 return NS_OK;
571 }
572 return NS_ERROR_FAILURE;
573 }
574
575 NS_IMETHODIMP
GetWindow(mozIDOMWindow ** aRequestingWindow)576 nsContentPermissionRequestProxy::GetWindow(mozIDOMWindow** aRequestingWindow) {
577 NS_ENSURE_ARG_POINTER(aRequestingWindow);
578 *aRequestingWindow = nullptr; // ipc doesn't have a window
579 return NS_OK;
580 }
581
582 NS_IMETHODIMP
GetPrincipal(nsIPrincipal ** aRequestingPrincipal)583 nsContentPermissionRequestProxy::GetPrincipal(
584 nsIPrincipal** aRequestingPrincipal) {
585 NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
586 if (mParent == nullptr) {
587 return NS_ERROR_FAILURE;
588 }
589
590 NS_ADDREF(*aRequestingPrincipal = mParent->mPrincipal);
591 return NS_OK;
592 }
593
594 NS_IMETHODIMP
GetElement(nsIDOMElement ** aRequestingElement)595 nsContentPermissionRequestProxy::GetElement(
596 nsIDOMElement** aRequestingElement) {
597 NS_ENSURE_ARG_POINTER(aRequestingElement);
598 if (mParent == nullptr) {
599 return NS_ERROR_FAILURE;
600 }
601
602 nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(mParent->mElement);
603 elem.forget(aRequestingElement);
604 return NS_OK;
605 }
606
607 NS_IMETHODIMP
GetIsHandlingUserInput(bool * aIsHandlingUserInput)608 nsContentPermissionRequestProxy::GetIsHandlingUserInput(
609 bool* aIsHandlingUserInput) {
610 NS_ENSURE_ARG_POINTER(aIsHandlingUserInput);
611 if (mParent == nullptr) {
612 return NS_ERROR_FAILURE;
613 }
614 *aIsHandlingUserInput = mParent->mIsHandlingUserInput;
615 return NS_OK;
616 }
617
618 NS_IMETHODIMP
Cancel()619 nsContentPermissionRequestProxy::Cancel() {
620 if (mParent == nullptr) {
621 return NS_ERROR_FAILURE;
622 }
623
624 // Don't send out the delete message when the managing protocol (PBrowser) is
625 // being destroyed and PContentPermissionRequest will soon be.
626 if (mParent->IsBeingDestroyed()) {
627 return NS_ERROR_FAILURE;
628 }
629
630 nsTArray<PermissionChoice> emptyChoices;
631
632 Unused << mParent->SendNotifyResult(false, emptyChoices);
633 mParent = nullptr;
634 return NS_OK;
635 }
636
637 NS_IMETHODIMP
Allow(JS::HandleValue aChoices)638 nsContentPermissionRequestProxy::Allow(JS::HandleValue aChoices) {
639 if (mParent == nullptr) {
640 return NS_ERROR_FAILURE;
641 }
642
643 // Don't send out the delete message when the managing protocol (PBrowser) is
644 // being destroyed and PContentPermissionRequest will soon be.
645 if (mParent->IsBeingDestroyed()) {
646 return NS_ERROR_FAILURE;
647 }
648
649 nsTArray<PermissionChoice> choices;
650 if (aChoices.isNullOrUndefined()) {
651 // No choice is specified.
652 } else if (aChoices.isObject()) {
653 // Iterate through all permission types.
654 for (uint32_t i = 0; i < mPermissionRequests.Length(); ++i) {
655 nsCString type = mPermissionRequests[i].type();
656
657 AutoJSAPI jsapi;
658 jsapi.Init();
659
660 JSContext* cx = jsapi.cx();
661 JS::Rooted<JSObject*> obj(cx, &aChoices.toObject());
662 JSAutoCompartment ac(cx, obj);
663
664 JS::Rooted<JS::Value> val(cx);
665
666 if (!JS_GetProperty(cx, obj, type.BeginReading(), &val) ||
667 !val.isString()) {
668 // no setting for the permission type, clear exception and skip it
669 jsapi.ClearException();
670 } else {
671 nsAutoJSString choice;
672 if (!choice.init(cx, val)) {
673 jsapi.ClearException();
674 return NS_ERROR_FAILURE;
675 }
676 choices.AppendElement(PermissionChoice(type, choice));
677 }
678 }
679 } else {
680 MOZ_ASSERT(false, "SelectedChoices should be undefined or an JS object");
681 return NS_ERROR_FAILURE;
682 }
683
684 Unused << mParent->SendNotifyResult(true, choices);
685 mParent = nullptr;
686 return NS_OK;
687 }
688
NotifyVisibility(const bool & aIsVisible)689 void nsContentPermissionRequestProxy::NotifyVisibility(const bool& aIsVisible) {
690 MOZ_ASSERT(mRequester);
691
692 mRequester->NotifyVisibilityResult(aIsVisible);
693 }
694
695 NS_IMETHODIMP
GetRequester(nsIContentPermissionRequester ** aRequester)696 nsContentPermissionRequestProxy::GetRequester(
697 nsIContentPermissionRequester** aRequester) {
698 NS_ENSURE_ARG_POINTER(aRequester);
699
700 RefPtr<nsContentPermissionRequesterProxy> requester = mRequester;
701 requester.forget(aRequester);
702 return NS_OK;
703 }
704
705 // RemotePermissionRequest
706
707 NS_IMPL_ISUPPORTS(RemotePermissionRequest, nsIContentPermissionRequestCallback);
708
RemotePermissionRequest(nsIContentPermissionRequest * aRequest,nsPIDOMWindowInner * aWindow)709 RemotePermissionRequest::RemotePermissionRequest(
710 nsIContentPermissionRequest* aRequest, nsPIDOMWindowInner* aWindow)
711 : mRequest(aRequest), mWindow(aWindow), mIPCOpen(false), mDestroyed(false) {
712 mListener = new VisibilityChangeListener(mWindow);
713 mListener->SetCallback(this);
714 }
715
~RemotePermissionRequest()716 RemotePermissionRequest::~RemotePermissionRequest() {
717 MOZ_ASSERT(
718 !mIPCOpen,
719 "Protocol must not be open when RemotePermissionRequest is destroyed.");
720 }
721
DoCancel()722 void RemotePermissionRequest::DoCancel() {
723 NS_ASSERTION(mRequest, "We need a request");
724 mRequest->Cancel();
725 }
726
DoAllow(JS::HandleValue aChoices)727 void RemotePermissionRequest::DoAllow(JS::HandleValue aChoices) {
728 NS_ASSERTION(mRequest, "We need a request");
729 mRequest->Allow(aChoices);
730 }
731
732 // PContentPermissionRequestChild
RecvNotifyResult(const bool & aAllow,InfallibleTArray<PermissionChoice> && aChoices)733 mozilla::ipc::IPCResult RemotePermissionRequest::RecvNotifyResult(
734 const bool& aAllow, InfallibleTArray<PermissionChoice>&& aChoices) {
735 Destroy();
736
737 if (aAllow && mWindow->IsCurrentInnerWindow()) {
738 // Use 'undefined' if no choice is provided.
739 if (aChoices.IsEmpty()) {
740 DoAllow(JS::UndefinedHandleValue);
741 return IPC_OK();
742 }
743
744 // Convert choices to a JS val if any.
745 // {"type1": "choice1", "type2": "choiceA"}
746 AutoJSAPI jsapi;
747 if (NS_WARN_IF(!jsapi.Init(mWindow))) {
748 return IPC_OK(); // This is not an IPC error.
749 }
750
751 JSContext* cx = jsapi.cx();
752 JS::Rooted<JSObject*> obj(cx);
753 obj = JS_NewPlainObject(cx);
754 for (uint32_t i = 0; i < aChoices.Length(); ++i) {
755 const nsString& choice = aChoices[i].choice();
756 const nsCString& type = aChoices[i].type();
757 JS::Rooted<JSString*> jChoice(
758 cx, JS_NewUCStringCopyN(cx, choice.get(), choice.Length()));
759 JS::Rooted<JS::Value> vChoice(cx, StringValue(jChoice));
760 if (!JS_SetProperty(cx, obj, type.get(), vChoice)) {
761 return IPC_FAIL_NO_REASON(this);
762 }
763 }
764 JS::RootedValue val(cx, JS::ObjectValue(*obj));
765 DoAllow(val);
766 } else {
767 DoCancel();
768 }
769 return IPC_OK();
770 }
771
RecvGetVisibility()772 mozilla::ipc::IPCResult RemotePermissionRequest::RecvGetVisibility() {
773 nsCOMPtr<nsIDocShell> docshell = mWindow->GetDocShell();
774 if (!docshell) {
775 return IPC_FAIL_NO_REASON(this);
776 }
777
778 bool isActive = false;
779 docshell->GetIsActive(&isActive);
780 Unused << SendNotifyVisibility(isActive);
781 return IPC_OK();
782 }
783
Destroy()784 void RemotePermissionRequest::Destroy() {
785 if (!IPCOpen()) {
786 return;
787 }
788 Unused << this->SendDestroy();
789 mListener->RemoveListener();
790 mListener = nullptr;
791 mDestroyed = true;
792 }
793
794 NS_IMETHODIMP
NotifyVisibility(bool isVisible)795 RemotePermissionRequest::NotifyVisibility(bool isVisible) {
796 if (!IPCOpen()) {
797 return NS_OK;
798 }
799
800 Unused << SendNotifyVisibility(isVisible);
801 return NS_OK;
802 }
803