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