tor-browser

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

nsFrameLoaderOwner.cpp (15041B)


      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 "nsFrameLoaderOwner.h"
      8 
      9 #include "mozilla/AsyncEventDispatcher.h"
     10 #include "mozilla/EventStateManager.h"
     11 #include "mozilla/Logging.h"
     12 #include "mozilla/ScopeExit.h"
     13 #include "mozilla/StaticPrefs_fission.h"
     14 #include "mozilla/dom/BrowserBridgeChild.h"
     15 #include "mozilla/dom/BrowserBridgeHost.h"
     16 #include "mozilla/dom/BrowserHost.h"
     17 #include "mozilla/dom/BrowserParent.h"
     18 #include "mozilla/dom/BrowsingContext.h"
     19 #include "mozilla/dom/CanonicalBrowsingContext.h"
     20 #include "mozilla/dom/ContentParent.h"
     21 #include "mozilla/dom/FrameLoaderBinding.h"
     22 #include "mozilla/dom/HTMLIFrameElement.h"
     23 #include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
     24 #include "nsFocusManager.h"
     25 #include "nsFrameLoader.h"
     26 #include "nsNetUtil.h"
     27 #include "nsQueryObject.h"
     28 #include "nsSubDocumentFrame.h"
     29 
     30 extern mozilla::LazyLogModule gSHIPBFCacheLog;
     31 
     32 using namespace mozilla;
     33 using namespace mozilla::dom;
     34 
     35 already_AddRefed<nsFrameLoader> nsFrameLoaderOwner::GetFrameLoader() {
     36  return do_AddRef(mFrameLoader);
     37 }
     38 
     39 void nsFrameLoaderOwner::SetFrameLoader(nsFrameLoader* aNewFrameLoader) {
     40  mFrameLoader = aNewFrameLoader;
     41 }
     42 
     43 mozilla::dom::BrowsingContext* nsFrameLoaderOwner::GetBrowsingContext() {
     44  if (mFrameLoader) {
     45    return mFrameLoader->GetBrowsingContext();
     46  }
     47  return nullptr;
     48 }
     49 
     50 mozilla::dom::BrowsingContext* nsFrameLoaderOwner::GetExtantBrowsingContext() {
     51  if (mFrameLoader) {
     52    return mFrameLoader->GetExtantBrowsingContext();
     53  }
     54  return nullptr;
     55 }
     56 
     57 bool nsFrameLoaderOwner::UseRemoteSubframes() {
     58  RefPtr<Element> owner = do_QueryObject(this);
     59 
     60  nsILoadContext* loadContext = owner->OwnerDoc()->GetLoadContext();
     61  MOZ_DIAGNOSTIC_ASSERT(loadContext);
     62 
     63  return loadContext->UseRemoteSubframes();
     64 }
     65 
     66 nsFrameLoaderOwner::ChangeRemotenessContextType
     67 nsFrameLoaderOwner::ShouldPreserveBrowsingContext(
     68    bool aIsRemote, bool aReplaceBrowsingContext) {
     69  if (aReplaceBrowsingContext) {
     70    return ChangeRemotenessContextType::DONT_PRESERVE;
     71  }
     72 
     73  if (XRE_IsParentProcess()) {
     74    // Don't preserve for remote => parent loads.
     75    if (!aIsRemote) {
     76      return ChangeRemotenessContextType::DONT_PRESERVE;
     77    }
     78 
     79    // Don't preserve for parent => remote loads.
     80    if (mFrameLoader && !mFrameLoader->IsRemoteFrame()) {
     81      return ChangeRemotenessContextType::DONT_PRESERVE;
     82    }
     83  }
     84 
     85  return ChangeRemotenessContextType::PRESERVE;
     86 }
     87 
     88 void nsFrameLoaderOwner::ChangeRemotenessCommon(
     89    const ChangeRemotenessContextType& aContextType,
     90    const NavigationIsolationOptions& aOptions, bool aSwitchingInProgressLoad,
     91    bool aIsRemote, BrowsingContextGroup* aGroup,
     92    std::function<void()>& aFrameLoaderInit, mozilla::ErrorResult& aRv) {
     93  MOZ_ASSERT_IF(aGroup, aContextType != ChangeRemotenessContextType::PRESERVE);
     94 
     95  RefPtr<mozilla::dom::BrowsingContext> bc;
     96  bool networkCreated = false;
     97 
     98  // In this case, we're not reparenting a frameloader, we're just destroying
     99  // our current one and creating a new one, so we can use ourselves as the
    100  // owner.
    101  RefPtr<Element> owner = do_QueryObject(this);
    102  MOZ_ASSERT(owner);
    103 
    104  // When we destroy the original frameloader, it will stop blocking the parent
    105  // document's load event, and immediately trigger the load event if there are
    106  // no other blockers. Since we're going to be adding a new blocker as soon as
    107  // we recreate the frame loader, this is not what we want, so add our own
    108  // blocker until the process is complete.
    109  Document* doc = owner->OwnerDoc();
    110  doc->BlockOnload();
    111  auto cleanup = MakeScopeExit([&]() { doc->UnblockOnload(false); });
    112 
    113  // If we store the previous nsFrameLoader in the bfcache, this will be filled
    114  // with the SessionHistoryEntry which now owns the frame.
    115  RefPtr<SessionHistoryEntry> bfcacheEntry;
    116 
    117  {
    118    // Introduce a script blocker to ensure no JS is executed during the
    119    // nsFrameLoader teardown & recreation process. Unload listeners will be run
    120    // for the previous document, and the load will be started for the new one,
    121    // at the end of this block.
    122    nsAutoScriptBlocker sb;
    123 
    124    // If we already have a Frameloader, destroy it, possibly preserving its
    125    // browsing context.
    126    if (mFrameLoader) {
    127      // Calling `GetBrowsingContext` here will force frameloader
    128      // initialization if it hasn't already happened, which we neither need
    129      // or want, so we use the initial (possibly pending) browsing context
    130      // directly, instead.
    131      bc = mFrameLoader->GetMaybePendingBrowsingContext();
    132 
    133      if (nsFocusManager* fm = nsFocusManager::GetFocusManager()) {
    134        fm->FixUpFocusBeforeFrameLoaderChange(*owner, bc);
    135      }
    136 
    137      networkCreated = mFrameLoader->IsNetworkCreated();
    138 
    139      MOZ_ASSERT_IF(aOptions.mTryUseBFCache, aOptions.mReplaceBrowsingContext);
    140      if (aOptions.mTryUseBFCache && bc) {
    141        bfcacheEntry = bc->Canonical()->GetActiveSessionHistoryEntry();
    142        bool useBFCache = bfcacheEntry &&
    143                          bfcacheEntry == aOptions.mActiveSessionHistoryEntry &&
    144                          !bfcacheEntry->GetFrameLoader();
    145        if (useBFCache) {
    146          MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug,
    147                  ("nsFrameLoaderOwner::ChangeRemotenessCommon: store the old "
    148                   "page in bfcache"));
    149          (void)bc->SetIsInBFCache(true);
    150          bfcacheEntry->SetFrameLoader(mFrameLoader);
    151          // Session history owns now the frameloader.
    152          mFrameLoader = nullptr;
    153        }
    154      }
    155 
    156      if (mFrameLoader) {
    157        if (aContextType == ChangeRemotenessContextType::PRESERVE) {
    158          mFrameLoader->SetWillChangeProcess();
    159        }
    160 
    161        // Preserve the networkCreated status, as nsDocShells created after a
    162        // process swap may shouldn't change their dynamically-created status.
    163        mFrameLoader->Destroy(aSwitchingInProgressLoad);
    164        mFrameLoader = nullptr;
    165      }
    166    }
    167 
    168    mFrameLoader = nsFrameLoader::Recreate(
    169        owner, bc, aGroup, aOptions, aIsRemote, networkCreated,
    170        aContextType == ChangeRemotenessContextType::PRESERVE);
    171    if (NS_WARN_IF(!mFrameLoader)) {
    172      aRv.Throw(NS_ERROR_FAILURE);
    173      return;
    174    }
    175 
    176    // Invoke the frame loader initialization callback to perform setup on our
    177    // new nsFrameLoader. This may cause our ErrorResult to become errored, so
    178    // double-check after calling.
    179    aFrameLoaderInit();
    180    if (NS_WARN_IF(aRv.Failed())) {
    181      return;
    182    }
    183  }
    184 
    185  // Now that we have a new FrameLoader, we'll eventually need to reset
    186  // nsSubDocumentFrame to use the new one. We can delay doing this if we're
    187  // keeping our old frameloader around in the BFCache and the new frame hasn't
    188  // presented yet to continue painting the previous document.
    189  const bool retainPaint = bfcacheEntry && mFrameLoader->IsRemoteFrame();
    190  if (!retainPaint) {
    191    MOZ_LOG(
    192        gSHIPBFCacheLog, LogLevel::Debug,
    193        ("Previous frameLoader not entering BFCache - not retaining paint data"
    194         "(bfcacheEntry=%p, isRemoteFrame=%d)",
    195         bfcacheEntry.get(), mFrameLoader->IsRemoteFrame()));
    196  }
    197 
    198  ChangeFrameLoaderCommon(owner, retainPaint);
    199 
    200  UpdateFocusAndMouseEnterStateAfterFrameLoaderChange(owner);
    201 }
    202 
    203 void nsFrameLoaderOwner::ChangeFrameLoaderCommon(Element* aOwner,
    204                                                 bool aRetainPaint) {
    205  // Now that we've got a new FrameLoader, we need to reset our
    206  // nsSubDocumentFrame to use the new FrameLoader.
    207  if (nsSubDocumentFrame* ourFrame = do_QueryFrame(aOwner->GetPrimaryFrame())) {
    208    auto retain = aRetainPaint ? nsSubDocumentFrame::RetainPaintData::Yes
    209                               : nsSubDocumentFrame::RetainPaintData::No;
    210    ourFrame->ResetFrameLoader(retain);
    211  }
    212 
    213  if (aOwner->IsXULElement()) {
    214    // Assuming this element is a XULFrameElement, once we've reset our
    215    // FrameLoader, fire an event to act like we've recreated ourselves, similar
    216    // to what XULFrameElement does after rebinding to the tree.
    217    // ChromeOnlyDispatch is turns on to make sure this isn't fired into
    218    // content.
    219    mozilla::AsyncEventDispatcher::RunDOMEventWhenSafe(
    220        *aOwner, u"XULFrameLoaderCreated"_ns, mozilla::CanBubble::eYes,
    221        mozilla::ChromeOnlyDispatch::eYes);
    222  }
    223 
    224  if (mFrameLoader) {
    225    mFrameLoader->PropagateIsUnderHiddenEmbedderElement(
    226        !aOwner->GetPrimaryFrame() ||
    227        !aOwner->GetPrimaryFrame()->StyleVisibility()->IsVisible());
    228  }
    229 }
    230 
    231 void nsFrameLoaderOwner::UpdateFocusAndMouseEnterStateAfterFrameLoaderChange() {
    232  RefPtr<Element> owner = do_QueryObject(this);
    233  UpdateFocusAndMouseEnterStateAfterFrameLoaderChange(owner);
    234 }
    235 
    236 void nsFrameLoaderOwner::UpdateFocusAndMouseEnterStateAfterFrameLoaderChange(
    237    Element* aOwner) {
    238  // If the element is focused, or the current mouse over target then
    239  // we need to update that state for the new BrowserParent too.
    240  if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
    241    if (fm->GetFocusedElement() == aOwner) {
    242      fm->FixUpFocusAfterFrameLoaderChange(*aOwner);
    243    }
    244  }
    245 
    246  if (aOwner->GetPrimaryFrame()) {
    247    EventStateManager* eventManager =
    248        aOwner->GetPrimaryFrame()->PresContext()->EventStateManager();
    249    eventManager->RecomputeMouseEnterStateForRemoteFrame(*aOwner);
    250  }
    251 }
    252 
    253 void nsFrameLoaderOwner::ChangeRemoteness(
    254    const mozilla::dom::RemotenessOptions& aOptions, mozilla::ErrorResult& rv) {
    255  bool isRemote = !aOptions.mRemoteType.IsEmpty();
    256 
    257  MOZ_RELEASE_ASSERT(mFrameLoader, "Expecting to have mFrameLoader here.");
    258  std::function<void()> frameLoaderInit = [&] {
    259    MOZ_RELEASE_ASSERT(mFrameLoader,
    260                       "Expecting still to have mFrameLoader here.");
    261    if (isRemote) {
    262      mFrameLoader->ConfigRemoteProcess(aOptions.mRemoteType, nullptr);
    263    }
    264 
    265    if (aOptions.mPendingSwitchID.WasPassed()) {
    266      mFrameLoader->ResumeLoad(aOptions.mPendingSwitchID.Value());
    267    } else {
    268      mFrameLoader->LoadFrame(/* aOriginalSrc */ false,
    269                              /* aShouldCheckForRecursion */ false);
    270    }
    271  };
    272 
    273  auto shouldPreserve = ShouldPreserveBrowsingContext(
    274      isRemote, /* replaceBrowsingContext */ false);
    275  NavigationIsolationOptions options;
    276  ChangeRemotenessCommon(shouldPreserve, options,
    277                         aOptions.mSwitchingInProgressLoad, isRemote,
    278                         /* group */ nullptr, frameLoaderInit, rv);
    279 }
    280 
    281 void nsFrameLoaderOwner::ChangeRemotenessWithBridge(BrowserBridgeChild* aBridge,
    282                                                    mozilla::ErrorResult& rv) {
    283  MOZ_ASSERT(XRE_IsContentProcess());
    284  if (NS_WARN_IF(!mFrameLoader)) {
    285    rv.Throw(NS_ERROR_UNEXPECTED);
    286    return;
    287  }
    288 
    289  std::function<void()> frameLoaderInit = [&] {
    290    MOZ_DIAGNOSTIC_ASSERT(!mFrameLoader->mInitialized);
    291    RefPtr<BrowserBridgeHost> host = aBridge->FinishInit(mFrameLoader);
    292    mFrameLoader->mPendingBrowsingContext->SetEmbedderElement(
    293        mFrameLoader->GetOwnerContent());
    294    mFrameLoader->mRemoteBrowser = host;
    295    mFrameLoader->mInitialized = true;
    296  };
    297 
    298  NavigationIsolationOptions options;
    299  ChangeRemotenessCommon(ChangeRemotenessContextType::PRESERVE, options,
    300                         /* inProgress */ true,
    301                         /* isRemote */ true, /* group */ nullptr,
    302                         frameLoaderInit, rv);
    303 }
    304 
    305 void nsFrameLoaderOwner::ChangeRemotenessToProcess(
    306    ContentParent* aContentParent, const NavigationIsolationOptions& aOptions,
    307    BrowsingContextGroup* aGroup, mozilla::ErrorResult& rv) {
    308  MOZ_ASSERT(XRE_IsParentProcess());
    309  MOZ_ASSERT_IF(aGroup, aOptions.mReplaceBrowsingContext);
    310  bool isRemote = aContentParent != nullptr;
    311 
    312  std::function<void()> frameLoaderInit = [&] {
    313    if (isRemote) {
    314      mFrameLoader->ConfigRemoteProcess(aContentParent->GetRemoteType(),
    315                                        aContentParent);
    316    }
    317  };
    318 
    319  auto shouldPreserve =
    320      ShouldPreserveBrowsingContext(isRemote, aOptions.mReplaceBrowsingContext);
    321  ChangeRemotenessCommon(shouldPreserve, aOptions, /* inProgress */ true,
    322                         isRemote, aGroup, frameLoaderInit, rv);
    323 }
    324 
    325 void nsFrameLoaderOwner::SubframeCrashed() {
    326  MOZ_ASSERT(XRE_IsContentProcess());
    327 
    328  std::function<void()> frameLoaderInit = [&] {
    329    RefPtr<nsFrameLoader> frameLoader = mFrameLoader;
    330    nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
    331        "nsFrameLoaderOwner::SubframeCrashed", [frameLoader]() {
    332          nsCOMPtr<nsIURI> uri;
    333          nsresult rv = NS_NewURI(getter_AddRefs(uri), "about:blank");
    334          if (NS_WARN_IF(NS_FAILED(rv))) {
    335            return;
    336          }
    337 
    338          RefPtr<nsDocShell> docShell =
    339              frameLoader->GetDocShell(IgnoreErrors());
    340          if (NS_WARN_IF(!docShell)) {
    341            return;
    342          }
    343          bool displayed = false;
    344          docShell->DisplayLoadError(NS_ERROR_FRAME_CRASHED, uri,
    345                                     u"about:blank", nullptr, &displayed);
    346        }));
    347  };
    348 
    349  NavigationIsolationOptions options;
    350  ChangeRemotenessCommon(ChangeRemotenessContextType::PRESERVE, options,
    351                         /* inProgress */ false, /* isRemote */ false,
    352                         /* group */ nullptr, frameLoaderInit, IgnoreErrors());
    353 }
    354 
    355 void nsFrameLoaderOwner::RestoreFrameLoaderFromBFCache(
    356    nsFrameLoader* aNewFrameLoader) {
    357  MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug,
    358          ("nsFrameLoaderOwner::RestoreFrameLoaderFromBFCache: Replace "
    359           "frameloader"));
    360 
    361  Maybe<bool> renderLayers;
    362  if (mFrameLoader) {
    363    if (auto* oldParent = mFrameLoader->GetBrowserParent()) {
    364      renderLayers.emplace(oldParent->GetRenderLayers());
    365    }
    366  }
    367 
    368  mFrameLoader = aNewFrameLoader;
    369 
    370  if (auto* browserParent = mFrameLoader->GetBrowserParent()) {
    371    browserParent->AddWindowListeners();
    372    if (renderLayers.isSome()) {
    373      browserParent->SetRenderLayers(renderLayers.value());
    374    }
    375  }
    376 
    377  RefPtr<Element> owner = do_QueryObject(this);
    378  ChangeFrameLoaderCommon(owner, /* aRetainPaint = */ false);
    379 }
    380 
    381 void nsFrameLoaderOwner::AttachFrameLoader(nsFrameLoader* aFrameLoader) {
    382  mFrameLoaderList.insertBack(aFrameLoader);
    383 }
    384 
    385 void nsFrameLoaderOwner::DetachFrameLoader(nsFrameLoader* aFrameLoader) {
    386  if (aFrameLoader->isInList()) {
    387    MOZ_ASSERT(mFrameLoaderList.contains(aFrameLoader));
    388    aFrameLoader->remove();
    389  }
    390 }
    391 
    392 void nsFrameLoaderOwner::FrameLoaderDestroying(nsFrameLoader* aFrameLoader,
    393                                               bool aDestroyBFCached) {
    394  if (aFrameLoader == mFrameLoader) {
    395    if (aDestroyBFCached) {
    396      while (!mFrameLoaderList.isEmpty()) {
    397        RefPtr<nsFrameLoader> loader = mFrameLoaderList.popFirst();
    398        if (loader != mFrameLoader) {
    399          loader->Destroy();
    400        }
    401      }
    402    }
    403  } else {
    404    DetachFrameLoader(aFrameLoader);
    405  }
    406 }