tor-browser

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

ClientOpenWindowUtils.cpp (19298B)


      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 "ClientOpenWindowUtils.h"
      8 
      9 #include "ClientInfo.h"
     10 #include "ClientManager.h"
     11 #include "ClientState.h"
     12 #include "mozilla/NullPrincipal.h"
     13 #include "mozilla/dom/BrowserParent.h"
     14 #include "mozilla/dom/BrowsingContext.h"
     15 #include "mozilla/dom/CanonicalBrowsingContext.h"
     16 #include "mozilla/dom/ContentParent.h"
     17 #include "mozilla/dom/PolicyContainer.h"
     18 #include "mozilla/dom/WindowGlobalParent.h"
     19 #include "mozilla/dom/nsCSPContext.h"
     20 #include "nsContentUtils.h"
     21 #include "nsDocShell.h"
     22 #include "nsDocShellLoadState.h"
     23 #include "nsFocusManager.h"
     24 #include "nsGlobalWindowOuter.h"
     25 #include "nsIBrowser.h"
     26 #include "nsIBrowserDOMWindow.h"
     27 #include "nsIDocShell.h"
     28 #include "nsIDocShellTreeOwner.h"
     29 #include "nsIMutableArray.h"
     30 #include "nsISupportsPrimitives.h"
     31 #include "nsIURI.h"
     32 #include "nsIWebProgress.h"
     33 #include "nsIWebProgressListener.h"
     34 #include "nsIWindowMediator.h"
     35 #include "nsIWindowWatcher.h"
     36 #include "nsIXPConnect.h"
     37 #include "nsNetUtil.h"
     38 #include "nsOpenWindowInfo.h"
     39 #include "nsPIDOMWindow.h"
     40 #include "nsPIWindowWatcher.h"
     41 #include "nsPrintfCString.h"
     42 #include "nsWindowWatcher.h"
     43 
     44 #ifdef MOZ_GECKOVIEW
     45 #  include "mozilla/dom/Promise-inl.h"
     46 #  include "nsIGeckoViewServiceWorker.h"
     47 #  include "nsImportModule.h"
     48 #endif
     49 
     50 namespace mozilla::dom {
     51 
     52 namespace {
     53 
     54 class WebProgressListener final : public nsIWebProgressListener,
     55                                  public nsSupportsWeakReference {
     56 public:
     57  NS_DECL_ISUPPORTS
     58 
     59  WebProgressListener(BrowsingContext* aBrowsingContext, nsIURI* aBaseURI,
     60                      already_AddRefed<ClientOpPromise::Private> aPromise)
     61      : mPromise(aPromise),
     62        mBaseURI(aBaseURI),
     63        mBrowserId(aBrowsingContext->GetBrowserId()) {
     64    MOZ_ASSERT(mBrowserId != 0);
     65    MOZ_ASSERT(aBaseURI);
     66    MOZ_ASSERT(NS_IsMainThread());
     67  }
     68 
     69  NS_IMETHOD
     70  OnStateChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
     71                uint32_t aStateFlags, nsresult aStatus) override {
     72    if (!(aStateFlags & STATE_IS_WINDOW) ||
     73        !(aStateFlags & (STATE_STOP | STATE_TRANSFERRING))) {
     74      return NS_OK;
     75    }
     76 
     77    // Our browsing context may have been discarded before finishing the load,
     78    // this is a navigation error.
     79    RefPtr<CanonicalBrowsingContext> browsingContext =
     80        CanonicalBrowsingContext::Cast(
     81            BrowsingContext::GetCurrentTopByBrowserId(mBrowserId));
     82    if (!browsingContext || browsingContext->IsDiscarded()) {
     83      CopyableErrorResult rv;
     84      rv.ThrowInvalidStateError("Unable to open window");
     85      mPromise->Reject(rv, __func__);
     86      mPromise = nullptr;
     87      return NS_OK;
     88    }
     89 
     90    // Our caller keeps a strong reference, so it is safe to remove the listener
     91    // from the BrowsingContext's nsIWebProgress.
     92    auto RemoveListener = [&] {
     93      nsCOMPtr<nsIWebProgress> webProgress = browsingContext->GetWebProgress();
     94      webProgress->RemoveProgressListener(this);
     95    };
     96 
     97    RefPtr<dom::WindowGlobalParent> wgp =
     98        browsingContext->GetCurrentWindowGlobal();
     99    if (NS_WARN_IF(!wgp)) {
    100      CopyableErrorResult rv;
    101      rv.ThrowInvalidStateError("Unable to open window");
    102      mPromise->Reject(rv, __func__);
    103      mPromise = nullptr;
    104      RemoveListener();
    105      return NS_OK;
    106    }
    107 
    108    if (NS_WARN_IF(wgp->IsInitialDocument())) {
    109      // This is the load of the initial document, which is not the document we
    110      // care about for the purposes of checking same-originness of the URL.
    111      return NS_OK;
    112    }
    113 
    114    RemoveListener();
    115 
    116    // Check same origin. If the origins do not match, resolve with null (per
    117    // step 7.2.7.1 of the openWindow spec).
    118    nsCOMPtr<nsIScriptSecurityManager> securityManager =
    119        nsContentUtils::GetSecurityManager();
    120    bool isPrivateWin =
    121        wgp->DocumentPrincipal()->OriginAttributesRef().IsPrivateBrowsing();
    122    nsresult rv = securityManager->CheckSameOriginURI(
    123        wgp->GetDocumentURI(), mBaseURI, false, isPrivateWin);
    124    if (NS_WARN_IF(NS_FAILED(rv))) {
    125      mPromise->Resolve(CopyableErrorResult(), __func__);
    126      mPromise = nullptr;
    127      return NS_OK;
    128    }
    129 
    130    Maybe<ClientInfo> info = wgp->GetClientInfo();
    131    if (info.isNothing()) {
    132      CopyableErrorResult rv;
    133      rv.ThrowInvalidStateError("Unable to open window");
    134      mPromise->Reject(rv, __func__);
    135      mPromise = nullptr;
    136      return NS_OK;
    137    }
    138 
    139    const nsID& id = info.ref().Id();
    140    const mozilla::ipc::PrincipalInfo& principal = info.ref().PrincipalInfo();
    141    ClientManager::GetInfoAndState(ClientGetInfoAndStateArgs(id, principal),
    142                                   GetCurrentSerialEventTarget())
    143        ->ChainTo(mPromise.forget(), __func__);
    144 
    145    return NS_OK;
    146  }
    147 
    148  NS_IMETHOD
    149  OnProgressChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
    150                   int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
    151                   int32_t aCurTotalProgress,
    152                   int32_t aMaxTotalProgress) override {
    153    MOZ_ASSERT(false, "Unexpected notification.");
    154    return NS_OK;
    155  }
    156 
    157  NS_IMETHOD
    158  OnLocationChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
    159                   nsIURI* aLocation, uint32_t aFlags) override {
    160    MOZ_ASSERT(false, "Unexpected notification.");
    161    return NS_OK;
    162  }
    163 
    164  NS_IMETHOD
    165  OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
    166                 nsresult aStatus, const char16_t* aMessage) override {
    167    MOZ_ASSERT(false, "Unexpected notification.");
    168    return NS_OK;
    169  }
    170 
    171  NS_IMETHOD
    172  OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
    173                   uint32_t aState) override {
    174    MOZ_ASSERT(false, "Unexpected notification.");
    175    return NS_OK;
    176  }
    177 
    178  NS_IMETHOD
    179  OnContentBlockingEvent(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
    180                         uint32_t aEvent) override {
    181    MOZ_ASSERT(false, "Unexpected notification.");
    182    return NS_OK;
    183  }
    184 
    185 private:
    186  ~WebProgressListener() {
    187    if (mPromise) {
    188      CopyableErrorResult rv;
    189      rv.ThrowAbortError("openWindow aborted");
    190      mPromise->Reject(rv, __func__);
    191      mPromise = nullptr;
    192    }
    193  }
    194 
    195  RefPtr<ClientOpPromise::Private> mPromise;
    196  nsCOMPtr<nsIURI> mBaseURI;
    197  uint64_t mBrowserId;
    198 };
    199 
    200 NS_IMPL_ISUPPORTS(WebProgressListener, nsIWebProgressListener,
    201                  nsISupportsWeakReference);
    202 
    203 struct ClientOpenWindowArgsParsed {
    204  nsCOMPtr<nsIURI> uri;
    205  nsCOMPtr<nsIURI> baseURI;
    206  nsCOMPtr<nsIPrincipal> principal;
    207  nsCOMPtr<nsIPolicyContainer> policyContainer;
    208  RefPtr<ThreadsafeContentParentHandle> originContent;
    209 };
    210 
    211 #ifndef MOZ_GECKOVIEW
    212 
    213 static Result<Ok, nsresult> OpenNewWindow(
    214    const ClientOpenWindowArgsParsed& aArgsValidated,
    215    nsOpenWindowInfo* aOpenWindowInfo) {
    216  nsresult rv;
    217 
    218  // XXX(krosylight): In an ideal world we should be able to pass the nsIURI
    219  // directly. See bug 1485961.
    220  nsAutoCString uriToLoad;
    221  MOZ_TRY(aArgsValidated.uri->GetSpec(uriToLoad));
    222 
    223  nsCOMPtr<nsISupportsCString> nsUriToLoad =
    224      do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
    225  MOZ_TRY(rv);
    226  MOZ_TRY(nsUriToLoad->SetData(uriToLoad));
    227 
    228  nsCOMPtr<nsISupportsPRBool> nsFalse =
    229      do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID, &rv);
    230  MOZ_TRY(rv);
    231  MOZ_TRY(nsFalse->SetData(false));
    232 
    233  nsCOMPtr<nsISupportsPRUint32> userContextId =
    234      do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
    235  MOZ_TRY(rv);
    236  MOZ_TRY(userContextId->SetData(aArgsValidated.principal->GetUserContextId()));
    237 
    238  nsCOMPtr<nsIMutableArray> args = do_CreateInstance(NS_ARRAY_CONTRACTID);
    239  // https://searchfox.org/mozilla-central/rev/02d33f4bf984f65bd394bfd2d19d66569ae2cfe1/browser/base/content/browser-init.js#725-735
    240  args->AppendElement(nsUriToLoad);               // 0: uriToLoad
    241  args->AppendElement(nullptr);                   // 1: extraOptions
    242  args->AppendElement(nullptr);                   // 2: referrerInfo
    243  args->AppendElement(nullptr);                   // 3: postData
    244  args->AppendElement(nsFalse);                   // 4: allowThirdPartyFixup
    245  args->AppendElement(userContextId);             // 5: userContextId
    246  args->AppendElement(nullptr);                   // 6: originPrincipal
    247  args->AppendElement(nullptr);                   // 7: originStoragePrincipal
    248  args->AppendElement(aArgsValidated.principal);  // 8: triggeringPrincipal
    249  args->AppendElement(nsFalse);                   // 9: allowInheritPrincipal
    250  args->AppendElement(aArgsValidated.policyContainer);  // 10: policyContainer
    251  args->AppendElement(aOpenWindowInfo);                 // 11: nsOpenWindowInfo
    252 
    253  nsCOMPtr<nsIWindowWatcher> ww = do_GetService(NS_WINDOWWATCHER_CONTRACTID);
    254  nsCString features = "chrome,all,dialog=no"_ns;
    255 
    256  if (aArgsValidated.principal->GetIsInPrivateBrowsing()) {
    257    // Private browsing would generally have a window, but with
    258    // browser.privatebrowsing.autostart=true it's still possible to get into
    259    // this path. See also bug 1972335.
    260    features += ",private";
    261  }
    262 
    263  nsCOMPtr<mozIDOMWindowProxy> win;
    264  MOZ_TRY(ww->OpenWindow(nullptr, nsDependentCString(BROWSER_CHROME_URL_QUOTED),
    265                         "_blank"_ns, features, args, getter_AddRefs(win)));
    266  return Ok();
    267 }
    268 
    269 /**
    270 * @return true when the caller need to load URI on the resulting browsing
    271 *         context, otherwise false
    272 */
    273 bool OpenWindow(const ClientOpenWindowArgsParsed& aArgsValidated,
    274                nsOpenWindowInfo* aOpenInfo, BrowsingContext** aBC,
    275                ErrorResult& aRv) {
    276  MOZ_DIAGNOSTIC_ASSERT(aBC);
    277 
    278  // [[6.1 Open Window]]
    279 
    280  // Find the most recent browser window and open a new tab in it.
    281  WindowMediatorFilter filter = WindowMediatorFilter::SkipClosed;
    282  if (aArgsValidated.principal->GetIsInPrivateBrowsing()) {
    283    filter |= WindowMediatorFilter::SkipNonPrivateBrowsing;
    284  } else {
    285    filter |= WindowMediatorFilter::SkipPrivateBrowsing;
    286  }
    287  nsCOMPtr<nsPIDOMWindowOuter> browserWindow =
    288      nsContentUtils::GetMostRecentWindowBy(filter);
    289  if (!browserWindow) {
    290    // It is possible to be running without a browser window (either because
    291    // macOS hidden window or non-browser windows like Library), so we need to
    292    // open a new chrome window.
    293    auto result = OpenNewWindow(aArgsValidated, aOpenInfo);
    294    if (NS_WARN_IF(result.isErr())) {
    295      aRv.ThrowTypeError("Unable to open window");
    296      return false;
    297    }
    298    return false;
    299  }
    300 
    301  if (NS_WARN_IF(!nsGlobalWindowOuter::Cast(browserWindow)->IsChromeWindow())) {
    302    // XXXbz Can this actually happen?  Seems unlikely.
    303    aRv.ThrowTypeError("Unable to open window");
    304    return false;
    305  }
    306 
    307  nsCOMPtr<nsIBrowserDOMWindow> bwin =
    308      nsGlobalWindowOuter::Cast(browserWindow)->GetBrowserDOMWindow();
    309 
    310  if (NS_WARN_IF(!bwin)) {
    311    aRv.ThrowTypeError("Unable to open window");
    312    return false;
    313  }
    314  nsresult rv = bwin->CreateContentWindow(
    315      nullptr, aOpenInfo, nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW,
    316      nsIBrowserDOMWindow::OPEN_NEW, aArgsValidated.principal,
    317      aArgsValidated.policyContainer, aBC);
    318  if (NS_WARN_IF(NS_FAILED(rv))) {
    319    aRv.ThrowTypeError("Unable to open window");
    320    return false;
    321  }
    322  return true;
    323 }
    324 #endif
    325 
    326 void WaitForLoad(const ClientOpenWindowArgsParsed& aArgsValidated,
    327                 BrowsingContext* aBrowsingContext,
    328                 ClientOpPromise::Private* aPromise, bool aShouldLoadURI) {
    329  MOZ_DIAGNOSTIC_ASSERT(aBrowsingContext);
    330 
    331  RefPtr<ClientOpPromise::Private> promise = aPromise;
    332  // We can get a WebProgress off of
    333  // the BrowsingContext for the <xul:browser> to listen for content
    334  // events. Note that this WebProgress filters out events which don't have
    335  // STATE_IS_NETWORK or STATE_IS_REDIRECTED_DOCUMENT set on them, and so this
    336  // listener will only see some web progress events.
    337  nsCOMPtr<nsIWebProgress> webProgress =
    338      aBrowsingContext->Canonical()->GetWebProgress();
    339  if (NS_WARN_IF(!webProgress)) {
    340    CopyableErrorResult result;
    341    result.ThrowInvalidStateError("Unable to watch window for navigation");
    342    promise->Reject(result, __func__);
    343    return;
    344  }
    345 
    346  // Add a progress listener before we start the load of the requested URI
    347  RefPtr<WebProgressListener> listener = new WebProgressListener(
    348      aBrowsingContext, aArgsValidated.baseURI, do_AddRef(promise));
    349 
    350  nsresult rv = webProgress->AddProgressListener(
    351      listener, nsIWebProgress::NOTIFY_STATE_WINDOW);
    352  if (NS_WARN_IF(NS_FAILED(rv))) {
    353    CopyableErrorResult result;
    354    // XXXbz Can we throw something better here?
    355    result.Throw(rv);
    356    promise->Reject(result, __func__);
    357    return;
    358  }
    359 
    360  if (aShouldLoadURI) {
    361    // Load the requested URI
    362    RefPtr<nsDocShellLoadState> loadState =
    363        new nsDocShellLoadState(aArgsValidated.uri);
    364    loadState->SetTriggeringPrincipal(aArgsValidated.principal);
    365    loadState->SetFirstParty(true);
    366    loadState->SetLoadFlags(
    367        nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL);
    368    loadState->SetTriggeringRemoteType(
    369        aArgsValidated.originContent
    370            ? aArgsValidated.originContent->GetRemoteType()
    371            : NOT_REMOTE_TYPE);
    372 
    373    rv = aBrowsingContext->LoadURI(loadState, true);
    374    if (NS_FAILED(rv)) {
    375      CopyableErrorResult result;
    376      result.ThrowInvalidStateError(
    377          "Unable to start the load of the actual URI");
    378      promise->Reject(result, __func__);
    379      return;
    380    }
    381  }
    382 
    383  // Hold the listener alive until the promise settles.
    384  promise->Then(
    385      GetMainThreadSerialEventTarget(), __func__,
    386      [listener](const ClientOpResult& aResult) {},
    387      [listener](const CopyableErrorResult& aResult) {});
    388 }
    389 
    390 #ifdef MOZ_GECKOVIEW
    391 void GeckoViewOpenWindow(const ClientOpenWindowArgsParsed& aArgsValidated,
    392                         nsOpenWindowInfo* aOpenInfo, ErrorResult& aRv) {
    393  MOZ_ASSERT(aOpenInfo);
    394 
    395  // passes the request to open a new window to GeckoView. Allowing the
    396  // application to decide how to hand the open window request.
    397  nsCOMPtr<nsIGeckoViewServiceWorker> sw = do_ImportESModule(
    398      "resource://gre/modules/GeckoViewServiceWorker.sys.mjs");
    399  MOZ_ASSERT(sw);
    400 
    401  RefPtr<dom::Promise> promise;
    402  nsresult rv =
    403      sw->OpenWindow(aArgsValidated.uri, aOpenInfo, getter_AddRefs(promise));
    404  if (NS_WARN_IF(NS_FAILED(rv))) {
    405    aRv.Throw(rv);
    406    return;
    407  }
    408 
    409  promise->AddCallbacksWithCycleCollectedArgs(
    410      [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
    411         nsOpenWindowInfo* aOpenWindowInfo) {
    412        if (aValue.isNull()) {
    413          // nsIBrowsingContextReadyCallback will be called when browsing
    414          // context is ready
    415          return;
    416        }
    417 
    418        auto cancelOpen =
    419            MakeScopeExit([&aOpenWindowInfo] { aOpenWindowInfo->Cancel(); });
    420 
    421        RefPtr<BrowsingContext> browsingContext;
    422        if (NS_WARN_IF(!aValue.isObject()) ||
    423            NS_WARN_IF(NS_FAILED(
    424                UNWRAP_OBJECT(BrowsingContext, aValue, browsingContext)))) {
    425          return;
    426        }
    427 
    428        if (nsIBrowsingContextReadyCallback* callback =
    429                aOpenWindowInfo->BrowsingContextReadyCallback()) {
    430          callback->BrowsingContextReady(browsingContext);
    431        }
    432        cancelOpen.release();
    433      },
    434      [](JSContext* aContext, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
    435         nsOpenWindowInfo* aOpenWindowInfo) { aOpenWindowInfo->Cancel(); },
    436      RefPtr(aOpenInfo));
    437 }
    438 #endif  // MOZ_GECKOVIEW
    439 
    440 }  // anonymous namespace
    441 
    442 RefPtr<ClientOpPromise> ClientOpenWindow(
    443    ThreadsafeContentParentHandle* aOriginContent,
    444    const ClientOpenWindowArgs& aArgs) {
    445  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
    446 
    447  RefPtr<ClientOpPromise::Private> promise =
    448      new ClientOpPromise::Private(__func__);
    449 
    450  // [[1. Let url be the result of parsing url with entry settings object's API
    451  //   base URL.]]
    452  nsCOMPtr<nsIURI> baseURI;
    453  nsresult rv = NS_NewURI(getter_AddRefs(baseURI), aArgs.baseURL());
    454  if (NS_WARN_IF(NS_FAILED(rv))) {
    455    nsPrintfCString err("Invalid base URL \"%s\"", aArgs.baseURL().get());
    456    CopyableErrorResult errResult;
    457    errResult.ThrowTypeError(err);
    458    promise->Reject(errResult, __func__);
    459    return promise;
    460  }
    461 
    462  nsCOMPtr<nsIURI> uri;
    463  rv = NS_NewURI(getter_AddRefs(uri), aArgs.url(), nullptr, baseURI);
    464  if (NS_WARN_IF(NS_FAILED(rv))) {
    465    nsPrintfCString err("Invalid URL \"%s\"", aArgs.url().get());
    466    CopyableErrorResult errResult;
    467    errResult.ThrowTypeError(err);
    468    promise->Reject(errResult, __func__);
    469    return promise;
    470  }
    471 
    472  auto principalOrErr = PrincipalInfoToPrincipal(aArgs.principalInfo());
    473  if (NS_WARN_IF(principalOrErr.isErr())) {
    474    CopyableErrorResult errResult;
    475    errResult.ThrowTypeError("Failed to obtain principal");
    476    promise->Reject(errResult, __func__);
    477    return promise;
    478  }
    479  nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
    480  MOZ_DIAGNOSTIC_ASSERT(principal);
    481 
    482  nsCOMPtr<nsIContentSecurityPolicy> csp;
    483  nsCOMPtr<PolicyContainer> policyContainer;
    484  if (aArgs.cspInfo().isSome()) {
    485    csp = CSPInfoToCSP(aArgs.cspInfo().ref(), nullptr);
    486    policyContainer = new PolicyContainer();
    487    PolicyContainer::Cast(policyContainer)->SetCSP(csp);
    488  }
    489  ClientOpenWindowArgsParsed argsValidated{
    490      .uri = uri,
    491      .baseURI = baseURI,
    492      .principal = principal,
    493      .policyContainer = policyContainer,
    494      .originContent = aOriginContent,
    495  };
    496 
    497  RefPtr<BrowsingContextCallbackReceivedPromise::Private>
    498      browsingContextReadyPromise =
    499          new BrowsingContextCallbackReceivedPromise::Private(__func__);
    500  RefPtr<nsIBrowsingContextReadyCallback> callback =
    501      new nsBrowsingContextReadyCallback(browsingContextReadyPromise);
    502 
    503  RefPtr<nsOpenWindowInfo> openInfo = new nsOpenWindowInfo();
    504  openInfo->mBrowsingContextReadyCallback = callback;
    505  nsCOMPtr<nsIURI> nullPrincipalURI = NullPrincipal::CreateURI(nullptr);
    506  nsCOMPtr<nsIPrincipal> initialPrincipal =
    507      NullPrincipal::Create(principal->OriginAttributesRef(), nullPrincipalURI);
    508  openInfo->mPrincipalToInheritForAboutBlank = initialPrincipal;
    509  openInfo->mIsRemote = true;
    510 
    511  RefPtr<BrowsingContext> bc;
    512  IgnoredErrorResult errResult;
    513  bool shouldLoadURI = true;
    514 #ifdef MOZ_GECKOVIEW
    515  // GeckoView has a delegation for service worker window.
    516  GeckoViewOpenWindow(argsValidated, openInfo, errResult);
    517 #else
    518  shouldLoadURI =
    519      OpenWindow(argsValidated, openInfo, getter_AddRefs(bc), errResult);
    520 #endif
    521  if (NS_WARN_IF(errResult.Failed())) {
    522    promise->Reject(errResult, __func__);
    523    return promise;
    524  }
    525 
    526  browsingContextReadyPromise->Then(
    527      GetCurrentSerialEventTarget(), __func__,
    528      [argsValidated, promise,
    529       shouldLoadURI](const RefPtr<BrowsingContext>& aBC) {
    530        WaitForLoad(argsValidated, aBC, promise, shouldLoadURI);
    531      },
    532      [promise]() {
    533        // in case of failure, reject the original promise
    534        CopyableErrorResult result;
    535        result.ThrowTypeError("Unable to open window");
    536        promise->Reject(result, __func__);
    537      });
    538  if (bc) {
    539    browsingContextReadyPromise->Resolve(bc, __func__);
    540  }
    541  return promise;
    542 }
    543 
    544 }  // namespace mozilla::dom