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