tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsContentPermissionHelper.cpp (27165B)


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