Merge mozilla-central to autoland on a CLOSED TREE
[gecko.git] / dom / base / nsFrameLoaderOwner.cpp
blobe9b8a876c5dfc2a9e139ff712fd9c32e4a666f2f
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 // We will preserve our browsing context if either fission is enabled, or the
85 // `preserve_browsing_contexts` pref is active.
86 if (UseRemoteSubframes() ||
87 StaticPrefs::fission_preserve_browsing_contexts()) {
88 return ChangeRemotenessContextType::PRESERVE;
90 return ChangeRemotenessContextType::DONT_PRESERVE;
93 void nsFrameLoaderOwner::ChangeRemotenessCommon(
94 const ChangeRemotenessContextType& aContextType,
95 const NavigationIsolationOptions& aOptions, bool aSwitchingInProgressLoad,
96 bool aIsRemote, BrowsingContextGroup* aGroup,
97 std::function<void()>& aFrameLoaderInit, mozilla::ErrorResult& aRv) {
98 MOZ_ASSERT_IF(aGroup, aContextType != ChangeRemotenessContextType::PRESERVE);
100 RefPtr<mozilla::dom::BrowsingContext> bc;
101 bool networkCreated = false;
103 // In this case, we're not reparenting a frameloader, we're just destroying
104 // our current one and creating a new one, so we can use ourselves as the
105 // owner.
106 RefPtr<Element> owner = do_QueryObject(this);
107 MOZ_ASSERT(owner);
109 // When we destroy the original frameloader, it will stop blocking the parent
110 // document's load event, and immediately trigger the load event if there are
111 // no other blockers. Since we're going to be adding a new blocker as soon as
112 // we recreate the frame loader, this is not what we want, so add our own
113 // blocker until the process is complete.
114 Document* doc = owner->OwnerDoc();
115 doc->BlockOnload();
116 auto cleanup = MakeScopeExit([&]() { doc->UnblockOnload(false); });
118 // If we store the previous nsFrameLoader in the bfcache, this will be filled
119 // with the SessionHistoryEntry which now owns the frame.
120 RefPtr<SessionHistoryEntry> bfcacheEntry;
123 // Introduce a script blocker to ensure no JS is executed during the
124 // nsFrameLoader teardown & recreation process. Unload listeners will be run
125 // for the previous document, and the load will be started for the new one,
126 // at the end of this block.
127 nsAutoScriptBlocker sb;
129 // If we already have a Frameloader, destroy it, possibly preserving its
130 // browsing context.
131 if (mFrameLoader) {
132 // Calling `GetBrowsingContext` here will force frameloader
133 // initialization if it hasn't already happened, which we neither need
134 // or want, so we use the initial (possibly pending) browsing context
135 // directly, instead.
136 bc = mFrameLoader->GetMaybePendingBrowsingContext();
137 networkCreated = mFrameLoader->IsNetworkCreated();
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 Unused << bc->SetIsInBFCache(true);
150 bfcacheEntry->SetFrameLoader(mFrameLoader);
151 // Session history owns now the frameloader.
152 mFrameLoader = nullptr;
156 if (mFrameLoader) {
157 if (aContextType == ChangeRemotenessContextType::PRESERVE) {
158 mFrameLoader->SetWillChangeProcess();
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;
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;
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;
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()));
198 ChangeFrameLoaderCommon(owner, retainPaint);
200 UpdateFocusAndMouseEnterStateAfterFrameLoaderChange(owner);
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);
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);
224 if (mFrameLoader) {
225 mFrameLoader->PropagateIsUnderHiddenEmbedderElement(
226 !aOwner->GetPrimaryFrame() ||
227 !aOwner->GetPrimaryFrame()->StyleVisibility()->IsVisible());
231 void nsFrameLoaderOwner::UpdateFocusAndMouseEnterStateAfterFrameLoaderChange() {
232 RefPtr<Element> owner = do_QueryObject(this);
233 UpdateFocusAndMouseEnterStateAfterFrameLoaderChange(owner);
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 (nsFocusManager* fm = nsFocusManager::GetFocusManager()) {
241 if (fm->GetFocusedElement() == aOwner) {
242 fm->ActivateRemoteFrameIfNeeded(*aOwner,
243 nsFocusManager::GenerateFocusActionId());
247 if (aOwner->GetPrimaryFrame()) {
248 EventStateManager* eventManager =
249 aOwner->GetPrimaryFrame()->PresContext()->EventStateManager();
250 eventManager->RecomputeMouseEnterStateForRemoteFrame(*aOwner);
254 void nsFrameLoaderOwner::ChangeRemoteness(
255 const mozilla::dom::RemotenessOptions& aOptions, mozilla::ErrorResult& rv) {
256 bool isRemote = !aOptions.mRemoteType.IsEmpty();
258 std::function<void()> frameLoaderInit = [&] {
259 if (isRemote) {
260 mFrameLoader->ConfigRemoteProcess(aOptions.mRemoteType, nullptr);
263 if (aOptions.mPendingSwitchID.WasPassed()) {
264 mFrameLoader->ResumeLoad(aOptions.mPendingSwitchID.Value());
265 } else {
266 mFrameLoader->LoadFrame(false);
270 auto shouldPreserve = ShouldPreserveBrowsingContext(
271 isRemote, /* replaceBrowsingContext */ false);
272 NavigationIsolationOptions options;
273 ChangeRemotenessCommon(shouldPreserve, options,
274 aOptions.mSwitchingInProgressLoad, isRemote,
275 /* group */ nullptr, frameLoaderInit, rv);
278 void nsFrameLoaderOwner::ChangeRemotenessWithBridge(BrowserBridgeChild* aBridge,
279 mozilla::ErrorResult& rv) {
280 MOZ_ASSERT(XRE_IsContentProcess());
281 if (NS_WARN_IF(!mFrameLoader)) {
282 rv.Throw(NS_ERROR_UNEXPECTED);
283 return;
286 std::function<void()> frameLoaderInit = [&] {
287 MOZ_DIAGNOSTIC_ASSERT(!mFrameLoader->mInitialized);
288 RefPtr<BrowserBridgeHost> host = aBridge->FinishInit(mFrameLoader);
289 mFrameLoader->mPendingBrowsingContext->SetEmbedderElement(
290 mFrameLoader->GetOwnerContent());
291 mFrameLoader->mRemoteBrowser = host;
292 mFrameLoader->mInitialized = true;
295 NavigationIsolationOptions options;
296 ChangeRemotenessCommon(ChangeRemotenessContextType::PRESERVE, options,
297 /* inProgress */ true,
298 /* isRemote */ true, /* group */ nullptr,
299 frameLoaderInit, rv);
302 void nsFrameLoaderOwner::ChangeRemotenessToProcess(
303 ContentParent* aContentParent, const NavigationIsolationOptions& aOptions,
304 BrowsingContextGroup* aGroup, mozilla::ErrorResult& rv) {
305 MOZ_ASSERT(XRE_IsParentProcess());
306 MOZ_ASSERT_IF(aGroup, aOptions.mReplaceBrowsingContext);
307 bool isRemote = aContentParent != nullptr;
309 std::function<void()> frameLoaderInit = [&] {
310 if (isRemote) {
311 mFrameLoader->ConfigRemoteProcess(aContentParent->GetRemoteType(),
312 aContentParent);
316 auto shouldPreserve =
317 ShouldPreserveBrowsingContext(isRemote, aOptions.mReplaceBrowsingContext);
318 ChangeRemotenessCommon(shouldPreserve, aOptions, /* inProgress */ true,
319 isRemote, aGroup, frameLoaderInit, rv);
322 void nsFrameLoaderOwner::SubframeCrashed() {
323 MOZ_ASSERT(XRE_IsContentProcess());
325 std::function<void()> frameLoaderInit = [&] {
326 RefPtr<nsFrameLoader> frameLoader = mFrameLoader;
327 nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
328 "nsFrameLoaderOwner::SubframeCrashed", [frameLoader]() {
329 nsCOMPtr<nsIURI> uri;
330 nsresult rv = NS_NewURI(getter_AddRefs(uri), "about:blank");
331 if (NS_WARN_IF(NS_FAILED(rv))) {
332 return;
335 RefPtr<nsDocShell> docShell =
336 frameLoader->GetDocShell(IgnoreErrors());
337 if (NS_WARN_IF(!docShell)) {
338 return;
340 bool displayed = false;
341 docShell->DisplayLoadError(NS_ERROR_FRAME_CRASHED, uri,
342 u"about:blank", nullptr, &displayed);
343 }));
346 NavigationIsolationOptions options;
347 ChangeRemotenessCommon(ChangeRemotenessContextType::PRESERVE, options,
348 /* inProgress */ false, /* isRemote */ false,
349 /* group */ nullptr, frameLoaderInit, IgnoreErrors());
352 void nsFrameLoaderOwner::RestoreFrameLoaderFromBFCache(
353 nsFrameLoader* aNewFrameLoader) {
354 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug,
355 ("nsFrameLoaderOwner::RestoreFrameLoaderFromBFCache: Replace "
356 "frameloader"));
358 Maybe<bool> renderLayers;
359 if (mFrameLoader) {
360 if (auto* oldParent = mFrameLoader->GetBrowserParent()) {
361 renderLayers.emplace(oldParent->GetRenderLayers());
365 mFrameLoader = aNewFrameLoader;
367 if (auto* browserParent = mFrameLoader->GetBrowserParent()) {
368 browserParent->AddWindowListeners();
369 if (renderLayers.isSome()) {
370 browserParent->SetRenderLayers(renderLayers.value());
374 RefPtr<Element> owner = do_QueryObject(this);
375 ChangeFrameLoaderCommon(owner, /* aRetainPaint = */ false);
378 void nsFrameLoaderOwner::AttachFrameLoader(nsFrameLoader* aFrameLoader) {
379 mFrameLoaderList.insertBack(aFrameLoader);
382 void nsFrameLoaderOwner::DetachFrameLoader(nsFrameLoader* aFrameLoader) {
383 if (aFrameLoader->isInList()) {
384 MOZ_ASSERT(mFrameLoaderList.contains(aFrameLoader));
385 aFrameLoader->remove();
389 void nsFrameLoaderOwner::FrameLoaderDestroying(nsFrameLoader* aFrameLoader) {
390 if (aFrameLoader == mFrameLoader) {
391 while (!mFrameLoaderList.isEmpty()) {
392 RefPtr<nsFrameLoader> loader = mFrameLoaderList.popFirst();
393 if (loader != mFrameLoader) {
394 loader->Destroy();
397 } else {
398 DetachFrameLoader(aFrameLoader);