blob: 40921051fd8ab87e28eabba3628c0fc3888e2960 [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2018 The Chromium Authors
Arthur Sonzogni620cec62018-12-13 13:08:572// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Fergal Dalyf5a50ab2021-11-09 05:36:185#include "content/browser/back_forward_cache_browsertest.h"
6
Fergal Dalya7dcc352022-10-19 03:43:187#include <climits>
Arthur Sonzognic686e8f2024-01-11 08:36:378#include <optional>
Md Hasibul Hasana963a9342024-04-03 10:15:149#include <string_view>
Ken Rockot05499cf2019-12-12 05:22:5410#include <unordered_map>
11
Arthur Sonzogni620cec62018-12-13 13:08:5712#include "base/command_line.h"
Peter Kasting837e2ccb2022-09-07 14:13:1713#include "base/containers/contains.h"
Avi Drissmanadac21992023-01-11 23:46:3914#include "base/functional/callback_helpers.h"
Alexander Timin44ccede2020-01-23 09:00:3415#include "base/location.h"
Keishi Hattori0e45c022021-11-27 09:25:5216#include "base/memory/raw_ptr.h"
Fergal Daly5bc3a542023-03-10 06:12:1617#include "base/metrics/metrics_hashes.h"
Carlos Caballero8b114db2020-08-13 08:57:0318#include "base/run_loop.h"
Lei Zhang0a85e65a2025-05-23 19:22:0619#include "base/strings/string_number_conversions.h"
[email protected]54eaf622019-11-25 12:36:0320#include "base/system/sys_info.h"
Harkiran Bolaria875dd0d2020-09-15 18:10:4621#include "base/task/common/task_annotator.h"
Sean Maher5b9af51f2022-11-21 15:32:4722#include "base/task/single_thread_task_runner.h"
Guido Urdanetaef4e91942020-11-09 15:06:2423#include "base/test/bind.h"
Ming-Ying Chunga8e8a2e2022-08-16 06:04:5024#include "base/test/metrics/histogram_tester.h"
Harkiran Bolaria12818bc2020-11-03 22:44:2725#include "base/test/test_timeouts.h"
Carlos Caballero8b114db2020-08-13 08:57:0326#include "base/threading/thread_restrictions.h"
arthursonzogni9cd875342019-09-04 14:53:4627#include "base/time/time.h"
Carlos Caballero8b114db2020-08-13 08:57:0328#include "base/trace_event/trace_log.h"
Rakina Zata Amni06343cc2019-10-11 01:33:2329#include "build/build_config.h"
Christian Dullweber5c5a42a2021-07-15 09:16:1430#include "build/chromecast_buildflags.h"
Ming-Ying Chunga8e8a2e2022-08-16 06:04:5031#include "components/ukm/test_ukm_recorder.h"
Carlos Caballero8b114db2020-08-13 08:57:0332#include "content/browser/bad_message.h"
Adithya Srinivasan7fe5ce662021-06-04 21:36:0433#include "content/browser/renderer_host/back_forward_cache_can_store_document_result.h"
Mingyu Lei8c1b9112023-01-18 03:59:0134#include "content/browser/renderer_host/back_forward_cache_disable.h"
danakj10f32372020-09-15 22:25:1635#include "content/browser/renderer_host/back_forward_cache_impl.h"
36#include "content/browser/renderer_host/frame_tree_node.h"
Mingyu Lei8c1b9112023-01-18 03:59:0137#include "content/browser/renderer_host/render_frame_host_impl.h"
Hajime Hoshi8fd675e02020-10-29 14:28:4138#include "content/browser/renderer_host/should_swap_browsing_instance.h"
Arthur Sonzogni620cec62018-12-13 13:08:5739#include "content/browser/web_contents/web_contents_impl.h"
[email protected]ee8ae332020-01-29 03:49:4540#include "content/common/content_navigation_policy.h"
Arthur Sonzognibdeca8e2023-09-11 08:32:1241#include "content/common/features.h"
Carlos Caballerob6b466482019-11-29 13:33:1842#include "content/public/browser/back_forward_cache.h"
Daniel Cheng9fb887ff2021-10-01 20:27:3843#include "content/public/browser/document_service.h"
Carlos Caballerob6b466482019-11-29 13:33:1844#include "content/public/browser/global_routing_id.h"
Carlos Caballero91fffb22019-10-29 15:24:3045#include "content/public/browser/web_contents.h"
Arthur Sonzogni620cec62018-12-13 13:08:5746#include "content/public/common/content_features.h"
Kouhei Ueno01b0f1192019-08-14 05:28:0747#include "content/public/common/content_switches.h"
Khushalc5eaf222021-06-30 20:15:4848#include "content/public/common/result_codes.h"
arthursonzognibec31272019-10-09 11:51:2149#include "content/public/test/back_forward_cache_util.h"
Peter Kasting919ce652020-05-07 10:22:3650#include "content/public/test/browser_test.h"
Arthur Sonzogni620cec62018-12-13 13:08:5751#include "content/public/test/browser_test_utils.h"
Adithya Srinivasan7fe5ce662021-06-04 21:36:0452#include "content/public/test/commit_message_delayer.h"
Arthur Sonzogni620cec62018-12-13 13:08:5753#include "content/public/test/content_browser_test_utils.h"
Gyuyoung Kim940115f2021-11-05 02:25:3554#include "content/public/test/fenced_frame_test_util.h"
Mohamed Abdelhalim462fff32019-08-13 14:13:4255#include "content/public/test/navigation_handle_observer.h"
Hajime Hoshie9262d32019-08-28 07:40:2356#include "content/public/test/test_navigation_observer.h"
Lowell Manners889189f2019-08-22 16:47:4557#include "content/public/test/test_navigation_throttle.h"
58#include "content/public/test/test_navigation_throttle_inserter.h"
Arthur Sonzogni620cec62018-12-13 13:08:5759#include "content/public/test/test_utils.h"
Rakina Zata Amniefcc2932020-08-28 07:24:3760#include "content/public/test/text_input_test_utils.h"
Fergal Daly7a05b4262022-03-15 09:18:4061#include "content/public/test/url_loader_interceptor.h"
Mustapha Jaber53892cc2024-01-05 01:13:3362#include "content/public/test/web_contents_observer_test_utils.h"
Arthur Sonzogni620cec62018-12-13 13:08:5763#include "content/shell/browser/shell.h"
Fergal Daly7991d9362019-12-20 02:28:2564#include "content/shell/browser/shell_javascript_dialog_manager.h"
Fergal Daly306fc3f2021-12-23 09:58:3065#include "content/test/content_browser_test_utils_internal.h"
Hajime Hoshi925793e72019-11-26 07:58:5566#include "media/base/media_switches.h"
Carlos Caballero8b114db2020-08-13 08:57:0367#include "mojo/public/cpp/bindings/message.h"
68#include "mojo/public/cpp/bindings/pending_remote.h"
69#include "mojo/public/cpp/bindings/remote.h"
arthursonzognie385b7b2019-09-02 11:11:5370#include "net/base/filename_util.h"
Arthur Sonzogni620cec62018-12-13 13:08:5771#include "net/dns/mock_host_resolver.h"
Lowell Mannersb3b30c22019-07-26 11:31:4172#include "net/test/embedded_test_server/controllable_http_response.h"
Arthur Sonzogni620cec62018-12-13 13:08:5773#include "net/test/embedded_test_server/embedded_test_server.h"
Matt Menke7c824a72025-06-26 13:43:2074#include "net/test/embedded_test_server/install_default_websocket_handlers.h"
Mingyu Lei8c1b9112023-01-18 03:59:0175#include "services/metrics/public/cpp/ukm_recorder.h"
76#include "services/metrics/public/cpp/ukm_source_id.h"
Sandor Majora9a29ad52025-02-20 16:00:1477#include "services/network/public/cpp/features.h"
Yuzu Saijob0f001b22020-04-08 04:07:0478#include "services/service_manager/public/cpp/interface_provider.h"
Sreeja Kamishetty92006e02021-02-05 05:18:1179#include "third_party/blink/public/common/device_memory/approximated_device_memory.h"
Harkiran Bolaria875dd0d2020-09-15 18:10:4680#include "third_party/blink/public/common/features.h"
Kurumi Muto863fd9a2024-02-06 11:51:3281#include "third_party/blink/public/common/features_generated.h"
Lowell Manners5cd4de42019-06-13 11:12:2282#include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h"
Stephen Chenney91a186b2021-03-23 10:42:0183#include "third_party/blink/public/common/switches.h"
Kurumi Muto0cf12802024-02-21 08:36:4284#include "third_party/blink/public/mojom/back_forward_cache_not_restored_reasons.mojom.h"
85#include "third_party/blink/public/mojom/frame/back_forward_cache_controller.mojom.h"
Fergal Daly5bc3a542023-03-10 06:12:1686#include "third_party/blink/public/mojom/frame/frame.mojom.h"
Chris Harrelsond2fe06ad2022-07-11 21:36:2387#include "third_party/blink/public/mojom/render_accessibility.mojom.h"
Kurumi Muto0cf12802024-02-21 08:36:4288#include "third_party/blink/public/mojom/script_source_location.mojom.h"
Fergal Dalyf5a50ab2021-11-09 05:36:1889
90// This file has too many tests.
91//
92// Before adding new tests to this file, consider if they will fit better into
93// one of the other back_forward_cache_*_browsertest.cc files or if there are
94// enough new tests to justify a new file.
arthursonzogni2f84dd62019-06-05 09:47:1295
Rakina Zata Amnic7bc82632019-12-09 05:21:2296using testing::_;
arthursonzogni2f84dd62019-06-05 09:47:1297using testing::Each;
Hajime Hoshif9eba2b2019-10-02 08:27:4298using testing::ElementsAre;
arthursonzogni2f84dd62019-06-05 09:47:1299using testing::Not;
Hajime Hoshi5e86bc82019-11-26 05:41:12100using testing::UnorderedElementsAreArray;
Arthur Sonzogni620cec62018-12-13 13:08:57101
102namespace content {
103
Yuzu Saijodcabe562022-04-25 06:06:39104using NotRestoredReasons =
105 BackForwardCacheCanStoreDocumentResult::NotRestoredReasons;
Riho Isawab1efe68f2021-12-20 01:54:07106using NotRestoredReason = BackForwardCacheMetrics::NotRestoredReason;
107
Fergal Daly2c7bc4052021-12-23 14:42:22108EvalJsResult GetLocalStorage(RenderFrameHostImpl* rfh, std::string key) {
109 return EvalJs(rfh, JsReplace("localStorage.getItem($1)", key));
110}
111
Fergal Daly91e62882023-10-30 08:26:42112[[nodiscard]] bool WaitForLocalStorage(RenderFrameHostImpl* rfh,
113 std::string key,
114 std::string expected_value) {
115 auto value = EvalJs(rfh, JsReplace(R"(
116 new Promise((resolve) => {
117 let key = $1;
118 let expected_value = $2;
119 if (localStorage.getItem(key) == expected_value) {
120 resolve(localStorage.getItem(key));
121 return;
122 }
123 let listener = window.addEventListener("storage", e => {
124 if (e.storageArea == localStorage && e.key == key
125 && e.newValue == expected_value) {
126 resolve(localStorage.getItem(key));
127 removeEventListener("storage", listener);
128 return;
129 }
130 });
131 });
132 )",
133 key, expected_value));
134 return value == expected_value;
135}
136
Fergal Dalyf5a50ab2021-11-09 05:36:18137BackForwardCacheBrowserTest::BackForwardCacheBrowserTest() = default;
138
139BackForwardCacheBrowserTest::~BackForwardCacheBrowserTest() {
140 if (fail_for_unexpected_messages_while_cached_) {
141 // If this is triggered, see MojoInterfaceName in
Kurumi Muto03d71752024-02-22 04:55:53142 // tools/metrics/histograms/metadata/navigation/enums.xml for which values
143 // correspond which messages.
Fergal Daly5bc3a542023-03-10 06:12:16144 std::vector<base::Bucket> samples = histogram_tester().GetAllSamples(
145 "BackForwardCache.UnexpectedRendererToBrowserMessage."
146 "InterfaceName");
Alison Gale770f3fc2024-04-27 00:39:58147 // TODO(crbug.com/40244391): Remove this.
Fergal Daly5bc3a542023-03-10 06:12:16148 // This bucket corresponds to the LocalFrameHost interface. It is known to
149 // be flaky due calls to `LocalFrameHost::DidFocusFrame()` after entering
150 // BFCache. So we ignore it for now by removing it if it's present until we
151 // can fix the root cause.
Alison Gale81f4f2c72024-04-22 19:33:31152 // TODO(crbug.com/40925798): Remove this.
Fergal Daly70e679b0c2023-09-21 03:58:56153 // As above but `LocalMainFrameHost::DidFirstVisuallyNonEmptyPaint()`.
Fergal Daly5bc3a542023-03-10 06:12:16154 std::erase_if(samples, [](base::Bucket bucket) {
155 return bucket.min ==
Ramon Cano Aparicio6797d4e2025-01-10 16:09:02156 static_cast<base::HistogramBase::Sample32>(base::HashMetricName(
Fergal Daly70e679b0c2023-09-21 03:58:56157 blink::mojom::LocalFrameHost::Name_)) ||
158 bucket.min ==
Ramon Cano Aparicio6797d4e2025-01-10 16:09:02159 static_cast<base::HistogramBase::Sample32>(base::HashMetricName(
Fergal Daly70e679b0c2023-09-21 03:58:56160 blink::mojom::LocalMainFrameHost::Name_));
Fergal Daly5bc3a542023-03-10 06:12:16161 });
162
163 EXPECT_THAT(samples, testing::ElementsAre());
Fergal Dalyf5a50ab2021-11-09 05:36:18164 }
165}
Hajime Hoshi765845d62020-02-21 02:56:21166
Yuzu Saijoc3478072022-03-24 06:15:26167void BackForwardCacheBrowserTest::NotifyNotRestoredReasons(
168 std::unique_ptr<BackForwardCacheCanStoreTreeResult> tree_result) {
169 tree_result_ = std::move(tree_result);
170}
171
Fergal Dalyf5a50ab2021-11-09 05:36:18172void BackForwardCacheBrowserTest::SetUpCommandLine(
173 base::CommandLine* command_line) {
Fergal Dalyf5a50ab2021-11-09 05:36:18174 mock_cert_verifier_.SetUpCommandLine(command_line);
Arthur Sonzognidc3d3b2a2021-08-06 13:13:23175
Lei Zhanga6aac9d2024-07-10 20:59:38176 command_line->AppendSwitch(switches::kUseFakeUIForMediaStream);
177 command_line->AppendSwitch(switches::kEnableExperimentalWebPlatformFeatures);
Fergal Dalyf5a50ab2021-11-09 05:36:18178 // TODO(sreejakshetty): Initialize ScopedFeatureLists from test constructor.
Ming-Ying Chung073e6f602023-01-26 02:47:40179 EnableFeatureAndSetParams(features::kBackForwardCacheTimeToLiveControl,
180 "time_to_live_seconds", "3600");
Fergal Daly7fdacfb2022-10-21 13:23:06181 // Entry to the cache can be slow during testing and cause flakiness.
182 DisableFeature(features::kBackForwardCacheEntryTimeout);
Fergal Dalyf5a50ab2021-11-09 05:36:18183 EnableFeatureAndSetParams(features::kBackForwardCache,
184 "message_handling_when_cached", "log");
185 EnableFeatureAndSetParams(
Fergal Dalyf5a50ab2021-11-09 05:36:18186 blink::features::kLogUnexpectedIPCPostedToBackForwardCachedDocuments,
187 "delay_before_tracking_ms", "0");
Fergal Dalya7dcc352022-10-19 03:43:18188 // Allow unlimited network during tests. Override this if you want to test the
189 // network limiting.
Fergal Dalyf5a50ab2021-11-09 05:36:18190 EnableFeatureAndSetParams(blink::features::kLoadingTasksUnfreezable,
191 "max_buffered_bytes_per_process",
Fergal Dalya7dcc352022-10-19 03:43:18192 base::NumberToString(INT_MAX));
193 EnableFeatureAndSetParams(blink::features::kLoadingTasksUnfreezable,
194 "grace_period_to_finish_loading_in_seconds",
195 base::NumberToString(INT_MAX));
Fergal Daly25b51802022-11-28 06:01:24196 // Enable capturing not-restored-reasons tree.
197 EnableFeatureAndSetParams(
198 blink::features::kBackForwardCacheSendNotRestoredReasons, "", "");
199
Yuzu Saijof55ccb62023-04-06 04:52:22200 // Do not trigger DumpWithoutCrashing() for JavaScript execution.
201 DisableFeature(blink::features::kBackForwardCacheDWCOnJavaScriptExecution);
Xiaohan Wang1ecfd002022-01-19 22:33:10202#if BUILDFLAG(IS_ANDROID)
203 EnableFeatureAndSetParams(features::kBackForwardCache,
204 "process_binding_strength", "NORMAL");
[email protected]42e682e52019-12-05 04:13:46205#endif
Sreeja Kamishetty92006e02021-02-05 05:18:11206 // Allow BackForwardCache for all devices regardless of their memory.
John Abd-El-Malek3c868c292021-02-19 20:00:09207 DisableFeature(features::kBackForwardCacheMemoryControls);
Fergal Daly7e9b4caa2025-07-17 13:56:09208 // Many browser tests assume a cache size of 1.
209 EnableCacheSize(1, std::nullopt);
Sreeja Kamishetty92006e02021-02-05 05:18:11210
[email protected]54eaf622019-11-25 12:36:03211 SetupFeaturesAndParameters();
Alexander Timin8304ddd942019-09-02 15:53:59212
Hajime Hoshi925793e72019-11-26 07:58:55213 command_line->AppendSwitchASCII(
214 switches::kAutoplayPolicy,
215 switches::autoplay::kNoUserGestureRequiredPolicy);
Stephen Chenney91a186b2021-03-23 10:42:01216 // Unfortunately needed for one test on slow bots, TextInputStateUpdated,
217 // where deferred commits delays input too much.
218 command_line->AppendSwitch(blink::switches::kAllowPreCommitInput);
Fergal Daly7e9b4caa2025-07-17 13:56:09219 ContentBrowserTest::SetUpCommandLine(command_line);
Fergal Dalyf5a50ab2021-11-09 05:36:18220}
221
222void BackForwardCacheBrowserTest::SetUpInProcessBrowserTestFixture() {
223 ContentBrowserTest::SetUpInProcessBrowserTestFixture();
224 mock_cert_verifier_.SetUpInProcessBrowserTestFixture();
225}
226
227void BackForwardCacheBrowserTest::TearDownInProcessBrowserTestFixture() {
228 ContentBrowserTest::TearDownInProcessBrowserTestFixture();
229 mock_cert_verifier_.TearDownInProcessBrowserTestFixture();
230}
231
232void BackForwardCacheBrowserTest::SetupFeaturesAndParameters() {
Daniel Cheng43a829c2022-10-14 00:16:36233 std::vector<base::test::FeatureRefAndParams> enabled_features;
Fergal Dalyf5a50ab2021-11-09 05:36:18234
Daniel Chengc9ead4b2022-10-05 03:54:29235 for (const auto& [feature_ref, params] : features_with_params_) {
236 enabled_features.emplace_back(*feature_ref, params);
Arthur Sonzognidc3d3b2a2021-08-06 13:13:23237 }
238
Fergal Dalyf5a50ab2021-11-09 05:36:18239 feature_list_.InitWithFeaturesAndParameters(enabled_features,
240 disabled_features_);
Fergal Daly432aa7c2022-06-14 07:30:54241 vmodule_switches_.InitWithSwitches("back_forward_cache_impl=1");
Fergal Dalyf5a50ab2021-11-09 05:36:18242}
Arthur Sonzognidc3d3b2a2021-08-06 13:13:23243
Fergal Dalyf5a50ab2021-11-09 05:36:18244void BackForwardCacheBrowserTest::EnableFeatureAndSetParams(
Daniel Chengc9ead4b2022-10-05 03:54:29245 const base::Feature& feature,
Fergal Dalyf5a50ab2021-11-09 05:36:18246 std::string param_name,
247 std::string param_value) {
Fergal Daly7e9b4caa2025-07-17 13:56:09248 const auto& it = features_with_params_.find(feature);
249 if (it != features_with_params_.end()) {
250 // If the feature-param has been set already, do not update it.
251 if (it->second.contains(param_name)) {
252 return;
253 }
254 }
Fergal Dalyf5a50ab2021-11-09 05:36:18255 features_with_params_[feature][param_name] = param_value;
256}
Arthur Sonzogni620cec62018-12-13 13:08:57257
Daniel Chengc9ead4b2022-10-05 03:54:29258void BackForwardCacheBrowserTest::DisableFeature(const base::Feature& feature) {
Fergal Daly7e9b4caa2025-07-17 13:56:09259 if (features_with_params_.contains(feature)) {
260 // If the feature has been explicitly enabled, ignore any subsequent
261 // disables.
262 return;
263 }
Fergal Dalyf5a50ab2021-11-09 05:36:18264 disabled_features_.push_back(feature);
265}
[email protected]54eaf622019-11-25 12:36:03266
Fergal Dalyb1258112025-07-15 03:00:51267void BackForwardCacheBrowserTest::EnableCacheSize(
268 std::optional<int> cache_size,
269 std::optional<int> foreground_cache_size) {
270 if (cache_size) {
Fergal Daly7e9b4caa2025-07-17 13:56:09271 EnableFeatureAndSetParams(content::kBackForwardCacheSize,
272 kBackForwardCacheSizeCacheSize.name,
Fergal Dalyb1258112025-07-15 03:00:51273 base::NumberToString(cache_size.value()));
274 }
275 if (foreground_cache_size) {
276 EnableFeatureAndSetParams(
Fergal Daly7e9b4caa2025-07-17 13:56:09277 content::kBackForwardCacheSize,
278 kBackForwardCacheSizeForegroundCacheSize.name,
Fergal Dalyb1258112025-07-15 03:00:51279 base::NumberToString(foreground_cache_size.value()));
280 }
281}
282
Fergal Dalyf5a50ab2021-11-09 05:36:18283void BackForwardCacheBrowserTest::SetUpOnMainThread() {
Matt Menke7c824a72025-06-26 13:43:20284 // Set up WebSocket handlers, as a number of tests use them.
Matt Menke21df5e72025-07-22 17:12:01285 net::test_server::InstallDefaultWebSocketHandlers(embedded_test_server());
Matt Menke7c824a72025-06-26 13:43:20286
Fergal Dalyf5a50ab2021-11-09 05:36:18287 mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK);
288 host_resolver()->AddRule("*", "127.0.0.1");
289 // TestAutoSetUkmRecorder's constructor requires a sequenced context.
290 ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
Ming-Ying Chunga8e8a2e2022-08-16 06:04:50291 histogram_tester_ = std::make_unique<base::HistogramTester>();
Fergal Dalyf5a50ab2021-11-09 05:36:18292 ContentBrowserTest::SetUpOnMainThread();
293}
294
295void BackForwardCacheBrowserTest::TearDownOnMainThread() {
296 ukm_recorder_.reset();
297 ContentBrowserTest::TearDownOnMainThread();
298}
299
300WebContentsImpl* BackForwardCacheBrowserTest::web_contents() const {
301 return static_cast<WebContentsImpl*>(shell()->web_contents());
302}
303
304RenderFrameHostImpl* BackForwardCacheBrowserTest::current_frame_host() {
305 return web_contents()->GetPrimaryFrameTree().root()->current_frame_host();
306}
307
308RenderFrameHostManager*
309BackForwardCacheBrowserTest::render_frame_host_manager() {
310 return web_contents()->GetPrimaryFrameTree().root()->render_manager();
311}
312
313std::string BackForwardCacheBrowserTest::DepictFrameTree(FrameTreeNode* node) {
314 return visualizer_.DepictFrameTree(node);
315}
316
317bool BackForwardCacheBrowserTest::HistogramContainsIntValue(
Ramon Cano Aparicio6797d4e2025-01-10 16:09:02318 base::HistogramBase::Sample32 sample,
Fergal Dalyf5a50ab2021-11-09 05:36:18319 std::vector<base::Bucket> histogram_values) {
Peter Kasting837e2ccb2022-09-07 14:13:17320 return base::Contains(histogram_values, static_cast<int>(sample),
321 &base::Bucket::min);
Fergal Dalyf5a50ab2021-11-09 05:36:18322}
323
Fergal Dalyf5a50ab2021-11-09 05:36:18324void BackForwardCacheBrowserTest::EvictByJavaScript(RenderFrameHostImpl* rfh) {
325 // Run JavaScript on a page in the back-forward cache. The page should be
326 // evicted. As the frame is deleted, ExecJs returns false without executing.
327 // Run without user gesture to prevent UpdateUserActivationState message
328 // being sent back to browser.
329 EXPECT_FALSE(
330 ExecJs(rfh, "console.log('hi');", EXECUTE_SCRIPT_NO_USER_GESTURE));
331}
Hajime Hoshi765845d62020-02-21 02:56:21332
Fergal Dalyf5a50ab2021-11-09 05:36:18333void BackForwardCacheBrowserTest::StartRecordingEvents(
334 RenderFrameHostImpl* rfh) {
335 EXPECT_TRUE(ExecJs(rfh, R"(
Yuzu Saijoecb779c2019-11-06 12:59:37336 window.testObservedEvents = [];
337 let event_list = [
338 'visibilitychange',
339 'pagehide',
340 'pageshow',
341 'freeze',
342 'resume',
343 ];
344 for (event_name of event_list) {
345 let result = event_name;
346 window.addEventListener(event_name, event => {
347 if (event.persisted)
Alexander Timin44ccede2020-01-23 09:00:34348 result += '.persisted';
349 window.testObservedEvents.push('window.' + result);
Yuzu Saijoecb779c2019-11-06 12:59:37350 });
351 document.addEventListener(event_name,
Alexander Timin44ccede2020-01-23 09:00:34352 () => window.testObservedEvents.push('document.' + result));
Yuzu Saijoecb779c2019-11-06 12:59:37353 }
354 )"));
Fergal Dalyf5a50ab2021-11-09 05:36:18355}
Yuzu Saijoecb779c2019-11-06 12:59:37356
Fergal Dalyf5a50ab2021-11-09 05:36:18357void BackForwardCacheBrowserTest::MatchEventList(RenderFrameHostImpl* rfh,
Matt Menke0500b4f42022-06-28 17:06:58358 base::Value list,
Fergal Dalyf5a50ab2021-11-09 05:36:18359 base::Location location) {
360 EXPECT_EQ(list, EvalJs(rfh, "window.testObservedEvents"))
361 << location.ToString();
362}
Yuzu Saijoecb779c2019-11-06 12:59:37363
Fergal Daly6b0db37e2022-06-27 05:41:03364// Creates a minimal HTTPS server, accessible through https_server().
365// Returns a pointer to the server.
Fergal Dalyf5a50ab2021-11-09 05:36:18366net::EmbeddedTestServer* BackForwardCacheBrowserTest::CreateHttpsServer() {
367 https_server_ = std::make_unique<net::EmbeddedTestServer>(
368 net::EmbeddedTestServer::TYPE_HTTPS);
369 https_server_->AddDefaultHandlers(GetTestDataFilePath());
Fergal Daly6b0db37e2022-06-27 05:41:03370 https_server_->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
Fergal Dalyf5a50ab2021-11-09 05:36:18371 return https_server();
372}
Fergal Daly637c4522019-11-22 00:21:31373
Fergal Dalyf5a50ab2021-11-09 05:36:18374net::EmbeddedTestServer* BackForwardCacheBrowserTest::https_server() {
375 return https_server_.get();
376}
Fergal Daly637c4522019-11-22 00:21:31377
Ming-Ying Chungee9b9ea2022-08-15 08:24:25378// Do not fail this test if a message from a renderer arrives at the browser
379// for a cached page.
Fergal Dalyf5a50ab2021-11-09 05:36:18380void BackForwardCacheBrowserTest::DoNotFailForUnexpectedMessagesWhileCached() {
381 fail_for_unexpected_messages_while_cached_ = false;
382}
Carlos Caballero0e2fff922020-09-15 12:52:19383
Yuzu Saijo6ddce412021-08-17 05:09:15384 // Navigates to a page at |page_url| with an img element with src set to
385 // "image.png".
Fergal Dalyf5a50ab2021-11-09 05:36:18386RenderFrameHostImpl* BackForwardCacheBrowserTest::NavigateToPageWithImage(
387 const GURL& page_url) {
388 EXPECT_TRUE(NavigateToURL(shell(), page_url));
389 RenderFrameHostImpl* rfh = current_frame_host();
390 // Wait for the document to load DOM to ensure that kLoading is not
391 // one of the reasons why the document wasn't cached.
Fergal Dalyc4d8b1822022-02-14 08:00:27392 EXPECT_TRUE(WaitForDOMContentLoaded(rfh));
Yuzu Saijo6ddce412021-08-17 05:09:15393
Fergal Dalyf5a50ab2021-11-09 05:36:18394 EXPECT_TRUE(ExecJs(rfh, R"(
Yuzu Saijo6ddce412021-08-17 05:09:15395 var image = document.createElement("img");
396 image.src = "image.png";
397 document.body.appendChild(image);
398
399 var image_load_status = new Promise((resolve, reject) => {
400 image.onload = () => { resolve("loaded"); }
401 image.onerror = () => { resolve("error"); }
402 });
403 )"));
Fergal Dalyf5a50ab2021-11-09 05:36:18404 return rfh;
405}
Yuzu Saijo6ddce412021-08-17 05:09:15406
Fergal Dalyf5a50ab2021-11-09 05:36:18407void BackForwardCacheBrowserTest::AcquireKeyboardLock(
408 RenderFrameHostImpl* rfh) {
Fergal Daly7434e712024-12-11 10:11:48409 EXPECT_EQ(42, EvalJs(rfh, R"(
Yuzu Saijoae15ed82021-10-14 07:03:10410 new Promise(resolve => {
411 navigator.keyboard.lock();
Fergal Daly7434e712024-12-11 10:11:48412 resolve(42);
Yuzu Saijoae15ed82021-10-14 07:03:10413 });
414 )"));
Fergal Dalyf5a50ab2021-11-09 05:36:18415}
Yuzu Saijoae15ed82021-10-14 07:03:10416
Fergal Dalyf5a50ab2021-11-09 05:36:18417void BackForwardCacheBrowserTest::ReleaseKeyboardLock(
418 RenderFrameHostImpl* rfh) {
Fergal Daly7434e712024-12-11 10:11:48419 EXPECT_EQ(42, EvalJs(rfh, R"(
Yuzu Saijoae15ed82021-10-14 07:03:10420 new Promise(resolve => {
421 navigator.keyboard.unlock();
Fergal Daly7434e712024-12-11 10:11:48422 resolve(42);
Yuzu Saijoae15ed82021-10-14 07:03:10423 });
424 )"));
arthursonzogni2f84dd62019-06-05 09:47:12425}
426
Fergal Daly7a05b4262022-03-15 09:18:40427void BackForwardCacheBrowserTest::NavigateAndBlock(GURL url,
428 int history_offset) {
429 // Block the navigation with an error.
430 std::unique_ptr<URLLoaderInterceptor> url_interceptor =
431 URLLoaderInterceptor::SetupRequestFailForURL(url,
432 net::ERR_BLOCKED_BY_CLIENT);
Fergal Daly7a05b4262022-03-15 09:18:40433 if (history_offset) {
434 shell()->GoBackOrForward(history_offset);
435 } else {
436 shell()->LoadURL(url);
437 }
Fergal Dalya69d2ff2022-03-29 08:10:17438 WaitForLoadStop(web_contents());
Fergal Daly7a05b4262022-03-15 09:18:40439 ASSERT_EQ(current_frame_host()->GetLastCommittedURL(), url);
440 ASSERT_TRUE(current_frame_host()->IsErrorDocument());
441}
442
Fergal Daly25b51802022-11-28 06:01:24443ReasonsMatcher BackForwardCacheBrowserTest::MatchesNotRestoredReasons(
Arthur Sonzognic686e8f2024-01-11 08:36:37444 const std::optional<testing::Matcher<std::string>>& id,
445 const std::optional<testing::Matcher<std::string>>& name,
446 const std::optional<testing::Matcher<std::string>>& src,
Kurumi Muto576c0a02024-02-08 04:17:32447 const std::vector<BlockingDetailsReasonsMatcher>& reasons,
Arthur Sonzognic686e8f2024-01-11 08:36:37448 const std::optional<SameOriginMatcher>& same_origin_details) {
Alison Gale770f3fc2024-04-27 00:39:58449 // TODO(crbug.com/41496143) Make this matcher display human-friendly messages.
Fergal Daly25b51802022-11-28 06:01:24450 return testing::Pointee(testing::AllOf(
Yuzu Saijoaa35fc82023-01-13 01:54:10451 id.has_value()
452 ? testing::Field(
453 "id", &blink::mojom::BackForwardCacheNotRestoredReasons::id,
454 testing::Optional(id.value()))
455 : testing::Field(
456 "id", &blink::mojom::BackForwardCacheNotRestoredReasons::id,
Arthur Sonzognic686e8f2024-01-11 08:36:37457 std::optional<std::string>(std::nullopt)),
Yuzu Saijoaa35fc82023-01-13 01:54:10458 name.has_value()
459 ? testing::Field(
460 "name", &blink::mojom::BackForwardCacheNotRestoredReasons::name,
461 testing::Optional(name.value()))
462 : testing::Field(
463 "name", &blink::mojom::BackForwardCacheNotRestoredReasons::name,
Arthur Sonzognic686e8f2024-01-11 08:36:37464 std::optional<std::string>(std::nullopt)),
Yuzu Saijoaa35fc82023-01-13 01:54:10465 src.has_value()
466 ? testing::Field(
467 "src", &blink::mojom::BackForwardCacheNotRestoredReasons::src,
468 testing::Optional(src.value()))
469 : testing::Field(
470 "src", &blink::mojom::BackForwardCacheNotRestoredReasons::src,
Arthur Sonzognic686e8f2024-01-11 08:36:37471 std::optional<std::string>(std::nullopt)),
rubberyuzu6211b1c82024-01-22 02:30:48472 testing::Field("reasons",
473 &blink::mojom::BackForwardCacheNotRestoredReasons::reasons,
474 testing::UnorderedElementsAreArray(reasons)),
Fergal Daly25b51802022-11-28 06:01:24475 testing::Field(
476 "same_origin_details",
477 &blink::mojom::BackForwardCacheNotRestoredReasons::
478 same_origin_details,
Fergal Daly3107ca72022-11-28 06:32:28479 same_origin_details.has_value()
480 ? same_origin_details.value()
Fergal Daly25b51802022-11-28 06:01:24481 : testing::Property(
482 "is_null",
483 &blink::mojom::SameOriginBfcacheNotRestoredDetailsPtr::
484 is_null,
485 true))));
486}
487
488SameOriginMatcher BackForwardCacheBrowserTest::MatchesSameOriginDetails(
Luke Gu990c227b2024-03-07 00:36:38489 const testing::Matcher<GURL>& url,
Fergal Daly25b51802022-11-28 06:01:24490 const std::vector<ReasonsMatcher>& children) {
Alison Gale770f3fc2024-04-27 00:39:58491 // TODO(crbug.com/41496143) Make this matcher display human-friendly messages.
Fergal Daly25b51802022-11-28 06:01:24492 return testing::Pointee(testing::AllOf(
493 testing::Field(
Fergal Daly25b51802022-11-28 06:01:24494 "url", &blink::mojom::SameOriginBfcacheNotRestoredDetails::url, url),
495 testing::Field(
Fergal Daly25b51802022-11-28 06:01:24496 "children",
497 &blink::mojom::SameOriginBfcacheNotRestoredDetails::children,
498 testing::ElementsAreArray(children))));
499}
500
Kurumi Muto576c0a02024-02-08 04:17:32501BlockingDetailsReasonsMatcher
502BackForwardCacheBrowserTest::MatchesDetailedReason(
503 const testing::Matcher<std::string>& name,
Kurumi Muto0cf12802024-02-21 08:36:42504 const std::optional<SourceLocationMatcher>& source) {
Alison Gale770f3fc2024-04-27 00:39:58505 // TODO(crbug.com/41496143) Make this matcher display human-friendly
Kurumi Muto576c0a02024-02-08 04:17:32506 // messages.
507 return testing::Pointee(testing::AllOf(
508 testing::Field("name", &blink::mojom::BFCacheBlockingDetailedReason::name,
509 name),
510 testing::Field(
511 "source", &blink::mojom::BFCacheBlockingDetailedReason::source,
512 source.has_value()
513 ? source.value()
514 : testing::Property(
Kurumi Muto0cf12802024-02-21 08:36:42515 "is_null", &blink::mojom::ScriptSourceLocationPtr::is_null,
Kurumi Muto576c0a02024-02-08 04:17:32516 true))));
517}
518
Kurumi Muto0cf12802024-02-21 08:36:42519BlockingDetailsMatcher BackForwardCacheBrowserTest::MatchesBlockingDetails(
520 const std::optional<SourceLocationMatcher>& source) {
Alison Gale770f3fc2024-04-27 00:39:58521 // TODO(crbug.com/41496143) Make this matcher display human-friendly messages.
Kurumi Muto0cf12802024-02-21 08:36:42522 return testing::Pointee(testing::Field(
523 "source", &blink::mojom::BlockingDetails::source,
524 source.has_value()
525 ? source.value()
526 : testing::Property("is_null",
527 &blink::mojom::ScriptSourceLocationPtr::is_null,
528 true)));
529}
530
531SourceLocationMatcher BackForwardCacheBrowserTest::MatchesSourceLocation(
Luke Gud2d21b842024-03-19 09:49:41532 const testing::Matcher<GURL>& url,
Kurumi Muto0cf12802024-02-21 08:36:42533 const testing::Matcher<std::string>& function_name,
Kurumi Muto576c0a02024-02-08 04:17:32534 const testing::Matcher<uint64_t>& line_number,
535 const testing::Matcher<uint64_t>& column_number) {
Alison Gale770f3fc2024-04-27 00:39:58536 // TODO(crbug.com/41496143) Make this matcher display human-friendly
Kurumi Muto576c0a02024-02-08 04:17:32537 // messages.
538 return testing::Pointee(testing::AllOf(
Kurumi Muto0cf12802024-02-21 08:36:42539 testing::Field("url", &blink::mojom::ScriptSourceLocation::url, url),
540 testing::Field("function_name",
541 &blink::mojom::ScriptSourceLocation::function_name,
542 function_name),
Kurumi Muto576c0a02024-02-08 04:17:32543 testing::Field("line_number",
Kurumi Muto0cf12802024-02-21 08:36:42544 &blink::mojom::ScriptSourceLocation::line_number,
Kurumi Muto576c0a02024-02-08 04:17:32545 line_number),
546 testing::Field("column_number",
Kurumi Muto0cf12802024-02-21 08:36:42547 &blink::mojom::ScriptSourceLocation::column_number,
rubberyuzu15d29d32023-07-28 05:22:24548 column_number)));
549}
550
Fergal Daly97c4e592023-02-28 10:27:33551void BackForwardCacheUnloadBrowserTest::SetUpCommandLine(
552 base::CommandLine* command_line) {
553 BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
554 scoped_feature_list_.InitAndEnableFeature(kBackForwardCacheUnloadAllowed);
555}
556
arthursonzogni2f84dd62019-06-05 09:47:12557std::initializer_list<RenderFrameHostImpl*> Elements(
558 std::initializer_list<RenderFrameHostImpl*> t) {
559 return t;
560}
561
Alexander Timinfa953bff2019-12-05 14:31:49562// Execute a custom callback when navigation is ready to commit. This is
Hajime Hoshie9262d32019-08-28 07:40:23563// useful for simulating race conditions happening when a page enters the
564// BackForwardCache and receive inflight messages sent when it wasn't frozen
565// yet.
Alexander Timinfa953bff2019-12-05 14:31:49566class ReadyToCommitNavigationCallback : public WebContentsObserver {
Hajime Hoshie9262d32019-08-28 07:40:23567 public:
Alexander Timinfa953bff2019-12-05 14:31:49568 ReadyToCommitNavigationCallback(
Hajime Hoshie9262d32019-08-28 07:40:23569 WebContents* content,
Alexander Timinfa953bff2019-12-05 14:31:49570 base::OnceCallback<void(NavigationHandle*)> callback)
Hajime Hoshie9262d32019-08-28 07:40:23571 : WebContentsObserver(content), callback_(std::move(callback)) {}
572
Peter Boström9b036532021-10-28 23:37:28573 ReadyToCommitNavigationCallback(const ReadyToCommitNavigationCallback&) =
574 delete;
575 ReadyToCommitNavigationCallback& operator=(
576 const ReadyToCommitNavigationCallback&) = delete;
577
Hajime Hoshie9262d32019-08-28 07:40:23578 private:
579 // WebContentsObserver:
Alexander Timinfa953bff2019-12-05 14:31:49580 void ReadyToCommitNavigation(NavigationHandle* navigation_handle) override {
Hajime Hoshie9262d32019-08-28 07:40:23581 if (callback_)
Alexander Timinfa953bff2019-12-05 14:31:49582 std::move(callback_).Run(navigation_handle);
Hajime Hoshie9262d32019-08-28 07:40:23583 }
584
Alexander Timinfa953bff2019-12-05 14:31:49585 base::OnceCallback<void(NavigationHandle*)> callback_;
Hajime Hoshie9262d32019-08-28 07:40:23586};
587
Carlos Caballero91fffb22019-10-29 15:24:30588class FirstVisuallyNonEmptyPaintObserver : public WebContentsObserver {
589 public:
590 explicit FirstVisuallyNonEmptyPaintObserver(WebContents* contents)
591 : WebContentsObserver(contents) {}
592 void DidFirstVisuallyNonEmptyPaint() override {
593 if (observed_)
594 return;
595 observed_ = true;
596 run_loop_.Quit();
597 }
598
599 bool did_fire() const { return observed_; }
600
601 void Wait() { run_loop_.Run(); }
602
603 private:
604 bool observed_ = false;
605 base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed};
606};
607
608void WaitForFirstVisuallyNonEmptyPaint(WebContents* contents) {
609 if (contents->CompletedFirstVisuallyNonEmptyPaint())
610 return;
611 FirstVisuallyNonEmptyPaintObserver observer(contents);
612 observer.Wait();
613}
614
Carlos Caballero1215f882019-10-29 15:58:43615class ThemeColorObserver : public WebContentsObserver {
616 public:
617 explicit ThemeColorObserver(WebContents* contents)
618 : WebContentsObserver(contents) {}
Fergal Dalya4dde50d2022-10-14 07:29:25619
620 // Can only be called once.
Fergal Daly152656502022-10-20 03:02:39621 [[nodiscard]] bool WaitUntilThemeColorChange() {
Fergal Dalya4dde50d2022-10-14 07:29:25622 CHECK(!loop_);
623 loop_ = std::make_unique<base::RunLoop>();
624 if (observed_) {
625 return true;
626 }
627 loop_->Run();
628 return observed_;
629 }
630
631 void DidChangeThemeColor() override {
632 observed_ = true;
633 if (loop_) {
634 loop_->Quit();
635 }
636 }
Carlos Caballero1215f882019-10-29 15:58:43637
638 bool did_fire() const { return observed_; }
639
640 private:
Fergal Dalya4dde50d2022-10-14 07:29:25641 std::unique_ptr<base::RunLoop> loop_;
Carlos Caballero1215f882019-10-29 15:58:43642 bool observed_ = false;
Carlos Caballero1215f882019-10-29 15:58:43643};
644
Hajime Hoshia12414a2021-12-08 08:21:23645PageLifecycleStateManagerTestDelegate::PageLifecycleStateManagerTestDelegate(
646 PageLifecycleStateManager* manager)
647 : manager_(manager) {
648 manager->SetDelegateForTesting(this);
649}
650
651PageLifecycleStateManagerTestDelegate::
652 ~PageLifecycleStateManagerTestDelegate() {
653 if (manager_)
654 manager_->SetDelegateForTesting(nullptr);
655}
656
Fergal Daly52ba1e12022-10-20 07:53:00657bool PageLifecycleStateManagerTestDelegate::WaitForInBackForwardCacheAck() {
Hajime Hoshia12414a2021-12-08 08:21:23658 DCHECK(manager_);
659 if (manager_->last_acknowledged_state().is_in_back_forward_cache) {
Fergal Daly52ba1e12022-10-20 07:53:00660 return true;
Hajime Hoshia12414a2021-12-08 08:21:23661 }
662 base::RunLoop loop;
663 store_in_back_forward_cache_ack_received_ = loop.QuitClosure();
664 loop.Run();
Fergal Daly52ba1e12022-10-20 07:53:00665 return manager_->last_acknowledged_state().is_in_back_forward_cache;
Hajime Hoshia12414a2021-12-08 08:21:23666}
667
668void PageLifecycleStateManagerTestDelegate::OnStoreInBackForwardCacheSent(
669 base::OnceClosure cb) {
670 store_in_back_forward_cache_sent_ = std::move(cb);
671}
672
673void PageLifecycleStateManagerTestDelegate::OnDisableJsEvictionSent(
674 base::OnceClosure cb) {
675 disable_eviction_sent_ = std::move(cb);
676}
677
678void PageLifecycleStateManagerTestDelegate::OnRestoreFromBackForwardCacheSent(
679 base::OnceClosure cb) {
680 restore_from_back_forward_cache_sent_ = std::move(cb);
681}
682
683void PageLifecycleStateManagerTestDelegate::OnLastAcknowledgedStateChanged(
684 const blink::mojom::PageLifecycleState& old_state,
685 const blink::mojom::PageLifecycleState& new_state) {
686 if (store_in_back_forward_cache_ack_received_ &&
687 new_state.is_in_back_forward_cache)
688 std::move(store_in_back_forward_cache_ack_received_).Run();
689}
690
691void PageLifecycleStateManagerTestDelegate::OnUpdateSentToRenderer(
692 const blink::mojom::PageLifecycleState& new_state) {
693 if (store_in_back_forward_cache_sent_ && new_state.is_in_back_forward_cache) {
694 std::move(store_in_back_forward_cache_sent_).Run();
695 }
696
697 if (disable_eviction_sent_ && new_state.eviction_enabled == false) {
698 std::move(disable_eviction_sent_).Run();
699 }
700
701 if (restore_from_back_forward_cache_sent_ &&
702 !new_state.is_in_back_forward_cache) {
703 std::move(restore_from_back_forward_cache_sent_).Run();
704 }
705}
706
707void PageLifecycleStateManagerTestDelegate::OnDeleted() {
708 manager_ = nullptr;
709}
710
Arthur Sonzogni620cec62018-12-13 13:08:57711// Check the visible URL in the omnibox is properly updated when restoring a
712// document from the BackForwardCache.
713IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, VisibleURL) {
714 ASSERT_TRUE(embedded_test_server()->Start());
arthursonzogni72b66492019-11-04 12:24:33715 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
716 GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
Arthur Sonzogni620cec62018-12-13 13:08:57717
718 // 1) Go to A.
arthursonzogni2f84dd62019-06-05 09:47:12719 EXPECT_TRUE(NavigateToURL(shell(), url_a));
Arthur Sonzogni620cec62018-12-13 13:08:57720
721 // 2) Go to B.
arthursonzogni2f84dd62019-06-05 09:47:12722 EXPECT_TRUE(NavigateToURL(shell(), url_b));
Arthur Sonzogni620cec62018-12-13 13:08:57723
724 // 3) Go back to A.
Fergal Daly5495b0e2021-11-19 02:03:28725 ASSERT_TRUE(HistoryGoBack(web_contents()));
Arthur Sonzogni620cec62018-12-13 13:08:57726 EXPECT_EQ(url_a, web_contents()->GetVisibleURL());
727
728 // 4) Go forward to B.
Fergal Daly5495b0e2021-11-19 02:03:28729 ASSERT_TRUE(HistoryGoForward(web_contents()));
Arthur Sonzogni620cec62018-12-13 13:08:57730 EXPECT_EQ(url_b, web_contents()->GetVisibleURL());
731}
732
Lowell Manners3c64d3a22019-09-06 10:52:33733// Test only 1 document is kept in the at a time BackForwardCache.
734IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
735 CacheSizeLimitedToOneDocumentPerTab) {
Arthur Sonzogni620cec62018-12-13 13:08:57736 ASSERT_TRUE(embedded_test_server()->Start());
arthursonzogni72b66492019-11-04 12:24:33737 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
738 GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
739 GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html"));
Arthur Sonzogni620cec62018-12-13 13:08:57740
Lowell Manners3c64d3a22019-09-06 10:52:33741 EXPECT_TRUE(NavigateToURL(shell(), url_a));
742 // BackForwardCache is empty.
Arthur Sonzogni620cec62018-12-13 13:08:57743 RenderFrameHostImpl* rfh_a = current_frame_host();
Hajime Hoshif53fa7ca2019-08-13 06:52:34744 RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
Arthur Sonzogni620cec62018-12-13 13:08:57745
Lowell Manners3c64d3a22019-09-06 10:52:33746 EXPECT_TRUE(NavigateToURL(shell(), url_b));
747 // BackForwardCache contains only rfh_a.
Arthur Sonzogni620cec62018-12-13 13:08:57748 RenderFrameHostImpl* rfh_b = current_frame_host();
Hajime Hoshif53fa7ca2019-08-13 06:52:34749 RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b);
Arthur Sonzogni620cec62018-12-13 13:08:57750
Lowell Manners3c64d3a22019-09-06 10:52:33751 EXPECT_TRUE(NavigateToURL(shell(), url_c));
752 // BackForwardCache contains only rfh_b.
753 delete_observer_rfh_a.WaitUntilDeleted();
754 EXPECT_FALSE(delete_observer_rfh_b.deleted());
Arthur Sonzogni620cec62018-12-13 13:08:57755
Lowell Manners3c64d3a22019-09-06 10:52:33756 // If/when the cache size is increased, this can be tested iteratively, see
757 // deleted code in: https://crrev.com/c/1782902.
Hajime Hoshif9eba2b2019-10-02 08:27:42758
Fergal Daly5495b0e2021-11-19 02:03:28759 ASSERT_TRUE(HistoryGoToOffset(web_contents(), -2));
Hajime Hoshi15f6b5012019-10-24 05:25:49760 ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::kCacheLimit},
Fergal Daly1336ac642021-09-14 15:13:11761 {}, {}, {}, {}, FROM_HERE);
Arthur Sonzogni620cec62018-12-13 13:08:57762}
763
Hajime Hoshiac6b8542021-02-25 15:18:52764IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, ResponseHeaders) {
765 CreateHttpsServer();
766 ASSERT_TRUE(https_server()->Start());
767
Fergal Daly6b0db37e2022-06-27 05:41:03768 GURL url_a(https_server()->GetURL("a.test", "/set-header?X-Foo: bar"));
769 GURL url_b(https_server()->GetURL("b.test", "/title1.html"));
Hajime Hoshiac6b8542021-02-25 15:18:52770
771 // 1) Navigate to A.
772 NavigationHandleObserver observer1(web_contents(), url_a);
773 EXPECT_TRUE(NavigateToURL(shell(), url_a));
774 RenderFrameHostImpl* rfh_a = current_frame_host();
775 RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
776 EXPECT_TRUE(observer1.has_committed());
777 EXPECT_EQ("bar", observer1.GetNormalizedResponseHeader("x-foo"));
778
779 // 2) Navigate to B.
780 NavigationHandleObserver observer2(web_contents(), url_b);
781 EXPECT_TRUE(NavigateToURL(shell(), url_b));
782 RenderFrameHostImpl* rfh_b = current_frame_host();
783 RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b);
784 EXPECT_FALSE(delete_observer_rfh_a.deleted());
785 EXPECT_TRUE(rfh_a->IsInBackForwardCache());
786 EXPECT_FALSE(rfh_b->IsInBackForwardCache());
787 EXPECT_TRUE(observer2.has_committed());
788
789 // 3) Go back to A.
790 NavigationHandleObserver observer3(web_contents(), url_a);
Fergal Daly5495b0e2021-11-19 02:03:28791 ASSERT_TRUE(HistoryGoBack(web_contents()));
Hajime Hoshiac6b8542021-02-25 15:18:52792 EXPECT_FALSE(delete_observer_rfh_a.deleted());
793 EXPECT_FALSE(delete_observer_rfh_b.deleted());
794 EXPECT_EQ(rfh_a, current_frame_host());
795 EXPECT_FALSE(rfh_a->IsInBackForwardCache());
796 EXPECT_TRUE(rfh_b->IsInBackForwardCache());
797 EXPECT_TRUE(observer3.has_committed());
798 EXPECT_EQ("bar", observer3.GetNormalizedResponseHeader("x-foo"));
799
800 ExpectRestored(FROM_HERE);
801}
802
Fergal Daly973dc9e2021-11-19 09:38:17803void HighCacheSizeBackForwardCacheBrowserTest::SetUpCommandLine(
804 base::CommandLine* command_line) {
Fergal Dalyb1258112025-07-15 03:00:51805 EnableCacheSize(kBackForwardCacheSize, std::nullopt);
Fergal Daly973dc9e2021-11-19 09:38:17806 BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
807}
Rakina Zata Amni8d49da52020-11-06 09:23:10808
809// Test documents are evicted from the BackForwardCache at some point.
810IN_PROC_BROWSER_TEST_F(HighCacheSizeBackForwardCacheBrowserTest,
kouheie8cd7ea52021-05-24 05:24:26811 CacheEvictionWithIncreasedCacheSize) {
Rakina Zata Amni8d49da52020-11-06 09:23:10812 ASSERT_TRUE(embedded_test_server()->Start());
Lowell Manners9f1a5492019-09-10 09:28:44813
arthursonzogni72b66492019-11-04 12:24:33814 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
815 GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
Lowell Manners9f1a5492019-09-10 09:28:44816
817 EXPECT_TRUE(NavigateToURL(shell(), url_a)); // BackForwardCache size is 0.
818 RenderFrameHostImpl* rfh_a = current_frame_host();
819 RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
820
821 EXPECT_TRUE(NavigateToURL(shell(), url_b)); // BackForwardCache size is 1.
822 RenderFrameHostImpl* rfh_b = current_frame_host();
823 RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b);
824
Rakina Zata Amni8d49da52020-11-06 09:23:10825 for (size_t i = 2; i < kBackForwardCacheSize; ++i) {
Lowell Manners9f1a5492019-09-10 09:28:44826 EXPECT_TRUE(NavigateToURL(shell(), i % 2 ? url_b : url_a));
827 // After |i+1| navigations, |i| documents went into the BackForwardCache.
828 // When |i| is greater than the BackForwardCache size limit, they are
829 // evicted:
Rakina Zata Amni8d49da52020-11-06 09:23:10830 EXPECT_EQ(i >= kBackForwardCacheSize + 1, delete_observer_rfh_a.deleted());
831 EXPECT_EQ(i >= kBackForwardCacheSize + 2, delete_observer_rfh_b.deleted());
Lowell Manners9f1a5492019-09-10 09:28:44832 }
833}
834
Rakina Zata Amni2eed6322021-09-30 22:22:57835// Tests that evicting a page in between the time the back/forward cache
836// NavigationRequest restore was created and when the NavigationRequest actually
837// starts after finishing beforeunload won't result in a crash.
838// See https://crbug.com/1218114.
839IN_PROC_BROWSER_TEST_F(HighCacheSizeBackForwardCacheBrowserTest,
840 EvictedWhileWaitingForBeforeUnload) {
841 ASSERT_TRUE(embedded_test_server()->Start());
842 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
843 GURL url_b(embedded_test_server()->GetURL("b.com", "/title2.html"));
844 GURL url_c(embedded_test_server()->GetURL("c.com", "/title3.html"));
845
846 // 1) Navigate to A.
847 EXPECT_TRUE(NavigateToURL(shell(), url_a));
848 RenderFrameHostImplWrapper rfh_a(current_frame_host());
Rakina Zata Amni2eed6322021-09-30 22:22:57849
850 // 2) Navigate to B.
851 EXPECT_TRUE(NavigateToURL(shell(), url_b));
852 RenderFrameHostImplWrapper rfh_b(current_frame_host());
Rakina Zata Amni2eed6322021-09-30 22:22:57853 EXPECT_TRUE(rfh_a->IsInBackForwardCache());
854
855 // 3) Navigate to C, which has a beforeunload handler that never finishes.
856 EXPECT_TRUE(NavigateToURL(shell(), url_c));
857 RenderFrameHostImplWrapper rfh_c(current_frame_host());
858 EXPECT_TRUE(ExecJs(rfh_c.get(), R"(
859 window.onbeforeunload = () => {
860 while (true) {}
861 }
862 )"));
863 // Both A & B are in the back/forward cache.
864 EXPECT_TRUE(rfh_a->IsInBackForwardCache());
865 EXPECT_TRUE(rfh_b->IsInBackForwardCache());
866
867 // 4) Evict entry A. This will post a task that destroys all evicted entries
868 // when it runs (task #1).
869 DisableBFCacheForRFHForTesting(rfh_a->GetGlobalId());
870 EXPECT_FALSE(rfh_a.IsDestroyed());
871 EXPECT_TRUE(rfh_a->is_evicted_from_back_forward_cache());
872
873 // 5) Trigger a back navigation to B. This will create a BFCache restore
874 // navigation to B, but will wait for C's beforeunload handler to finish
875 // running before continuing.
Mingyu Lei7956b8b2023-07-24 08:24:08876 // The BFCache entry will be evicted before the back navigation completes, so
877 // the old navigation will be reset and a new navigation will be restarted.
878 // This observer is waiting for the two navigation requests to complete.
879 TestNavigationObserver observer(web_contents(),
880 /* expected_number_of_navigations= */ 2,
881 MessageLoopRunner::QuitMode::IMMEDIATE,
882 /* ignore_uncommitted_navigations= */ false);
Rakina Zata Amni2eed6322021-09-30 22:22:57883 web_contents()->GetController().GoBack();
884
885 // 6) Post a task to run BeforeUnloadCompleted (task #2). This will continue
886 // the BFCache restore navigation to B from step 5, which is currently waiting
887 // for a BeforeUnloadCompleted call.
Carlos Caballero15caeeb2021-10-27 09:57:55888 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Sean Maher5b9af51f2022-11-21 15:32:47889 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
Rakina Zata Amni2eed6322021-09-30 22:22:57890 FROM_HERE, base::BindLambdaForTesting([&]() {
Charlie Reis9e20dd12025-01-02 20:11:53891 root->navigator().BeforeUnloadCompleted(
892 root, /*proceed=*/true, base::TimeTicks::Now(),
893 /*for_legacy=*/false, /*showed_dialog=*/false);
Rakina Zata Amni2eed6322021-09-30 22:22:57894 }));
895
896 // 7) Evict entry B. This will post a task (task #3) to restart the navigation
897 // to B, and also another task (task #4) to destroy all evicted entries.
898 DisableBFCacheForRFHForTesting(rfh_b->GetGlobalId());
899 EXPECT_FALSE(rfh_b.IsDestroyed());
900 EXPECT_TRUE(rfh_b->is_evicted_from_back_forward_cache());
901
902 // 8) Wait until the back navigation to B finishes. This will run posted tasks
903 // in order. So:
904 // - Task #1 from step 4 will run and destroy all evicted entries. As both the
905 // entries for A & B have been evicted, they are both destroyed.
906 // - Task #2 from step 6 will run and continue the back/forward cache restore
907 // NavigationRequest to B. However, it would notice that the entry for B is
908 // now gone, and should handle it gracefully.
909 // - Task #3 from step 7 to restart navigation to B runs, and should create a
910 // NavigationRequest to replace the previous NavigationRequest to B.
911 // - Task #4 from step 7 to destroy evicted entries runs and won't destroy
912 // any entry since there's no longer any entry in the back/forward cache.
Mingyu Lei7956b8b2023-07-24 08:24:08913 observer.Wait();
Rakina Zata Amni2eed6322021-09-30 22:22:57914 EXPECT_EQ(web_contents()->GetLastCommittedURL(), url_b);
915 ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::
916 kDisableForRenderFrameHostCalled},
917 {}, {}, {RenderFrameHostDisabledForTestingReason()}, {},
918 FROM_HERE);
919}
920
Lowell Manners2d0163e42019-07-30 09:24:30921IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
Alexander Timin26864e12019-10-18 02:00:06922 SubframeWithOngoingNavigationNotCached) {
923 net::test_server::ControllableHttpResponse response(embedded_test_server(),
924 "/hung");
925 ASSERT_TRUE(embedded_test_server()->Start());
926
927 // Navigate to a page with an iframe.
928 TestNavigationObserver navigation_observer1(web_contents());
929 GURL main_url(embedded_test_server()->GetURL(
930 "a.com", "/back_forward_cache/page_with_hung_iframe.html"));
931 shell()->LoadURL(main_url);
932 navigation_observer1.WaitForNavigationFinished();
933
934 RenderFrameHostImpl* main_frame = current_frame_host();
935 RenderFrameDeletedObserver frame_deleted_observer(main_frame);
936 response.WaitForRequest();
937
938 // Navigate away.
939 TestNavigationObserver navigation_observer2(web_contents());
940 shell()->LoadURL(embedded_test_server()->GetURL("b.com", "/title1.html"));
941 navigation_observer2.WaitForNavigationFinished();
942
943 // The page with the unsupported feature should be deleted (not cached).
944 frame_deleted_observer.WaitUntilDeleted();
945}
946
arthursonzognie385b7b2019-09-02 11:11:53947// Only HTTP/HTTPS main document can enter the BackForwardCache.
948IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, CacheHTTPDocumentOnly) {
949 ASSERT_TRUE(embedded_test_server()->Start());
Fergal Daly637c4522019-11-22 00:21:31950 ASSERT_TRUE(CreateHttpsServer()->Start());
arthursonzognie385b7b2019-09-02 11:11:53951
Fergal Daly6b0db37e2022-06-27 05:41:03952 GURL http_url(embedded_test_server()->GetURL("a.test", "/title1.html"));
953 GURL https_url(https_server()->GetURL("a.test", "/title1.html"));
arthursonzognie385b7b2019-09-02 11:11:53954 GURL file_url = net::FilePathToFileURL(GetTestFilePath("", "title1.html"));
955 GURL data_url = GURL("data:text/html,");
956 GURL blank_url = GURL(url::kAboutBlankURL);
957 GURL webui_url = GetWebUIURL("gpu");
958
959 enum { STORED, DELETED };
960 struct {
961 int expectation;
962 GURL url;
963 } test_cases[] = {
964 // Only document with HTTP/HTTPS URLs are allowed to enter the
965 // BackForwardCache.
966 {STORED, http_url},
967 {STORED, https_url},
968
969 // Others aren't allowed.
970 {DELETED, file_url},
971 {DELETED, data_url},
972 {DELETED, webui_url},
973 {DELETED, blank_url},
974 };
975
976 char hostname[] = "a.unique";
977 for (auto& test_case : test_cases) {
978 SCOPED_TRACE(testing::Message()
979 << std::endl
980 << "expectation = " << test_case.expectation << std::endl
981 << "url = " << test_case.url << std::endl);
982
983 // 1) Navigate to.
984 EXPECT_TRUE(NavigateToURL(shell(), test_case.url));
Rakina Zata Amni348fe2b2021-07-09 05:28:52985 RenderFrameHostImplWrapper rfh(current_frame_host());
arthursonzognie385b7b2019-09-02 11:11:53986
987 // 2) Navigate away.
988 hostname[0]++;
989 GURL reset_url(embedded_test_server()->GetURL(hostname, "/title1.html"));
990 EXPECT_TRUE(NavigateToURL(shell(), reset_url));
991
992 if (test_case.expectation == STORED) {
Rakina Zata Amni348fe2b2021-07-09 05:28:52993 EXPECT_FALSE(rfh.IsRenderFrameDeleted());
Hajime Hoshibbc509c2020-04-06 08:55:08994 EXPECT_TRUE(rfh->IsInBackForwardCache());
arthursonzognie385b7b2019-09-02 11:11:53995 continue;
996 }
997
Rakina Zata Amni348fe2b2021-07-09 05:28:52998 if (rfh.get() == current_frame_host()) {
999 // If the RenderFrameHost is reused, it won't be deleted, so don't wait
1000 // for deletion. Just check that it's not saved in the back-forward cache.
1001 EXPECT_FALSE(rfh.IsRenderFrameDeleted());
Hajime Hoshibbc509c2020-04-06 08:55:081002 EXPECT_FALSE(rfh->IsInBackForwardCache());
arthursonzognie385b7b2019-09-02 11:11:531003 continue;
1004 }
1005
Rakina Zata Amni348fe2b2021-07-09 05:28:521006 // When the RenderFrameHost is not reused and it's not stored in the
1007 // back-forward cache, it will eventually be deleted.
Fergal Dalyd52b14f2021-11-08 13:25:021008 ASSERT_TRUE(rfh.WaitUntilRenderFrameDeleted());
arthursonzognie385b7b2019-09-02 11:11:531009 }
1010}
1011
arthursonzogni6c27c3152019-09-12 08:00:571012// Regression test for https://crbug.com/993337.
Lowell Mannerscc67fc62019-10-18 10:21:471013//
1014// A note about sharing BrowsingInstances and the BackForwardCache:
1015//
1016// We should never keep around more than one main frame that belongs to the same
1017// BrowsingInstance. When swapping two pages, when one is stored in the
1018// back-forward cache or one is restored from it, the current code expects the
1019// two to live in different BrowsingInstances.
1020//
1021// History navigation can recreate a page with the same BrowsingInstance as the
1022// one stored in the back-forward cache. This case must to be handled. When it
1023// happens, the back-forward cache page is evicted.
1024//
1025// Since cache eviction is asynchronous, it's is possible for two main frames
1026// belonging to the same BrowsingInstance to be alive for a brief period of time
1027// (the new page being navigated to, and a page in the cache, until it is
1028// destroyed asynchronously via eviction).
1029//
1030// The test below tests that the brief period of time where two main frames are
1031// alive in the same BrowsingInstance does not cause anything to blow up.
Asami Doi5dd0f6b2021-08-23 08:27:041032
Mingyu Lei6ad77ef2023-05-23 07:13:231033// TODO(crbug.com/1127979, crbug.com/1446206): Flaky on Linux, Windows and
Ian Vollickf5b8bdf2023-07-26 15:52:271034// ChromeOS, iOS, and Mac.
Brian Begnoche3db0d872023-07-24 17:48:331035#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS) || \
Ian Vollickf5b8bdf2023-07-26 15:52:271036 BUILDFLAG(IS_MAC) || BUILDFLAG(IS_IOS)
Yuzu Saijo8ba605f2023-03-27 02:52:151037#define MAYBE_NavigateToTwoPagesOnSameSite DISABLED_NavigateToTwoPagesOnSameSite
1038#else
1039#define MAYBE_NavigateToTwoPagesOnSameSite NavigateToTwoPagesOnSameSite
1040#endif
Lowell Mannersfa8d9222019-09-05 09:47:031041IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
Yuzu Saijo8ba605f2023-03-27 02:52:151042 MAYBE_NavigateToTwoPagesOnSameSite) {
Lowell Mannersfa8d9222019-09-05 09:47:031043 ASSERT_TRUE(embedded_test_server()->Start());
1044 GURL url_a1(embedded_test_server()->GetURL("a.com", "/title1.html"));
1045 GURL url_a2(embedded_test_server()->GetURL("a.com", "/title2.html"));
Lowell Mannerscc67fc62019-10-18 10:21:471046 GURL url_b3(embedded_test_server()->GetURL("b.com", "/title1.html"));
Lowell Mannersfa8d9222019-09-05 09:47:031047
1048 // 1) Navigate to A1.
1049 EXPECT_TRUE(NavigateToURL(shell(), url_a1));
1050
1051 // 2) Navigate to A2.
1052 EXPECT_TRUE(NavigateToURL(shell(), url_a2));
1053 RenderFrameHostImpl* rfh_a2 = current_frame_host();
arthursonzogni6c27c3152019-09-12 08:00:571054 RenderFrameDeletedObserver delete_rfh_a2(current_frame_host());
Lowell Mannersfa8d9222019-09-05 09:47:031055
Lowell Mannerscc67fc62019-10-18 10:21:471056 // 3) Navigate to B3.
1057 EXPECT_TRUE(NavigateToURL(shell(), url_b3));
Hajime Hoshibbc509c2020-04-06 08:55:081058 EXPECT_TRUE(rfh_a2->IsInBackForwardCache());
Lowell Mannerscc67fc62019-10-18 10:21:471059 RenderFrameHostImpl* rfh_b3 = current_frame_host();
Lowell Mannersfa8d9222019-09-05 09:47:031060
1061 // 4) Do a history navigation back to A1.
Fergal Daly5495b0e2021-11-19 02:03:281062 ASSERT_TRUE(HistoryGoToIndex(web_contents(), 0));
Hajime Hoshibbc509c2020-04-06 08:55:081063 EXPECT_TRUE(rfh_b3->IsInBackForwardCache());
arthursonzogni6c27c3152019-09-12 08:00:571064
Lowell Mannerscc67fc62019-10-18 10:21:471065 // Note that the frame for A1 gets created before A2 is deleted from the
1066 // cache, so there will be a brief period where two the main frames (A1 and
1067 // A2) are alive in the same BrowsingInstance/SiteInstance, at the same time.
1068 // That is the scenario this test is covering. This used to cause a CHECK,
1069 // because the two main frames shared a single RenderViewHost (no longer the
1070 // case after https://crrev.com/c/1833616).
1071
1072 // A2 should be evicted from the cache and asynchronously deleted, due to the
1073 // cache size limit (B3 took its place in the cache).
arthursonzogni6c27c3152019-09-12 08:00:571074 delete_rfh_a2.WaitUntilDeleted();
Lowell Mannersfa8d9222019-09-05 09:47:031075}
Kouhei Ueno7b6f3cd02019-09-09 04:48:501076
Lowell Mannerscc67fc62019-10-18 10:21:471077IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
1078 NavigateToTwoPagesOnSameSiteWithSubframes) {
1079 ASSERT_TRUE(embedded_test_server()->Start());
1080 // This test covers the same scenario as NavigateToTwoPagesOnSameSite, except
1081 // the pages contain subframes:
1082 // A1(B) -> A2(B(C)) -> D3 -> A1(B)
1083 //
1084 // The subframes shouldn't make a difference, so the expected behavior is the
1085 // same as NavigateToTwoPagesOnSameSite.
1086 GURL url_a1(embedded_test_server()->GetURL(
1087 "a.com", "/cross_site_iframe_factory.html?a(b)"));
1088 GURL url_a2(embedded_test_server()->GetURL(
1089 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
1090 GURL url_d3(embedded_test_server()->GetURL("d.com", "/title1.html"));
1091
1092 // 1) Navigate to A1(B).
1093 EXPECT_TRUE(NavigateToURL(shell(), url_a1));
1094
1095 // 2) Navigate to A2(B(C)).
1096 EXPECT_TRUE(NavigateToURL(shell(), url_a2));
1097 RenderFrameHostImpl* rfh_a2 = current_frame_host();
1098 RenderFrameDeletedObserver delete_rfh_a2(current_frame_host());
1099
1100 // 3) Navigate to D3.
1101 EXPECT_TRUE(NavigateToURL(shell(), url_d3));
Hajime Hoshibbc509c2020-04-06 08:55:081102 EXPECT_TRUE(rfh_a2->IsInBackForwardCache());
Lowell Mannerscc67fc62019-10-18 10:21:471103 RenderFrameHostImpl* rfh_d3 = current_frame_host();
1104
1105 // 4) Do a history navigation back to A1(B).
Fergal Daly5495b0e2021-11-19 02:03:281106 ASSERT_TRUE(HistoryGoToIndex(web_contents(), 0));
Lowell Mannerscc67fc62019-10-18 10:21:471107
1108 // D3 takes A2(B(C))'s place in the cache.
Hajime Hoshibbc509c2020-04-06 08:55:081109 EXPECT_TRUE(rfh_d3->IsInBackForwardCache());
Lowell Mannerscc67fc62019-10-18 10:21:471110 delete_rfh_a2.WaitUntilDeleted();
1111}
1112
Sreeja Kamishetty299329ad2021-03-25 14:06:011113// Sub-frame doesn't transition from LifecycleStateImpl::kInBackForwardCache to
1114// LifecycleStateImpl::kRunningUnloadHandlers even when the sub-frame having
1115// unload handlers is being evicted from BackForwardCache.
Fergal Daly97c4e592023-02-28 10:27:331116IN_PROC_BROWSER_TEST_F(BackForwardCacheUnloadBrowserTest,
1117 SubframeWithUnloadHandler) {
Sreeja Kamishetty9bcdc972020-10-23 13:33:211118 ASSERT_TRUE(embedded_test_server()->Start());
1119 GURL main_url(embedded_test_server()->GetURL(
1120 "a.com", "/cross_site_iframe_factory.html?a.com(a.com)"));
1121 GURL child_url = embedded_test_server()->GetURL(
1122 "a.com", "/cross_site_iframe_factory.html?a.com()");
1123 GURL url_2(embedded_test_server()->GetURL("a.com", "/title1.html"));
1124
1125 // 1) Navigate to |main_url|.
1126 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1127 RenderFrameHostImpl* main_rfh = current_frame_host();
1128 ASSERT_EQ(1U, main_rfh->child_count());
1129 RenderFrameHostImpl* child_rfh = main_rfh->child_at(0)->current_frame_host();
1130 RenderFrameDeletedObserver main_rfh_observer(main_rfh),
1131 child_rfh_observer(child_rfh);
1132
1133 // 2) Add an unload handler to the child RFH.
1134 EXPECT_TRUE(ExecJs(child_rfh, "window.onunload = () => {} "));
1135
1136 // 3) Navigate to |url_2|.
1137 EXPECT_TRUE(NavigateToURL(shell(), url_2));
1138
1139 // 4) The previous main RFH and child RFH should be in the back-forward
1140 // cache.
1141 EXPECT_FALSE(main_rfh_observer.deleted());
1142 EXPECT_FALSE(child_rfh_observer.deleted());
1143 EXPECT_TRUE(main_rfh->IsInBackForwardCache());
1144 EXPECT_TRUE(child_rfh->IsInBackForwardCache());
1145
1146 // Destruction of bfcached page happens after shutdown and it should not
1147 // trigger unload handlers and be destroyed directly.
1148}
1149
Carlos Caballero2380e022019-10-30 18:27:181150// Do a same document navigation and make sure we do not fire the
1151// DidFirstVisuallyNonEmptyPaint again
Carlos Caballero91fffb22019-10-29 15:24:301152IN_PROC_BROWSER_TEST_F(
1153 BackForwardCacheBrowserTest,
Georg Neis0dce3332024-12-13 01:51:181154 DoesNotFireDidFirstVisuallyNonEmptyPaintForSameDocumentNavigation) {
Carlos Caballero91fffb22019-10-29 15:24:301155 ASSERT_TRUE(embedded_test_server()->Start());
arthursonzogni72b66492019-11-04 12:24:331156 GURL url_a_1(embedded_test_server()->GetURL(
Carlos Caballero91fffb22019-10-29 15:24:301157 "a.com", "/accessibility/html/a-name.html"));
arthursonzogni72b66492019-11-04 12:24:331158 GURL url_a_2(embedded_test_server()->GetURL(
Carlos Caballero91fffb22019-10-29 15:24:301159 "a.com", "/accessibility/html/a-name.html#id"));
Carlos Caballero91fffb22019-10-29 15:24:301160
1161 EXPECT_TRUE(NavigateToURL(shell(), url_a_1));
1162 WaitForFirstVisuallyNonEmptyPaint(shell()->web_contents());
Carlos Caballero91fffb22019-10-29 15:24:301163
1164 FirstVisuallyNonEmptyPaintObserver observer(web_contents());
1165 EXPECT_TRUE(NavigateToURL(shell(), url_a_2));
1166 // Make sure the bfcache restore code does not fire the event during commit
1167 // navigation.
1168 EXPECT_FALSE(observer.did_fire());
Carlos Caballero2380e022019-10-30 18:27:181169 EXPECT_TRUE(web_contents()->CompletedFirstVisuallyNonEmptyPaint());
Carlos Caballero91fffb22019-10-29 15:24:301170}
1171
1172// Make sure we fire DidFirstVisuallyNonEmptyPaint when restoring from bf-cache.
Brian Begnochebc623f32024-02-27 17:46:191173// TODO(crbug.com/327195951): Re-enable this test
Georg Neis0dce3332024-12-13 01:51:181174#if BUILDFLAG(IS_ANDROID)
Brian Begnochebc623f32024-02-27 17:46:191175#define MAYBE_FiresDidFirstVisuallyNonEmptyPaintWhenRestoredFromCache \
1176 DISABLED_FiresDidFirstVisuallyNonEmptyPaintWhenRestoredFromCache
1177#else
1178#define MAYBE_FiresDidFirstVisuallyNonEmptyPaintWhenRestoredFromCache \
1179 FiresDidFirstVisuallyNonEmptyPaintWhenRestoredFromCache
1180#endif
Carlos Caballero91fffb22019-10-29 15:24:301181IN_PROC_BROWSER_TEST_F(
1182 BackForwardCacheBrowserTest,
Brian Begnochebc623f32024-02-27 17:46:191183 MAYBE_FiresDidFirstVisuallyNonEmptyPaintWhenRestoredFromCache) {
Carlos Caballero91fffb22019-10-29 15:24:301184 ASSERT_TRUE(embedded_test_server()->Start());
arthursonzogni72b66492019-11-04 12:24:331185 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
1186 GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
Carlos Caballero91fffb22019-10-29 15:24:301187
1188 // 1) Navigate to A.
Fergal Dalya4dde50d2022-10-14 07:29:251189 ASSERT_TRUE(NavigateToURL(shell(), url_a));
Carlos Caballero91fffb22019-10-29 15:24:301190 WaitForFirstVisuallyNonEmptyPaint(shell()->web_contents());
1191 RenderFrameHostImpl* rfh_a = current_frame_host();
Carlos Caballeroddf98dc2019-10-30 19:00:471192 RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
Carlos Caballero91fffb22019-10-29 15:24:301193
1194 // 2) Navigate to B.
Fergal Dalya4dde50d2022-10-14 07:29:251195 ASSERT_TRUE(NavigateToURL(shell(), url_b));
Carlos Caballeroddf98dc2019-10-30 19:00:471196 ASSERT_FALSE(delete_observer_rfh_a.deleted());
Fergal Dalya4dde50d2022-10-14 07:29:251197 ASSERT_TRUE(rfh_a->IsInBackForwardCache());
Carlos Caballero91fffb22019-10-29 15:24:301198 WaitForFirstVisuallyNonEmptyPaint(shell()->web_contents());
1199
1200 // 3) Navigate to back to A.
1201 FirstVisuallyNonEmptyPaintObserver observer(web_contents());
Fergal Daly5495b0e2021-11-19 02:03:281202 ASSERT_TRUE(HistoryGoBack(web_contents()));
Carlos Caballero91fffb22019-10-29 15:24:301203 // Make sure the bfcache restore code does fire the event during commit
1204 // navigation.
1205 EXPECT_TRUE(web_contents()->CompletedFirstVisuallyNonEmptyPaint());
1206 EXPECT_TRUE(observer.did_fire());
1207}
Georg Neis0dce3332024-12-13 01:51:181208#if BUILDFLAG(IS_ANDROID)
Takumi Fujimoto24fbc77f2024-03-22 06:42:081209#define MAYBE_SetsThemeColorWhenRestoredFromCache \
1210 DISABLED_SetsThemeColorWhenRestoredFromCache
1211#else
1212#define MAYBE_SetsThemeColorWhenRestoredFromCache \
1213 SetsThemeColorWhenRestoredFromCache
1214#endif
Carlos Caballero1215f882019-10-29 15:58:431215IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
Takumi Fujimoto24fbc77f2024-03-22 06:42:081216 MAYBE_SetsThemeColorWhenRestoredFromCache) {
Carlos Caballero1215f882019-10-29 15:58:431217 ASSERT_TRUE(embedded_test_server()->Start());
arthursonzogni72b66492019-11-04 12:24:331218 GURL url_a(embedded_test_server()->GetURL("a.com", "/theme_color.html"));
1219 GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
Carlos Caballero1215f882019-10-29 15:58:431220
Fergal Dalya4dde50d2022-10-14 07:29:251221 ASSERT_TRUE(NavigateToURL(shell(), url_a));
Carlos Caballero1215f882019-10-29 15:58:431222 WaitForFirstVisuallyNonEmptyPaint(web_contents());
Fergal Dalya4dde50d2022-10-14 07:29:251223 RenderFrameHostImplWrapper rfh_a(current_frame_host());
Carlos Caballero1215f882019-10-29 15:58:431224 EXPECT_EQ(web_contents()->GetThemeColor(), 0xFFFF0000u);
1225
Fergal Dalya4dde50d2022-10-14 07:29:251226 ASSERT_TRUE(NavigateToURL(shell(), url_b));
Carlos Caballero1215f882019-10-29 15:58:431227 WaitForFirstVisuallyNonEmptyPaint(web_contents());
Fergal Dalya4dde50d2022-10-14 07:29:251228 ASSERT_TRUE(rfh_a->IsInBackForwardCache());
Arthur Sonzognic686e8f2024-01-11 08:36:371229 EXPECT_EQ(web_contents()->GetThemeColor(), std::nullopt);
Carlos Caballero1215f882019-10-29 15:58:431230
1231 ThemeColorObserver observer(web_contents());
Fergal Daly5495b0e2021-11-19 02:03:281232 ASSERT_TRUE(HistoryGoBack(web_contents()));
Fergal Dalya4dde50d2022-10-14 07:29:251233 ASSERT_TRUE(observer.WaitUntilThemeColorChange());
Carlos Caballero1215f882019-10-29 15:58:431234 EXPECT_EQ(web_contents()->GetThemeColor(), 0xFFFF0000u);
1235}
1236
Yuzu Saijo40aa84e72020-05-28 07:18:281237IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
1238 ContentsMimeTypeWhenRestoredFromCache) {
1239 ASSERT_TRUE(embedded_test_server()->Start());
1240 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
1241 GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
1242
1243 // Navigate to A.
1244 EXPECT_TRUE(NavigateToURL(shell(), url_a));
1245 RenderFrameHostImpl* rfh_a = current_frame_host();
1246 RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
1247 EXPECT_EQ(web_contents()->GetContentsMimeType(), "text/html");
1248
1249 // Navigate to B.
1250 EXPECT_TRUE(NavigateToURL(shell(), url_b));
1251 ASSERT_FALSE(delete_observer_rfh_a.deleted());
1252 EXPECT_TRUE(rfh_a->IsInBackForwardCache());
1253
1254 // Go back to A, which restores A from bfcache. ContentsMimeType should be
1255 // restored as well.
Fergal Daly5495b0e2021-11-19 02:03:281256 ASSERT_TRUE(HistoryGoBack(web_contents()));
Yuzu Saijo40aa84e72020-05-28 07:18:281257 EXPECT_EQ(rfh_a, current_frame_host());
Fergal Daly09833062021-02-09 07:10:001258 ExpectRestored(FROM_HERE);
Yuzu Saijo40aa84e72020-05-28 07:18:281259 EXPECT_EQ(web_contents()->GetContentsMimeType(), "text/html");
1260}
1261
Sreeja Kamishetty92006e02021-02-05 05:18:111262// Check BackForwardCache is enabled and works for devices with very low memory.
1263// Navigate from A -> B and go back.
1264IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
1265 BackForwardCacheEnabledOnLowMemoryDevices) {
1266 // Set device physical memory to 10 MB.
1267 blink::ApproximatedDeviceMemory::SetPhysicalMemoryMBForTesting(10);
1268 ASSERT_TRUE(embedded_test_server()->Start());
1269 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
1270 GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
1271
1272 // 1) Navigate to A.
1273 EXPECT_TRUE(NavigateToURL(shell(), url_a));
1274 RenderFrameDeletedObserver delete_observer_rfh_a(current_frame_host());
1275 RenderFrameHostImpl* rfh_a = current_frame_host();
1276
1277 // 2) Navigate to B. A should be in BackForwardCache.
1278 EXPECT_TRUE(NavigateToURL(shell(), url_b));
1279 RenderFrameHostImpl* rfh_b = current_frame_host();
1280 RenderFrameDeletedObserver delete_observer_rfh_b(rfh_b);
1281 EXPECT_FALSE(delete_observer_rfh_a.deleted());
1282 EXPECT_TRUE(rfh_a->IsInBackForwardCache());
1283
1284 // 3) Go back to A. B should be in BackForwardCache.
Fergal Daly5495b0e2021-11-19 02:03:281285 ASSERT_TRUE(HistoryGoBack(web_contents()));
Sreeja Kamishetty92006e02021-02-05 05:18:111286 EXPECT_FALSE(delete_observer_rfh_b.deleted());
1287 EXPECT_TRUE(rfh_b->IsInBackForwardCache());
1288}
1289
[email protected]54eaf622019-11-25 12:36:031290// Test for functionality of memory controls in back-forward cache for low
1291// memory devices.
1292class BackForwardCacheBrowserTestForLowMemoryDevices
1293 : public BackForwardCacheBrowserTest {
1294 protected:
1295 void SetUpCommandLine(base::CommandLine* command_line) override {
Sreeja Kamishetty92006e02021-02-05 05:18:111296 BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
1297
[email protected]54eaf622019-11-25 12:36:031298 // Set the value of memory threshold more than the physical memory and check
1299 // if back-forward cache is disabled or not.
François Doray9cee8042025-08-15 19:02:551300 std::string memory_threshold = base::NumberToString(
1301 base::SysInfo::AmountOfPhysicalMemory().InMiB() + 1);
Sreeja Kamishetty92006e02021-02-05 05:18:111302 scoped_feature_list_.InitWithFeaturesAndParameters(
John Abd-El-Malek3c868c292021-02-19 20:00:091303 {{features::kBackForwardCacheMemoryControls,
Sreeja Kamishetty92006e02021-02-05 05:18:111304 {{"memory_threshold_for_back_forward_cache_in_mb",
Rakina Zata Amnib6b7f7bf2021-02-12 10:25:391305 memory_threshold}}},
1306 {blink::features::kLoadingTasksUnfreezable, {}}},
Sreeja Kamishetty92006e02021-02-05 05:18:111307 {});
[email protected]54eaf622019-11-25 12:36:031308 }
Sreeja Kamishetty92006e02021-02-05 05:18:111309
1310 private:
1311 base::test::ScopedFeatureList scoped_feature_list_;
[email protected]54eaf622019-11-25 12:36:031312};
1313
Nathan Memmottb2aa56652024-12-11 02:17:531314// Ensure that the BackForwardCache trial is not activated as expected on
Rakina Zata Amni454f5a902022-05-31 06:18:021315// low-memory devices.
[email protected]54eaf622019-11-25 12:36:031316IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestForLowMemoryDevices,
1317 DisableBFCacheForLowEndDevices) {
[email protected]54eaf622019-11-25 12:36:031318 ASSERT_TRUE(embedded_test_server()->Start());
Alexander Timin3e88b8f2019-12-16 16:32:201319 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
1320 GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
[email protected]54eaf622019-11-25 12:36:031321
Nathan Memmottb2aa56652024-12-11 02:17:531322 // Ensure that the BackForwardCache trial starts inactive.
Alexander Timinddc49312020-02-28 15:24:191323 EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
1324 base::FeatureList::GetFieldTrial(features::kBackForwardCache)
1325 ->trial_name()));
1326
1327 EXPECT_FALSE(IsBackForwardCacheEnabled());
1328
Rakina Zata Amni454f5a902022-05-31 06:18:021329 // Ensure that we do not activate the BackForwardCache trial when querying
Nathan Memmottb2aa56652024-12-11 02:17:531330 // bfcache status.
Alexander Timinddc49312020-02-28 15:24:191331 EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
1332 base::FeatureList::GetFieldTrial(features::kBackForwardCache)
1333 ->trial_name()));
1334
[email protected]54eaf622019-11-25 12:36:031335 // 1) Navigate to A.
1336 EXPECT_TRUE(NavigateToURL(shell(), url_a));
1337 RenderFrameHostImpl* rfh_a = current_frame_host();
1338 RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
1339
1340 // 2) Navigate to B.
1341 EXPECT_TRUE(NavigateToURL(shell(), url_b));
1342
1343 // 3) A shouldn't be stored in back-forward cache because the physical
1344 // memory is less than the memory threshold.
1345 delete_observer_rfh_a.WaitUntilDeleted();
1346
Rakina Zata Amni454f5a902022-05-31 06:18:021347 // 4) Go back to check the
1348 // NotRestoredReasons.kBackForwardCacheDisabledByLowMemory is recorded when
1349 // the memory is less than the threshold value.
1350 ASSERT_TRUE(HistoryGoBack(web_contents()));
Alexander Timinddc49312020-02-28 15:24:191351
Rakina Zata Amni454f5a902022-05-31 06:18:021352 ExpectNotRestored(
1353 {
1354 BackForwardCacheMetrics::NotRestoredReason::kBackForwardCacheDisabled,
1355 BackForwardCacheMetrics::NotRestoredReason::
1356 kBackForwardCacheDisabledByLowMemory,
1357 },
1358 {}, {}, {}, {}, FROM_HERE);
1359
Nathan Memmottb2aa56652024-12-11 02:17:531360 // Ensure that the BackForwardCache trial still hasn't been activated.
Alexander Timinddc49312020-02-28 15:24:191361 EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
1362 base::FeatureList::GetFieldTrial(features::kBackForwardCache)
1363 ->trial_name()));
[email protected]54eaf622019-11-25 12:36:031364}
1365
Rakina Zata Amnib6b7f7bf2021-02-12 10:25:391366// Trigger network reqeuests, then navigate from A to B, then go back.
1367IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestForLowMemoryDevices,
Rakina Zata Amnieaf87b72021-03-02 02:04:351368 DisableBFCacheForLowEndDevices_NetworkRequests) {
Rakina Zata Amnib6b7f7bf2021-02-12 10:25:391369 net::test_server::ControllableHttpResponse image_response(
1370 embedded_test_server(), "/image.png");
1371 ASSERT_TRUE(embedded_test_server()->Start());
1372 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
1373 GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
1374
1375 // Ensure that the trials starts inactive.
1376 EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
1377 base::FeatureList::GetFieldTrial(features::kBackForwardCache)
1378 ->trial_name()));
1379 EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
1380 base::FeatureList::GetFieldTrial(
1381 blink::features::kLoadingTasksUnfreezable)
1382 ->trial_name()));
1383
1384 EXPECT_FALSE(IsBackForwardCacheEnabled());
1385
1386 // Ensure that we do not activate the trials for kBackForwardCache and
1387 // kLoadingTasksUnfreezable when querying bfcache or unfreezable loading tasks
1388 // status.
1389 EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
1390 base::FeatureList::GetFieldTrial(features::kBackForwardCache)
1391 ->trial_name()));
1392 EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
1393 base::FeatureList::GetFieldTrial(
1394 blink::features::kLoadingTasksUnfreezable)
1395 ->trial_name()));
1396
1397 // 1) Navigate to A.
1398 EXPECT_TRUE(NavigateToURL(shell(), url_a));
1399 RenderFrameHostImpl* rfh_a = current_frame_host();
1400 RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
1401
1402 // Request for an image and send a response to trigger loading code. This is
1403 // to ensure kLoadingTasksUnfreezable won't trigger bfcache activation.
1404 EXPECT_TRUE(ExecJs(rfh_a, R"(
1405 var image = document.createElement("img");
1406 image.src = "image.png";
1407 document.body.appendChild(image);
1408 )"));
1409 image_response.WaitForRequest();
1410 image_response.Send(net::HTTP_OK, "image/png");
1411 image_response.Send("image_body");
1412 image_response.Done();
1413
1414 // 2) Navigate to B.
1415 EXPECT_TRUE(NavigateToURL(shell(), url_b));
1416
1417 // 3) A shouldn't be stored in back-forward cache because the physical
1418 // memory is less than the memory threshold.
1419 delete_observer_rfh_a.WaitUntilDeleted();
1420
1421 // Nothing is recorded when the memory is less than the threshold value.
1422 ExpectOutcomeDidNotChange(FROM_HERE);
1423 ExpectNotRestoredDidNotChange(FROM_HERE);
1424
1425 // Ensure that the trials still haven't been activated.
1426 EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
1427 base::FeatureList::GetFieldTrial(features::kBackForwardCache)
1428 ->trial_name()));
1429 EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
1430 base::FeatureList::GetFieldTrial(
1431 blink::features::kLoadingTasksUnfreezable)
1432 ->trial_name()));
1433}
1434
[email protected]54eaf622019-11-25 12:36:031435// Test for functionality of memory controls in back-forward cache for high
1436// memory devices.
1437class BackForwardCacheBrowserTestForHighMemoryDevices
1438 : public BackForwardCacheBrowserTest {
1439 protected:
1440 void SetUpCommandLine(base::CommandLine* command_line) override {
Rakina Zata Amni454f5a902022-05-31 06:18:021441 BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
1442
[email protected]54eaf622019-11-25 12:36:031443 // Set the value of memory threshold less than the physical memory and check
1444 // if back-forward cache is enabled or not.
François Doray9cee8042025-08-15 19:02:551445 std::string memory_threshold = base::NumberToString(
1446 base::SysInfo::AmountOfPhysicalMemory().InMiB() - 1);
Rakina Zata Amni454f5a902022-05-31 06:18:021447 scoped_feature_list_.InitWithFeaturesAndParameters(
1448 {{features::kBackForwardCacheMemoryControls,
1449 {{"memory_threshold_for_back_forward_cache_in_mb",
1450 memory_threshold}}},
Rakina Zata Amni454f5a902022-05-31 06:18:021451 {blink::features::kLoadingTasksUnfreezable, {}}},
1452 {});
[email protected]54eaf622019-11-25 12:36:031453 }
Rakina Zata Amni454f5a902022-05-31 06:18:021454
1455 private:
1456 base::test::ScopedFeatureList scoped_feature_list_;
[email protected]54eaf622019-11-25 12:36:031457};
1458
Nathan Memmottb2aa56652024-12-11 02:17:531459// Ensure that the BackForwardCache trial got activated as expected on
1460// high-memory devices when the BackForwardCache feature is enabled.
[email protected]54eaf622019-11-25 12:36:031461IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestForHighMemoryDevices,
1462 EnableBFCacheForHighMemoryDevices) {
Nathan Memmottb2aa56652024-12-11 02:17:531463 // Ensure that the BackForwardCache trial starts active on high-memory devices
1464 // when the BackForwardCache feature is enabled, because
1465 // IsBackForwardCacheEnabled() got queried already before the test starts.
Rakina Zata Amni454f5a902022-05-31 06:18:021466 EXPECT_TRUE(base::FieldTrialList::IsTrialActive(
1467 base::FeatureList::GetFieldTrial(features::kBackForwardCache)
1468 ->trial_name()));
Rakina Zata Amni454f5a902022-05-31 06:18:021469
1470 EXPECT_TRUE(IsBackForwardCacheEnabled());
1471
Nathan Memmottb2aa56652024-12-11 02:17:531472 // Ensure that the BackForwardCache trial stays active after querying
1473 // IsBackForwardCacheEnabled().
Rakina Zata Amni454f5a902022-05-31 06:18:021474 EXPECT_TRUE(base::FieldTrialList::IsTrialActive(
1475 base::FeatureList::GetFieldTrial(features::kBackForwardCache)
1476 ->trial_name()));
Rakina Zata Amni454f5a902022-05-31 06:18:021477
[email protected]54eaf622019-11-25 12:36:031478 ASSERT_TRUE(embedded_test_server()->Start());
Alexander Timin3e88b8f2019-12-16 16:32:201479 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
1480 GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
[email protected]54eaf622019-11-25 12:36:031481
1482 // 1) Navigate to A.
1483 EXPECT_TRUE(NavigateToURL(shell(), url_a));
1484 RenderFrameHostImpl* rfh_a = current_frame_host();
1485
1486 // 2) Navigate to B.
1487 EXPECT_TRUE(NavigateToURL(shell(), url_b));
1488
1489 // 3) A should be stored in back-forward cache because the physical memory is
1490 // greater than the memory threshold.
Hajime Hoshibbc509c2020-04-06 08:55:081491 EXPECT_TRUE(rfh_a->IsInBackForwardCache());
Rakina Zata Amni454f5a902022-05-31 06:18:021492
Nathan Memmottb2aa56652024-12-11 02:17:531493 // Ensure that the BackForwardCache trial stays active.
Rakina Zata Amni454f5a902022-05-31 06:18:021494 EXPECT_TRUE(base::FieldTrialList::IsTrialActive(
1495 base::FeatureList::GetFieldTrial(features::kBackForwardCache)
1496 ->trial_name()));
[email protected]54eaf622019-11-25 12:36:031497}
Alexander Timine92f1652019-11-27 14:51:181498
Rakina Zata Amnib6b7f7bf2021-02-12 10:25:391499// Trigger network reqeuests, then navigate from A to B, then go back.
1500IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTestForHighMemoryDevices,
Rakina Zata Amnieaf87b72021-03-02 02:04:351501 EnableBFCacheForHighMemoryDevices_NetworkRequests) {
Rakina Zata Amnib6b7f7bf2021-02-12 10:25:391502 net::test_server::ControllableHttpResponse image_response(
1503 embedded_test_server(), "/image.png");
1504 ASSERT_TRUE(embedded_test_server()->Start());
1505 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
1506 GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
1507
1508 // Ensure that back-forward cache flag is enabled and the trial is active.
1509 EXPECT_TRUE(IsBackForwardCacheEnabled());
1510 EXPECT_TRUE(base::FieldTrialList::IsTrialActive(
1511 base::FeatureList::GetFieldTrial(features::kBackForwardCache)
1512 ->trial_name()));
1513
1514 // Ensure that the LoadingTasksUnfreezable trials starts as inactive.
1515 EXPECT_FALSE(base::FieldTrialList::IsTrialActive(
1516 base::FeatureList::GetFieldTrial(
1517 blink::features::kLoadingTasksUnfreezable)
1518 ->trial_name()));
1519
1520 // 1) Navigate to A.
1521 EXPECT_TRUE(NavigateToURL(shell(), url_a));
1522 RenderFrameHostImpl* rfh_a = current_frame_host();
1523 RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
1524
1525 // Request for an image and send a response to trigger loading code.
1526 EXPECT_TRUE(ExecJs(rfh_a, R"(
1527 var image = document.createElement("img");
1528 image.src = "image.png";
1529 document.body.appendChild(image);
1530 )"));
1531 image_response.WaitForRequest();
1532 image_response.Send(net::HTTP_OK, "image/png");
1533 image_response.Send("image_body");
1534 image_response.Done();
1535
1536 // The loading code activates the LoadingTasksUnfreezable trial.
1537 EXPECT_TRUE(base::FieldTrialList::IsTrialActive(
1538 base::FeatureList::GetFieldTrial(
1539 blink::features::kLoadingTasksUnfreezable)
1540 ->trial_name()));
1541
1542 // 2) Navigate to B.
1543 EXPECT_TRUE(NavigateToURL(shell(), url_b));
1544
1545 // 3) A should be stored in back-forward cache because the physical memory is
1546 // greater than the memory threshold.
1547 EXPECT_TRUE(rfh_a->IsInBackForwardCache());
1548
1549 // Ensure that the trials stay activated.
1550 EXPECT_TRUE(base::FieldTrialList::IsTrialActive(
1551 base::FeatureList::GetFieldTrial(features::kBackForwardCache)
1552 ->trial_name()));
1553 EXPECT_TRUE(base::FieldTrialList::IsTrialActive(
1554 base::FeatureList::GetFieldTrial(
1555 blink::features::kLoadingTasksUnfreezable)
1556 ->trial_name()));
1557}
1558
Rakina Zata Amni454f5a902022-05-31 06:18:021559// Tests for high memory devices that have the BackForwardCache feature flag
1560// disabled.
1561class BackForwardCacheBrowserTestForHighMemoryDevicesWithBFCacheDisabled
1562 : public BackForwardCacheBrowserTest {
1563 protected:
1564 void SetUpCommandLine(base::CommandLine* command_line) override {
1565 BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
1566
1567 // Set the value of memory threshold less than the physical memory and check
1568 // if back-forward cache is enabled or not.
François Doray9cee8042025-08-15 19:02:551569 std::string memory_threshold = base::NumberToString(
1570 base::SysInfo::AmountOfPhysicalMemory().InMiB() - 1);
Rakina Zata Amni454f5a902022-05-31 06:18:021571 scoped_feature_list_.InitWithFeaturesAndParameters(
1572 /*enabled_features=*/
1573 {{features::kBackForwardCacheMemoryControls,
1574 {{"memory_threshold_for_back_forward_cache_in_mb",
1575 memory_threshold}}},
Rakina Zata Amni454f5a902022-05-31 06:18:021576 {blink::features::kLoadingTasksUnfreezable, {}}},
1577 /*disabled_features=*/
1578 {features::kBackForwardCache});
1579 }
1580
1581 private:
1582 base::test::ScopedFeatureList scoped_feature_list_;
1583};
1584
Rakina Zata Amni454f5a902022-05-31 06:18:021585IN_PROC_BROWSER_TEST_F(
1586 BackForwardCacheBrowserTestForHighMemoryDevicesWithBFCacheDisabled,
1587 HighMemoryDevicesWithBFacheDisabled) {
Rakina Zata Amni454f5a902022-05-31 06:18:021588 // Ensure that IsBackForwardCacheEnabled() returns false, because the
1589 // BackForwardCache feature is disabled.
1590 EXPECT_FALSE(IsBackForwardCacheEnabled());
1591
Rakina Zata Amni454f5a902022-05-31 06:18:021592 ASSERT_TRUE(embedded_test_server()->Start());
1593 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
1594 GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
1595
1596 // 1) Navigate to A.
1597 EXPECT_TRUE(NavigateToURL(shell(), url_a));
1598 RenderFrameHostImpl* rfh_a = current_frame_host();
1599 RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
1600
1601 // 2) Navigate to B.
1602 EXPECT_TRUE(NavigateToURL(shell(), url_b));
1603
1604 // 3) A shouldn't be stored in back-forward cache because the BackForwardCache
1605 // feature is disabled.
1606 delete_observer_rfh_a.WaitUntilDeleted();
1607
1608 // 4) Go back to check that only kBackForwardCacheDisabled is recorded.
1609 ASSERT_TRUE(HistoryGoBack(web_contents()));
1610
1611 ExpectNotRestored(
1612 {
1613 BackForwardCacheMetrics::NotRestoredReason::kBackForwardCacheDisabled,
1614 },
1615 {}, {}, {}, {}, FROM_HERE);
Rakina Zata Amni454f5a902022-05-31 06:18:021616}
1617
Fergal Daly7991d9362019-12-20 02:28:251618// Start an inifite dialogs in JS, yielding after each. The first dialog should
1619// be dismissed by navigation. The later dialogs should be handled gracefully
1620// and not appear while in BFCache. Finally, when the page comes out of BFCache,
1621// dialogs should appear again.
1622IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
1623 CanUseCacheWhenPageAlertsInTimeoutLoop) {
1624 ASSERT_TRUE(embedded_test_server()->Start());
1625
1626 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
1627 GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
1628
1629 // Navigate to A.
1630 EXPECT_TRUE(NavigateToURL(shell(), url_a));
1631 RenderFrameHostImpl* rfh_a = current_frame_host();
1632 RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
1633
Marko Ivanovich7e403fc2020-10-09 05:56:471634 AppModalDialogWaiter dialog_waiter(shell());
Fergal Daly7991d9362019-12-20 02:28:251635
1636 EXPECT_TRUE(ExecJs(rfh_a, R"(
1637 function alertLoop() {
1638 setTimeout(alertLoop, 0);
1639 window.alert("alert");
1640 }
1641 // Don't block this script.
1642 setTimeout(alertLoop, 0);
1643 )"));
1644
Marko Ivanovich7e403fc2020-10-09 05:56:471645 dialog_waiter.Wait();
Fergal Daly7991d9362019-12-20 02:28:251646
1647 // Navigate to B.
1648 ASSERT_TRUE(NavigateToURL(shell(), url_b));
1649 RenderFrameHostImpl* rfh_b = current_frame_host();
1650
1651 ASSERT_FALSE(delete_observer_rfh_a.deleted());
1652 ASSERT_THAT(rfh_a, InBackForwardCache());
1653 ASSERT_NE(rfh_a, rfh_b);
1654
Marko Ivanovich7e403fc2020-10-09 05:56:471655 dialog_waiter.Restart();
Fergal Daly7991d9362019-12-20 02:28:251656
1657 // Go back.
Fergal Daly5495b0e2021-11-19 02:03:281658 ASSERT_TRUE(HistoryGoBack(web_contents()));
Fergal Daly7991d9362019-12-20 02:28:251659 EXPECT_EQ(rfh_a, current_frame_host());
Hajime Hoshibbc509c2020-04-06 08:55:081660 EXPECT_FALSE(rfh_a->IsInBackForwardCache());
Fergal Daly7991d9362019-12-20 02:28:251661
1662 // The page should still be requesting dialogs in a loop. Wait for one to be
1663 // requested.
Marko Ivanovich7e403fc2020-10-09 05:56:471664 dialog_waiter.Wait();
Fergal Daly7991d9362019-12-20 02:28:251665}
1666
Nasko Oskov0f3cbb12020-01-07 17:52:141667// UnloadOldFrame will clear all dialogs. We test that further requests for
Fergal Daly7991d9362019-12-20 02:28:251668// dialogs coming from JS do not result in the creation of a dialog. This test
1669// posts some dialog creation JS to the render from inside the
1670// CommitNavigationCallback task. This JS is then able to post a task back to
1671// the renders to show a dialog. By the time this task runs, we the
1672// RenderFrameHostImpl's is_active() should be false.
1673//
1674// This test is not perfect, it can pass simply because the renderer thread does
1675// not run the JS in time. Ideally it would block until the renderer posts the
1676// request for a dialog but it's possible to do that without creating a nested
1677// message loop and if we do that, we risk processing the dialog request.
1678IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
1679 DialogsCancelledAndSuppressedWhenCached) {
1680 ASSERT_TRUE(embedded_test_server()->Start());
1681
1682 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
1683 GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
1684
1685 // Navigate to A.
1686 EXPECT_TRUE(NavigateToURL(shell(), url_a));
1687 RenderFrameHostImpl* rfh_a = current_frame_host();
1688 RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
1689
1690 // Let's us know whether the following callback ran. Not strictly necessary
1691 // since it really should run.
1692 bool posted_dialog_js = false;
1693 // Create a callback that will be called during the DidCommitNavigation task.
1694 WillEnterBackForwardCacheCallbackForTesting
1695 will_enter_back_forward_cache_callback =
1696 base::BindLambdaForTesting([&]() {
1697 // Post a dialog, it should not result in a dialog being created.
1698 ExecuteScriptAsync(rfh_a, R"(window.alert("alert");)");
1699 posted_dialog_js = true;
1700 });
1701 rfh_a->render_view_host()->SetWillEnterBackForwardCacheCallbackForTesting(
1702 will_enter_back_forward_cache_callback);
1703
Marko Ivanovich7e403fc2020-10-09 05:56:471704 AppModalDialogWaiter dialog_waiter(shell());
Fergal Daly7991d9362019-12-20 02:28:251705
1706 // Try show another dialog. It should work.
1707 ExecuteScriptAsync(rfh_a, R"(window.alert("alert");)");
Marko Ivanovich7e403fc2020-10-09 05:56:471708 dialog_waiter.Wait();
Fergal Daly7991d9362019-12-20 02:28:251709
Marko Ivanovich7e403fc2020-10-09 05:56:471710 dialog_waiter.Restart();
Fergal Daly7991d9362019-12-20 02:28:251711
1712 // Navigate to B.
1713 ASSERT_TRUE(NavigateToURL(shell(), url_b));
1714 RenderFrameHostImpl* rfh_b = current_frame_host();
1715
1716 ASSERT_FALSE(delete_observer_rfh_a.deleted());
1717 ASSERT_THAT(rfh_a, InBackForwardCache());
1718 ASSERT_NE(rfh_a, rfh_b);
1719 // Test that the JS was run and that it didn't result in a dialog.
1720 ASSERT_TRUE(posted_dialog_js);
Marko Ivanovich7e403fc2020-10-09 05:56:471721 ASSERT_FALSE(dialog_waiter.WasDialogRequestedCallbackCalled());
Fergal Daly7991d9362019-12-20 02:28:251722
1723 // Go back.
Fergal Daly5495b0e2021-11-19 02:03:281724 ASSERT_TRUE(HistoryGoBack(web_contents()));
Fergal Daly7991d9362019-12-20 02:28:251725
1726 EXPECT_EQ(rfh_a, current_frame_host());
Hajime Hoshibbc509c2020-04-06 08:55:081727 EXPECT_FALSE(rfh_a->IsInBackForwardCache());
Fergal Daly7991d9362019-12-20 02:28:251728
1729 // Try show another dialog. It should work.
1730 ExecuteScriptAsync(rfh_a, R"(window.alert("alert");)");
Marko Ivanovich7e403fc2020-10-09 05:56:471731 dialog_waiter.Wait();
Fergal Daly7991d9362019-12-20 02:28:251732}
1733
Rakina Zata Amni60858132020-08-19 10:33:481734// Tests that pagehide handlers of the old RFH are run for bfcached pages even
1735// if the page is already hidden (and visibilitychange won't run).
David Bertoni3aec0aaf2024-06-05 23:09:251736// Disabled on Linux and Win because of flakiness, see crbug.com/40165901.
David Bertoni3aec0aaf2024-06-05 23:09:251737#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
1738#define MAYBE_PagehideRunsWhenPageIsHidden DISABLED_PagehideRunsWhenPageIsHidden
1739#else
1740#define MAYBE_PagehideRunsWhenPageIsHidden PagehideRunsWhenPageIsHidden
1741#endif
Rakina Zata Amni60858132020-08-19 10:33:481742IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
David Bertoni3aec0aaf2024-06-05 23:09:251743 MAYBE_PagehideRunsWhenPageIsHidden) {
Rakina Zata Amni60858132020-08-19 10:33:481744 ASSERT_TRUE(embedded_test_server()->Start());
1745 GURL url_1(embedded_test_server()->GetURL("a.com", "/title1.html"));
1746 GURL url_2(embedded_test_server()->GetURL("b.com", "/title2.html"));
1747 GURL url_3(embedded_test_server()->GetURL("a.com", "/title2.html"));
1748 WebContentsImpl* web_contents =
1749 static_cast<WebContentsImpl*>(shell()->web_contents());
1750
1751 // 1) Navigate to |url_1| and hide the tab.
1752 EXPECT_TRUE(NavigateToURL(shell(), url_1));
Yuzu Saijoda4f49f2022-10-17 06:57:011753 RenderFrameHostImplWrapper main_frame_1(web_contents->GetPrimaryMainFrame());
Rakina Zata Amni60858132020-08-19 10:33:481754 // We need to set it to Visibility::VISIBLE first in case this is the first
1755 // time the visibility is updated.
1756 web_contents->UpdateWebContentsVisibility(Visibility::VISIBLE);
1757 web_contents->UpdateWebContentsVisibility(Visibility::HIDDEN);
1758 EXPECT_EQ(Visibility::HIDDEN, web_contents->GetVisibility());
1759
1760 // Create a pagehide handler that sets item "pagehide_storage" and a
1761 // visibilitychange handler that sets item "visibilitychange_storage" in
1762 // localStorage.
Yuzu Saijoda4f49f2022-10-17 06:57:011763 EXPECT_TRUE(ExecJs(main_frame_1.get(),
Rakina Zata Amni60858132020-08-19 10:33:481764 R"(
1765 localStorage.setItem('pagehide_storage', 'not_dispatched');
1766 var dispatched_pagehide = false;
1767 window.onpagehide = function(e) {
1768 if (dispatched_pagehide) {
1769 // We shouldn't dispatch pagehide more than once.
1770 localStorage.setItem('pagehide_storage', 'dispatched_more_than_once');
1771 } else if (!e.persisted) {
1772 localStorage.setItem('pagehide_storage', 'wrong_persisted');
1773 } else {
1774 localStorage.setItem('pagehide_storage', 'dispatched_once');
1775 }
1776 dispatched_pagehide = true;
1777 }
1778 localStorage.setItem('visibilitychange_storage', 'not_dispatched');
1779 document.onvisibilitychange = function(e) {
1780 localStorage.setItem('visibilitychange_storage',
1781 'should_not_be_dispatched');
1782 }
1783 )"));
1784 // |visibilitychange_storage| should be set to its initial correct value.
Fergal Daly2c7bc4052021-12-23 14:42:221785 EXPECT_EQ("not_dispatched",
Yuzu Saijoda4f49f2022-10-17 06:57:011786 GetLocalStorage(main_frame_1.get(), "visibilitychange_storage"));
Rakina Zata Amni60858132020-08-19 10:33:481787
1788 // 2) Navigate cross-site to |url_2|. We need to navigate cross-site to make
1789 // sure we won't run pagehide and visibilitychange during new page's commit,
1790 // which is tested in ProactivelySwapBrowsingInstancesSameSiteTest.
1791 EXPECT_TRUE(NavigateToURL(shell(), url_2));
1792
1793 // |main_frame_1| should be in the back-forward cache.
1794 EXPECT_TRUE(main_frame_1->IsInBackForwardCache());
1795
1796 // 3) Navigate to |url_3| which is same-origin with |url_1|, so we can check
1797 // the localStorage values.
1798 EXPECT_TRUE(NavigateToURL(shell(), url_3));
Dave Tapuska327c06c92022-06-13 20:31:511799 RenderFrameHostImpl* main_frame_3 = web_contents->GetPrimaryMainFrame();
Rakina Zata Amni60858132020-08-19 10:33:481800
1801 // Check that the value for 'pagehide_storage' and 'visibilitychange_storage'
1802 // are set correctly.
Fergal Daly91e62882023-10-30 08:26:421803 EXPECT_TRUE(
1804 WaitForLocalStorage(main_frame_3, "pagehide_storage", "dispatched_once"));
1805 EXPECT_TRUE(WaitForLocalStorage(main_frame_3, "visibilitychange_storage",
1806 "not_dispatched"));
Rakina Zata Amni60858132020-08-19 10:33:481807}
1808
Rakina Zata Amniefcc2932020-08-28 07:24:371809// Tests that we're getting the correct TextInputState and focus updates when a
1810// page enters the back-forward cache and when it gets restored.
Ivana Žužićd514c142024-02-09 16:12:221811// TODO(b/324570785): Re-enable the test for Android.
Yoshisato Yanagisawa47effef2025-05-19 06:18:311812#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_MAC)
Ivana Žužićd514c142024-02-09 16:12:221813#define MAYBE_TextInputStateUpdated DISABLED_TextInputStateUpdated
1814#else
1815#define MAYBE_TextInputStateUpdated TextInputStateUpdated
1816#endif
1817IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
1818 MAYBE_TextInputStateUpdated) {
Rakina Zata Amniefcc2932020-08-28 07:24:371819 ASSERT_TRUE(embedded_test_server()->Start());
1820 GURL url_1(embedded_test_server()->GetURL("a.com", "/title1.html"));
1821 GURL url_2(embedded_test_server()->GetURL("b.com", "/title2.html"));
1822
1823 // 1) Navigate to |url_1| and add a text input with "foo" as the value.
1824 EXPECT_TRUE(NavigateToURL(shell(), url_1));
Mustaq Ahmed17d4e672025-05-16 22:27:341825 SimulateEndOfPaintHoldingOnPrimaryMainFrame(web_contents());
Rakina Zata Amniefcc2932020-08-28 07:24:371826 RenderFrameHostImpl* rfh_1 = current_frame_host();
1827 EXPECT_TRUE(ExecJs(rfh_1,
1828 "document.title='bfcached';"
1829 "var input = document.createElement('input');"
1830 "input.setAttribute('type', 'text');"
1831 "input.setAttribute('value', 'foo');"
1832 "document.body.appendChild(input);"
1833 "var focusCount = 0;"
1834 "var blurCount = 0;"
1835 "input.onfocus = () => { focusCount++;};"
1836 "input.onblur = () => { blurCount++; };"));
1837
1838 {
1839 TextInputManagerTypeObserver type_observer(web_contents(),
1840 ui::TEXT_INPUT_TYPE_TEXT);
1841 TextInputManagerValueObserver value_observer(web_contents(), "foo");
1842 // 2) Press tab key to focus the <input>, and verify the type & value.
1843 SimulateKeyPress(web_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
1844 ui::VKEY_TAB, false, false, false, false);
1845 type_observer.Wait();
1846 value_observer.Wait();
1847
1848 EXPECT_EQ(rfh_1, web_contents()->GetFocusedFrame());
1849 EXPECT_EQ(EvalJs(rfh_1, "focusCount").ExtractInt(), 1);
1850 EXPECT_EQ(EvalJs(rfh_1, "blurCount").ExtractInt(), 0);
1851 }
1852
1853 {
1854 TextInputManagerTester tester(web_contents());
1855 TextInputManagerValueObserver value_observer(web_contents(), "A");
1856 // 3) Press the "A" key to change the text input value. This should notify
1857 // the browser that the text input value has changed.
1858 SimulateKeyPress(web_contents(), ui::DomKey::FromCharacter('A'),
1859 ui::DomCode::US_A, ui::VKEY_A, false, false, false, false);
1860 value_observer.Wait();
1861
1862 EXPECT_EQ(rfh_1, web_contents()->GetFocusedFrame());
1863 EXPECT_EQ(EvalJs(rfh_1, "focusCount").ExtractInt(), 1);
1864 EXPECT_EQ(EvalJs(rfh_1, "blurCount").ExtractInt(), 0);
1865 }
1866
1867 {
1868 TextInputManagerTypeObserver type_observer(web_contents(),
1869 ui::TEXT_INPUT_TYPE_NONE);
1870 // 4) Navigating to |url_2| should reset type to TEXT_INPUT_TYPE_NONE.
1871 EXPECT_TRUE(NavigateToURL(shell(), url_2));
1872 type_observer.Wait();
1873 // |rfh_1| should get into the back-forward cache.
1874 EXPECT_TRUE(rfh_1->IsInBackForwardCache());
1875 EXPECT_EQ(current_frame_host(), web_contents()->GetFocusedFrame());
1876 EXPECT_NE(rfh_1, web_contents()->GetFocusedFrame());
1877 }
1878
1879 {
1880 // 5) Navigating back to |url_1|, we shouldn't restore the focus to the
1881 // text input, but |rfh_1| will be focused again as we will restore focus
1882 // to main frame after navigation.
Fergal Daly5495b0e2021-11-19 02:03:281883 ASSERT_TRUE(HistoryGoBack(web_contents()));
Rakina Zata Amniefcc2932020-08-28 07:24:371884
1885 EXPECT_EQ(rfh_1, web_contents()->GetFocusedFrame());
1886 EXPECT_EQ(EvalJs(rfh_1, "focusCount").ExtractInt(), 1);
1887 EXPECT_EQ(EvalJs(rfh_1, "blurCount").ExtractInt(), 1);
1888 }
1889
1890 {
1891 TextInputManagerTypeObserver type_observer(web_contents(),
1892 ui::TEXT_INPUT_TYPE_TEXT);
1893 TextInputManagerValueObserver value_observer(web_contents(), "A");
1894 // 6) Press tab key to focus the <input> again. Note that we need to press
1895 // the tab key twice here, because the last "tab focus" point was the
1896 // <input> element. The first tab key press would focus on the UI/url bar,
1897 // then the second tab key would go back to the <input>.
1898 SimulateKeyPress(web_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
1899 ui::VKEY_TAB, false, false, false, false);
1900 SimulateKeyPress(web_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
1901 ui::VKEY_TAB, false, false, false, false);
1902 type_observer.Wait();
1903 value_observer.Wait();
1904
1905 EXPECT_EQ(rfh_1, web_contents()->GetFocusedFrame());
1906 EXPECT_EQ(EvalJs(rfh_1, "focusCount").ExtractInt(), 2);
1907 EXPECT_EQ(EvalJs(rfh_1, "blurCount").ExtractInt(), 1);
1908 }
1909}
1910
Rakina Zata Amni01721acd2023-12-05 19:38:311911#if (BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID))
Rakina Zata Amnibebca3e2022-10-12 07:22:421912#define MAYBE_SubframeTextInputStateUpdated DISABLED_SubframeTextInputStateUpdated
1913#else
1914#define MAYBE_SubframeTextInputStateUpdated SubframeTextInputStateUpdated
1915#endif
Rakina Zata Amniefcc2932020-08-28 07:24:371916IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
Rakina Zata Amnibebca3e2022-10-12 07:22:421917 MAYBE_SubframeTextInputStateUpdated) {
Rakina Zata Amniefcc2932020-08-28 07:24:371918 ASSERT_TRUE(embedded_test_server()->Start());
1919 GURL url_1(embedded_test_server()->GetURL(
1920 "a.com", "/cross_site_iframe_factory.html?a(b(a))"));
1921 GURL url_2(embedded_test_server()->GetURL("b.com", "/title2.html"));
1922
1923 // 1) Navigate to |url_1| and add a text input with "foo" as the value in the
1924 // a.com subframe.
1925 EXPECT_TRUE(NavigateToURL(shell(), url_1));
Mustaq Ahmed17d4e672025-05-16 22:27:341926 SimulateEndOfPaintHoldingOnPrimaryMainFrame(web_contents());
Rakina Zata Amniefcc2932020-08-28 07:24:371927 RenderFrameHostImpl* rfh_a = current_frame_host();
1928 RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
1929 RenderFrameHostImpl* rfh_subframe_a =
1930 rfh_b->child_at(0)->current_frame_host();
1931 EXPECT_TRUE(ExecJs(rfh_subframe_a,
1932 "var input = document.createElement('input');"
1933 "input.setAttribute('type', 'text');"
1934 "input.setAttribute('value', 'foo');"
1935 "document.body.appendChild(input);"
1936 "var focusCount = 0;"
1937 "var blurCount = 0;"
1938 "input.onfocus = () => { focusCount++;};"
1939 "input.onblur = () => { blurCount++; };"));
1940
1941 {
1942 TextInputManagerTypeObserver type_observer(web_contents(),
1943 ui::TEXT_INPUT_TYPE_TEXT);
1944 TextInputManagerValueObserver value_observer(web_contents(), "foo");
1945 // 2) Press tab key to focus the <input>, and verify the type & value.
1946 SimulateKeyPress(web_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
1947 ui::VKEY_TAB, false, false, false, false);
1948 type_observer.Wait();
1949 value_observer.Wait();
1950
1951 EXPECT_EQ(rfh_subframe_a, web_contents()->GetFocusedFrame());
1952 EXPECT_EQ(EvalJs(rfh_subframe_a, "focusCount").ExtractInt(), 1);
1953 EXPECT_EQ(EvalJs(rfh_subframe_a, "blurCount").ExtractInt(), 0);
1954 }
1955
1956 {
1957 TextInputManagerTester tester(web_contents());
1958 TextInputManagerValueObserver value_observer(web_contents(), "A");
1959 // 3) Press the "A" key to change the text input value. This should notify
1960 // the browser that the text input value has changed.
1961 SimulateKeyPress(web_contents(), ui::DomKey::FromCharacter('A'),
1962 ui::DomCode::US_A, ui::VKEY_A, false, false, false, false);
1963 value_observer.Wait();
1964
1965 EXPECT_EQ(rfh_subframe_a, web_contents()->GetFocusedFrame());
1966 EXPECT_EQ(EvalJs(rfh_subframe_a, "focusCount").ExtractInt(), 1);
1967 EXPECT_EQ(EvalJs(rfh_subframe_a, "blurCount").ExtractInt(), 0);
1968 }
1969
1970 {
1971 TextInputManagerTypeObserver type_observer(web_contents(),
1972 ui::TEXT_INPUT_TYPE_NONE);
1973 // 4) Navigating to |url_2| should reset type to TEXT_INPUT_TYPE_NONE and
1974 // changed focus to the new page's main frame.
1975 EXPECT_TRUE(NavigateToURL(shell(), url_2));
1976 type_observer.Wait();
1977
1978 // |rfh_a| and its subframes should get into the back-forward cache.
1979 EXPECT_TRUE(rfh_a->IsInBackForwardCache());
1980 EXPECT_TRUE(rfh_b->IsInBackForwardCache());
1981 EXPECT_TRUE(rfh_subframe_a->IsInBackForwardCache());
Rakina Zata Amni53f91fb2022-10-10 18:58:481982 EXPECT_NE(rfh_subframe_a, web_contents()->GetFocusedFrame());
Rakina Zata Amniefcc2932020-08-28 07:24:371983 }
1984
1985 {
1986 // 5) Navigating back to |url_1|, we shouldn't restore the focus to the
1987 // text input in the subframe (we will focus on the main frame |rfh_a|
1988 // instead).
Fergal Daly5495b0e2021-11-19 02:03:281989 ASSERT_TRUE(HistoryGoBack(web_contents()));
Rakina Zata Amniefcc2932020-08-28 07:24:371990
1991 EXPECT_EQ(rfh_a, web_contents()->GetFocusedFrame());
1992 EXPECT_EQ(EvalJs(rfh_subframe_a, "focusCount").ExtractInt(), 1);
1993 EXPECT_EQ(EvalJs(rfh_subframe_a, "blurCount").ExtractInt(), 1);
1994 }
1995
1996 {
1997 TextInputManagerTypeObserver type_observer(web_contents(),
1998 ui::TEXT_INPUT_TYPE_TEXT);
1999 TextInputManagerValueObserver value_observer(web_contents(), "A");
2000 // 6) Press tab key to focus the <input> again.
2001 SimulateKeyPress(web_contents(), ui::DomKey::TAB, ui::DomCode::TAB,
2002 ui::VKEY_TAB, false, false, false, false);
2003 type_observer.Wait();
2004 value_observer.Wait();
2005
2006 EXPECT_EQ(rfh_subframe_a, web_contents()->GetFocusedFrame());
2007 EXPECT_EQ(EvalJs(rfh_subframe_a, "focusCount").ExtractInt(), 2);
2008 EXPECT_EQ(EvalJs(rfh_subframe_a, "blurCount").ExtractInt(), 1);
2009 }
2010}
2011
Rakina Zata Amnicca4889e2021-09-28 23:25:552012// Tests that trying to focus on a BFCached cross-site iframe won't crash.
2013// See https://crbug.com/1250218.
Alison Gale81f4f2c72024-04-22 19:33:312014// TODO(crbug.com/40856039): Flaky on linux tsan
Matt Wolenetz86173c4d72022-08-03 20:38:352015#if BUILDFLAG(IS_LINUX) && defined(THREAD_SANITIZER)
2016#define MAYBE_FocusSameSiteSubframeOnPagehide \
2017 DISABLED_FocusSameSiteSubframeOnPagehide
2018#else
2019#define MAYBE_FocusSameSiteSubframeOnPagehide FocusSameSiteSubframeOnPagehide
2020#endif
Rakina Zata Amnicca4889e2021-09-28 23:25:552021IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
Matt Wolenetz86173c4d72022-08-03 20:38:352022 MAYBE_FocusSameSiteSubframeOnPagehide) {
Rakina Zata Amnicca4889e2021-09-28 23:25:552023 ASSERT_TRUE(embedded_test_server()->Start());
2024 GURL main_url(
2025 embedded_test_server()->GetURL("a.com", "/page_with_iframe.html"));
2026 GURL main_url_2(embedded_test_server()->GetURL("b.com", "/title2.html"));
2027
2028 // 1) Navigate to a page with a same-site iframe.
2029 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2030 RenderFrameHostImplWrapper rfh_1(current_frame_host());
2031 EXPECT_EQ(rfh_1.get(), web_contents()->GetFocusedFrame());
2032
2033 // 2) Navigate away from the page while trying to focus the subframe on
2034 // pagehide. The DidFocusFrame IPC should arrive after the page gets into
2035 // BFCache and should be ignored by the browser. The focus after navigation
2036 // should go to the new main frame.
2037 EXPECT_TRUE(ExecJs(rfh_1.get(), R"(
2038 window.onpagehide = function(e) {
2039 document.getElementById("test_iframe").focus();
2040 })"));
2041 EXPECT_TRUE(NavigateToURL(shell(), main_url_2));
2042 EXPECT_TRUE(rfh_1->IsInBackForwardCache());
2043 EXPECT_NE(rfh_1.get(), web_contents()->GetFocusedFrame());
2044 EXPECT_EQ(current_frame_host(), web_contents()->GetFocusedFrame());
2045
2046 // 3) Navigate back to the page. The focus should be on the main frame.
Fergal Daly5495b0e2021-11-19 02:03:282047 ASSERT_TRUE(HistoryGoBack(web_contents()));
Rakina Zata Amnicca4889e2021-09-28 23:25:552048 EXPECT_EQ(rfh_1.get(), web_contents()->GetFocusedFrame());
2049 ExpectRestored(FROM_HERE);
2050}
2051
2052// Tests that trying to focus on a BFCached cross-site iframe won't crash.
2053// See https://crbug.com/1250218.
2054IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
2055 FocusCrossSiteSubframeOnPagehide) {
2056 ASSERT_TRUE(embedded_test_server()->Start());
2057 GURL main_url(embedded_test_server()->GetURL(
2058 "a.com", "/cross_site_iframe_factory.html?a(b)"));
2059 GURL main_url_2(embedded_test_server()->GetURL("b.com", "/title2.html"));
2060
2061 // 1) Navigate to a page with a cross-site iframe.
2062 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2063 RenderFrameHostImplWrapper rfh_1(current_frame_host());
2064 EXPECT_EQ(rfh_1.get(), web_contents()->GetFocusedFrame());
2065
2066 // 2) Navigate away from the page while trying to focus the subframe on
2067 // pagehide. The DidFocusFrame IPC should arrive after the page gets into
2068 // BFCache and should be ignored by the browser. The focus after navigation
2069 // should go to the new main frame.
2070 EXPECT_TRUE(ExecJs(rfh_1.get(), R"(
2071 window.onpagehide = function(e) {
2072 document.getElementById("child-0").focus();
2073 })"));
2074 EXPECT_TRUE(NavigateToURL(shell(), main_url_2));
2075 EXPECT_TRUE(rfh_1->IsInBackForwardCache());
2076 EXPECT_NE(rfh_1.get(), web_contents()->GetFocusedFrame());
2077
2078 // 3) Navigate back to the page. The focus should be on the original page's
2079 // main frame.
Fergal Daly5495b0e2021-11-19 02:03:282080 ASSERT_TRUE(HistoryGoBack(web_contents()));
Rakina Zata Amnicca4889e2021-09-28 23:25:552081 EXPECT_EQ(rfh_1.get(), current_frame_host());
2082 ExpectRestored(FROM_HERE);
2083}
2084
Alexander Timin45b716c2020-11-06 01:40:312085IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
2086 MainDocumentCSPHeadersAreRestored) {
2087 ASSERT_TRUE(embedded_test_server()->Start());
2088
2089 GURL url_a(embedded_test_server()->GetURL(
2090 "a.com",
2091 "/set-header?"
2092 "Content-Security-Policy: frame-src 'none'"));
2093 GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
2094
2095 // 1) Navigate to A, which should set CSP.
2096 EXPECT_TRUE(NavigateToURL(shell(), url_a));
2097 RenderFrameHostImpl* rfh_a = current_frame_host();
2098
2099 // Check that CSP was set.
2100 {
Antonio Sartori8f5b7ee2021-01-29 13:00:542101 const std::vector<network::mojom::ContentSecurityPolicyPtr>& root_csp =
2102 current_frame_host()
Antonio Sartorif45b2fc2021-03-04 10:15:072103 ->policy_container_host()
2104 ->policies()
2105 .content_security_policies;
Alexander Timin45b716c2020-11-06 01:40:312106 EXPECT_EQ(1u, root_csp.size());
Antonio Sartori8f5b7ee2021-01-29 13:00:542107 EXPECT_EQ("frame-src 'none'", root_csp[0]->header->header_value);
Alexander Timin45b716c2020-11-06 01:40:312108 }
2109
2110 // 2) Navigate to B.
2111 EXPECT_TRUE(NavigateToURL(shell(), url_b));
2112
2113 // 3) Navigate back and expect that the CSP headers are present on the main
2114 // frame.
Fergal Daly5495b0e2021-11-19 02:03:282115 ASSERT_TRUE(HistoryGoBack(web_contents()));
Alexander Timin45b716c2020-11-06 01:40:312116 EXPECT_EQ(rfh_a, current_frame_host());
Fergal Daly09833062021-02-09 07:10:002117 ExpectRestored(FROM_HERE);
Alexander Timin45b716c2020-11-06 01:40:312118
2119 // Check that CSP was restored.
2120 {
Antonio Sartori8f5b7ee2021-01-29 13:00:542121 const std::vector<network::mojom::ContentSecurityPolicyPtr>& root_csp =
2122 current_frame_host()
Antonio Sartorif45b2fc2021-03-04 10:15:072123 ->policy_container_host()
2124 ->policies()
2125 .content_security_policies;
Alexander Timin45b716c2020-11-06 01:40:312126 EXPECT_EQ(1u, root_csp.size());
Antonio Sartori8f5b7ee2021-01-29 13:00:542127 EXPECT_EQ("frame-src 'none'", root_csp[0]->header->header_value);
Alexander Timin45b716c2020-11-06 01:40:312128 }
2129}
2130
arthursonzogni76098e52020-11-25 14:18:452131// Check that sandboxed documents are cached and won't lose their sandbox flags
2132// after restoration.
2133IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, CspSandbox) {
Alexander Timin45b716c2020-11-06 01:40:312134 ASSERT_TRUE(embedded_test_server()->Start());
2135
2136 GURL url_a(
2137 embedded_test_server()->GetURL("a.com",
2138 "/set-header?"
2139 "Content-Security-Policy: sandbox"));
2140 GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
2141
2142 // 1) Navigate to A, which should set CSP.
2143 EXPECT_TRUE(NavigateToURL(shell(), url_a));
arthursonzogni76098e52020-11-25 14:18:452144 RenderFrameHostImpl* rfh_a = current_frame_host();
2145 RenderFrameDeletedObserver delete_observer_rfh_a(rfh_a);
Alexander Timin45b716c2020-11-06 01:40:312146 {
Antonio Sartori8f5b7ee2021-01-29 13:00:542147 const std::vector<network::mojom::ContentSecurityPolicyPtr>& root_csp =
2148 current_frame_host()
Antonio Sartorif45b2fc2021-03-04 10:15:072149 ->policy_container_host()
2150 ->policies()
2151 .content_security_policies;
arthursonzogni76098e52020-11-25 14:18:452152 ASSERT_EQ(1u, root_csp.size());
Antonio Sartori8f5b7ee2021-01-29 13:00:542153 ASSERT_EQ("sandbox", root_csp[0]->header->header_value);
arthursonzogni76098e52020-11-25 14:18:452154 ASSERT_EQ(network::mojom::WebSandboxFlags::kAll,
2155 current_frame_host()->active_sandbox_flags());
Lei Zhang05c13df2024-07-10 20:59:122156 }
Alexander Timin45b716c2020-11-06 01:40:312157
arthursonzogni76098e52020-11-25 14:18:452158 // 2) Navigate to B. Expect the previous RenderFrameHost to enter the bfcache.
Alexander Timin45b716c2020-11-06 01:40:312159 EXPECT_TRUE(NavigateToURL(shell(), url_b));
arthursonzogni76098e52020-11-25 14:18:452160 EXPECT_FALSE(delete_observer_rfh_a.deleted());
2161 EXPECT_TRUE(rfh_a->IsInBackForwardCache());
2162 {
Antonio Sartori8f5b7ee2021-01-29 13:00:542163 const std::vector<network::mojom::ContentSecurityPolicyPtr>& root_csp =
2164 current_frame_host()
Antonio Sartorif45b2fc2021-03-04 10:15:072165 ->policy_container_host()
2166 ->policies()
2167 .content_security_policies;
arthursonzogni76098e52020-11-25 14:18:452168 ASSERT_EQ(0u, root_csp.size());
2169 ASSERT_EQ(network::mojom::WebSandboxFlags::kNone,
2170 current_frame_host()->active_sandbox_flags());
Lei Zhang05c13df2024-07-10 20:59:122171 }
Alexander Timin45b716c2020-11-06 01:40:312172
arthursonzogni76098e52020-11-25 14:18:452173 // 3) Navigate back and expect the page to be restored, with the correct
2174 // CSP and sandbox flags.
Fergal Daly5495b0e2021-11-19 02:03:282175 ASSERT_TRUE(HistoryGoBack(web_contents()));
arthursonzogni76098e52020-11-25 14:18:452176 EXPECT_FALSE(delete_observer_rfh_a.deleted());
2177 EXPECT_EQ(current_frame_host(), rfh_a);
2178 {
Antonio Sartori8f5b7ee2021-01-29 13:00:542179 const std::vector<network::mojom::ContentSecurityPolicyPtr>& root_csp =
2180 current_frame_host()
Antonio Sartorif45b2fc2021-03-04 10:15:072181 ->policy_container_host()
2182 ->policies()
2183 .content_security_policies;
arthursonzogni76098e52020-11-25 14:18:452184 ASSERT_EQ(1u, root_csp.size());
Antonio Sartori8f5b7ee2021-01-29 13:00:542185 ASSERT_EQ("sandbox", root_csp[0]->header->header_value);
arthursonzogni76098e52020-11-25 14:18:452186 ASSERT_EQ(network::mojom::WebSandboxFlags::kAll,
2187 current_frame_host()->active_sandbox_flags());
Lei Zhang05c13df2024-07-10 20:59:122188 }
Alexander Timin45b716c2020-11-06 01:40:312189}
2190
Hajime Hoshic7606502021-04-14 02:42:162191// Check that about:blank is not cached.
2192IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, AboutBlankWillNotBeCached) {
2193 ASSERT_TRUE(embedded_test_server()->Start());
2194
2195 // 1) Navigate to about:blank.
2196 GURL blank_url(url::kAboutBlankURL);
2197 EXPECT_TRUE(NavigateToURL(shell(), blank_url));
Rakina Zata Amni4cab0822023-10-26 12:28:242198 RenderFrameHostImplWrapper rfh_blank(current_frame_host());
Hajime Hoshic7606502021-04-14 02:42:162199
2200 // 2) Navigate to a.com.
2201 GURL url_a(embedded_test_server()->GetURL("a.com", "/empty.html"));
2202 EXPECT_TRUE(NavigateToURL(shell(), url_a));
2203
2204 // 3) Navigate back to about:blank.
Fergal Daly5495b0e2021-11-19 02:03:282205 ASSERT_TRUE(HistoryGoBack(web_contents()));
Hajime Hoshic7606502021-04-14 02:42:162206
2207 // This about:blank document does not have a SiteInstance and then loading a
2208 // page on it doesn't swap the browsing instance.
Rakina Zata Amni4cab0822023-10-26 12:28:242209
2210 if (ShouldCreateNewHostForAllFrames()) {
2211 EXPECT_TRUE(rfh_blank.WaitUntilRenderFrameDeleted());
2212 ExpectNotRestored(
2213 {
2214 BackForwardCacheMetrics::NotRestoredReason::kHTTPStatusNotOK,
2215 BackForwardCacheMetrics::NotRestoredReason::kSchemeNotHTTPOrHTTPS,
2216 BackForwardCacheMetrics::NotRestoredReason::
2217 kBrowsingInstanceNotSwapped,
Rakina Zata Amni4cab0822023-10-26 12:28:242218 },
2219 {}, {ShouldSwapBrowsingInstance::kNo_DoesNotHaveSite}, {}, {},
2220 FROM_HERE);
2221
2222 } else {
2223 EXPECT_FALSE(rfh_blank->IsInBackForwardCache());
2224 ExpectNotRestored(
2225 {
2226 BackForwardCacheMetrics::NotRestoredReason::
2227 kBrowsingInstanceNotSwapped,
2228 },
2229 {}, {ShouldSwapBrowsingInstance::kNo_DoesNotHaveSite}, {}, {},
2230 FROM_HERE);
2231 }
Hajime Hoshic7606502021-04-14 02:42:162232}
2233
Hajime Hoshic7606502021-04-14 02:42:162234// Check that browsing instances are not swapped when a navigation redirects
2235// toward the last committed URL and the reasons are recorded correctly.
2236IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, RedirectToSelf) {
2237 ASSERT_TRUE(embedded_test_server()->Start());
2238 NavigationControllerImpl& controller = web_contents()->GetController();
2239
2240 // 1) Navigate to a.com/empty.html.
2241 GURL url_a(embedded_test_server()->GetURL("a.com", "/empty.html"));
2242 EXPECT_TRUE(NavigateToURL(shell(), url_a));
Rakina Zata Amni4cab0822023-10-26 12:28:242243 RenderFrameHostImplWrapper rfh_a(current_frame_host());
Hajime Hoshic7606502021-04-14 02:42:162244 EXPECT_EQ(1, controller.GetEntryCount());
2245 EXPECT_EQ(url_a, controller.GetLastCommittedEntry()->GetURL());
2246
2247 // 2) Navigate to the same page by redirection.
2248 GURL url_a2(embedded_test_server()->GetURL(
2249 "a.com", "/server-redirect-301?" + url_a.spec()));
2250 EXPECT_TRUE(NavigateToURL(shell(), url_a2, url_a));
Rakina Zata Amni4cab0822023-10-26 12:28:242251 RenderFrameHostImplWrapper rfh_b(current_frame_host());
Hajime Hoshic7606502021-04-14 02:42:162252 EXPECT_EQ(2, controller.GetEntryCount());
2253
Rakina Zata Amni4cab0822023-10-26 12:28:242254 if (ShouldCreateNewHostForAllFrames()) {
2255 EXPECT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
2256 } else {
2257 EXPECT_FALSE(rfh_a->IsInBackForwardCache());
2258 EXPECT_TRUE(rfh_a->GetSiteInstance()->IsRelatedSiteInstance(
2259 rfh_b->GetSiteInstance()));
2260 }
2261
Hajime Hoshic7606502021-04-14 02:42:162262 EXPECT_EQ(url_a, controller.GetLastCommittedEntry()->GetURL());
2263
2264 // 3) Navigate back to the previous page.
Fergal Daly5495b0e2021-11-19 02:03:282265 ASSERT_TRUE(HistoryGoBack(web_contents()));
Hajime Hoshic7606502021-04-14 02:42:162266 EXPECT_EQ(2, controller.GetEntryCount());
2267 EXPECT_EQ(url_a, controller.GetLastCommittedEntry()->GetURL());
2268
Alison Gale81f4f2c72024-04-22 19:33:312269 // TODO(crbug.com/40760515): Investigate whether these navigation results are
Hajime Hoshic7606502021-04-14 02:42:162270 // expected.
Hajime Hoshic7606502021-04-14 02:42:162271 ExpectNotRestored(
2272 {
2273 BackForwardCacheMetrics::NotRestoredReason::
2274 kBrowsingInstanceNotSwapped,
2275 },
Fergal Daly1336ac642021-09-14 15:13:112276 {}, {ShouldSwapBrowsingInstance::kNo_SameUrlNavigation}, {}, {},
2277 FROM_HERE);
Hajime Hoshic7606502021-04-14 02:42:162278}
2279
Hajime Hoshic7606502021-04-14 02:42:162280// Check that reloading doesn't affect the back-forward cache usage.
2281IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, ReloadDoesntAffectCache) {
2282 ASSERT_TRUE(embedded_test_server()->Start());
2283 NavigationControllerImpl& controller = web_contents()->GetController();
2284
2285 // 1) Navigate to a.com.
2286 GURL url_a(embedded_test_server()->GetURL("a.com", "/empty.html"));
2287 EXPECT_TRUE(NavigateToURL(shell(), url_a));
2288 EXPECT_EQ(1, controller.GetEntryCount());
2289 EXPECT_EQ(url_a, controller.GetLastCommittedEntry()->GetURL());
2290
2291 // 2) Navigate to b.com.
2292 GURL url_b(embedded_test_server()->GetURL("b.com", "/empty.html"));
2293 EXPECT_TRUE(NavigateToURL(shell(), url_b));
2294 EXPECT_EQ(2, controller.GetEntryCount());
2295 EXPECT_EQ(url_b, controller.GetLastCommittedEntry()->GetURL());
2296
2297 // 3) Go back to a.com and reload.
Fergal Daly5495b0e2021-11-19 02:03:282298 ASSERT_TRUE(HistoryGoBack(web_contents()));
Hajime Hoshic7606502021-04-14 02:42:162299 EXPECT_EQ(2, controller.GetEntryCount());
2300 EXPECT_EQ(url_a, controller.GetLastCommittedEntry()->GetURL());
2301
2302 ExpectRestored(FROM_HERE);
2303
2304 // 4) Reload the tab.
2305 web_contents()->GetController().Reload(content::ReloadType::NORMAL, false);
2306 EXPECT_TRUE(WaitForLoadStop(web_contents()));
2307 EXPECT_EQ(2, controller.GetEntryCount());
2308 EXPECT_EQ(url_a, controller.GetLastCommittedEntry()->GetURL());
2309
2310 // By reloading the tab, ShouldSwapBrowsingInstance::
2311 // kNo_AlreadyHasMatchingBrowsingInstance is set once. This should be reset
2312 // when the navigation 4)'s commit finishes and should not prevent putting the
2313 // page into the back-forward cache.
2314 //
Hajime Hoshi07618442021-04-21 08:38:002315 // Note that SetBrowsingInstanceSwapResult might not be called for every
Hajime Hoshic7606502021-04-14 02:42:162316 // navigation because we might not get to this point for some navigations,
2317 // e.g. if the navigation uses a pre-existing RenderFrameHost and SiteInstance
2318 // for navigation.
2319 //
Alison Gale81f4f2c72024-04-22 19:33:312320 // TODO(crbug.com/40747698): Tie BrowsingInstanceSwapResult to
Hajime Hoshi07618442021-04-21 08:38:002321 // NavigationRequest instead and move the SetBrowsingInstanceSwapResult call
2322 // for navigations to happen at commit time instead.
Hajime Hoshic7606502021-04-14 02:42:162323
2324 // 5) Go forward to b.com and reload.
Fergal Daly5495b0e2021-11-19 02:03:282325 ASSERT_TRUE(HistoryGoForward(web_contents()));
Hajime Hoshic7606502021-04-14 02:42:162326 EXPECT_EQ(2, controller.GetEntryCount());
2327 EXPECT_EQ(url_b, controller.GetLastCommittedEntry()->GetURL());
2328
2329 // The page loaded at B) is correctly cached and restored. Reloading doesn't
2330 // affect the cache usage.
2331 ExpectRestored(FROM_HERE);
2332
2333 // 6) Go back to a.com.
Fergal Daly5495b0e2021-11-19 02:03:282334 ASSERT_TRUE(HistoryGoBack(web_contents()));
Hajime Hoshic7606502021-04-14 02:42:162335 EXPECT_EQ(2, controller.GetEntryCount());
2336 EXPECT_EQ(url_a, controller.GetLastCommittedEntry()->GetURL());
2337
2338 // The page loaded at 3) is correctly cached and restored. Reloading doesn't
2339 // affect the cache usage.
2340 ExpectRestored(FROM_HERE);
2341}
2342
Rakina Zata Amnia6558682021-03-05 04:10:572343// Regression test for crbug.com/1183313. Checks that CommitNavigationParam's
2344// |has_user_gesture| value reflects the gesture from the latest navigation
2345// after the commit finished.
2346IN_PROC_BROWSER_TEST_F(
2347 BackForwardCacheBrowserTest,
2348 SameDocumentNavAfterRestoringDocumentLoadedWithUserGesture) {
2349 ASSERT_TRUE(embedded_test_server()->Start());
2350
2351 GURL start_url(embedded_test_server()->GetURL("/title1.html"));
2352 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
2353 GURL url_a_foo(embedded_test_server()->GetURL("a.com", "/title1.html#foo"));
2354 GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
2355 NavigationControllerImpl& controller = web_contents()->GetController();
2356 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:552357 ->GetPrimaryFrameTree()
2358 .root();
Rakina Zata Amnia6558682021-03-05 04:10:572359
2360 // Initial navigation (so that we can initiate a navigation from renderer).
2361 EXPECT_TRUE(NavigateToURL(shell(), start_url));
2362
2363 // 1) Navigate to A with user gesture.
2364 {
2365 FrameNavigateParamsCapturer params_capturer(root);
2366 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), url_a));
2367 params_capturer.Wait();
2368 EXPECT_TRUE(params_capturer.has_user_gesture());
Rakina Zata Amniff10975d2021-10-08 06:04:242369 EXPECT_TRUE(root->current_frame_host()
Yao Xiaod553b702022-12-08 02:20:472370 ->last_committed_common_params_has_user_gesture());
Rakina Zata Amnia6558682021-03-05 04:10:572371 }
2372 RenderFrameHostImpl* rfh_a = current_frame_host();
2373
2374 // 2) Navigate to B. A should be stored in the back-forward cache.
2375 EXPECT_TRUE(NavigateToURL(shell(), url_b));
2376 EXPECT_TRUE(rfh_a->IsInBackForwardCache());
Rakina Zata Amniff10975d2021-10-08 06:04:242377 EXPECT_FALSE(root->current_frame_host()
Yao Xiaod553b702022-12-08 02:20:472378 ->last_committed_common_params_has_user_gesture());
Rakina Zata Amnia6558682021-03-05 04:10:572379
2380 // 3) GoBack to A. RenderFrameHost of A should be restored from the
2381 // back-forward cache, and "has_user_gesture" is set to false correctly.
2382 // Note that since this is a back-forward cache restore we create the
2383 // DidCommitProvisionalLoadParams completely in the browser, so we got the
2384 // correct value from the latest navigation. However, we did not update the
2385 // renderer's navigation-related values, so the renderer's DocumentLoader
2386 // still thinks the last "gesture" value is "true", which will get corrected
2387 // on the next navigation.
2388 {
2389 FrameNavigateParamsCapturer params_capturer(root);
2390 controller.GoBack();
2391 params_capturer.Wait();
2392 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
2393 EXPECT_EQ(rfh_a, current_frame_host());
2394 // The navigation doesn't have user gesture.
2395 EXPECT_FALSE(params_capturer.has_user_gesture());
Rakina Zata Amniff10975d2021-10-08 06:04:242396 EXPECT_FALSE(root->current_frame_host()
Yao Xiaod553b702022-12-08 02:20:472397 ->last_committed_common_params_has_user_gesture());
Rakina Zata Amnia6558682021-03-05 04:10:572398 }
2399
2400 // 4) Same-document navigation to A#foo without user gesture. At this point
2401 // we will update the renderer's DocumentLoader's latest gesture value to
2402 // "no user gesture", and we'll get the correct gesture value in
2403 // DidCommitProvisionalLoadParams.
2404 {
2405 FrameNavigateParamsCapturer params_capturer(root);
2406 EXPECT_TRUE(
2407 NavigateToURLFromRendererWithoutUserGesture(shell(), url_a_foo));
2408 params_capturer.Wait();
2409 // The navigation doesn't have user gesture.
2410 EXPECT_FALSE(params_capturer.has_user_gesture());
Rakina Zata Amniff10975d2021-10-08 06:04:242411 EXPECT_FALSE(root->current_frame_host()
Yao Xiaod553b702022-12-08 02:20:472412 ->last_committed_common_params_has_user_gesture());
Rakina Zata Amnia6558682021-03-05 04:10:572413 }
2414}
2415
Riho Isawab1efe68f2021-12-20 01:54:072416testing::Matcher<BackForwardCacheCanStoreTreeResult> MatchesTreeResult(
2417 testing::Matcher<bool> same_origin,
2418 GURL url) {
2419 return testing::AllOf(
2420 testing::Property("IsSameOrigin",
2421 &BackForwardCacheCanStoreTreeResult::IsSameOrigin,
2422 same_origin),
2423 testing::Property("GetUrl", &BackForwardCacheCanStoreTreeResult::GetUrl,
2424 url));
2425}
2426
2427RenderFrameHostImpl* ChildFrame(RenderFrameHostImpl* rfh, int child_index) {
2428 return rfh->child_at(child_index)->current_frame_host();
2429}
2430
2431// Verifies that the reasons match those given and no others.
Yuzu Saijoc3478072022-03-24 06:15:262432testing::Matcher<BackForwardCacheCanStoreDocumentResult>
2433BackForwardCacheBrowserTest::MatchesDocumentResult(
Yuzu Saijodcabe562022-04-25 06:06:392434 testing::Matcher<NotRestoredReasons> not_stored,
Riho Isawab1efe68f2021-12-20 01:54:072435 BlockListedFeatures block_listed) {
2436 return testing::AllOf(
2437 testing::Property(
Yuzu Saijodcabe562022-04-25 06:06:392438 "not_restored_reasons",
2439 &BackForwardCacheCanStoreDocumentResult::not_restored_reasons,
Riho Isawab1efe68f2021-12-20 01:54:072440 not_stored),
2441 testing::Property(
2442 "blocklisted_features",
2443 &BackForwardCacheCanStoreDocumentResult::blocklisted_features,
2444 block_listed),
2445 testing::Property(
2446 "disabled_reasons",
2447 &BackForwardCacheCanStoreDocumentResult::disabled_reasons,
Mingyu Lei8c1b9112023-01-18 03:59:012448 BackForwardCacheCanStoreDocumentResult::DisabledReasonsMap()),
Riho Isawab1efe68f2021-12-20 01:54:072449 testing::Property(
2450 "disallow_activation_reasons",
2451 &BackForwardCacheCanStoreDocumentResult::disallow_activation_reasons,
2452 std::set<uint64_t>()));
2453}
2454
2455// Check the contents of the BackForwardCacheCanStoreTreeResult of a page.
Yuzu Saijoc3478072022-03-24 06:15:262456IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, TreeResultFeatureUsage) {
Riho Isawab1efe68f2021-12-20 01:54:072457 ASSERT_TRUE(embedded_test_server()->Start());
2458 GURL url_a(embedded_test_server()->GetURL(
2459 "a.com", "/cross_site_iframe_factory.html?a(a, b, c)"));
Fergal Daly306fc3f2021-12-23 09:58:302460 GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
Riho Isawab1efe68f2021-12-20 01:54:072461
2462 // 1) Navigate to a(a, b, c).
Fergal Daly306fc3f2021-12-23 09:58:302463 ASSERT_TRUE(NavigateToURL(shell(), url_a));
2464 RenderFrameHostImplWrapper rfh(current_frame_host());
Riho Isawab1efe68f2021-12-20 01:54:072465
2466 // 2) Add a blocking feature to the main frame A and the sub frame B.
2467 current_frame_host()
2468 ->UseDummyStickyBackForwardCacheDisablingFeatureForTesting();
2469 current_frame_host()
2470 ->child_at(1)
2471 ->current_frame_host()
2472 ->UseDummyStickyBackForwardCacheDisablingFeatureForTesting();
2473
Fergal Daly306fc3f2021-12-23 09:58:302474 GURL url_subframe_a = ChildFrame(rfh.get(), 0)->GetLastCommittedURL();
2475 GURL url_subframe_b = ChildFrame(rfh.get(), 1)->GetLastCommittedURL();
2476 GURL url_subframe_c = ChildFrame(rfh.get(), 2)->GetLastCommittedURL();
2477
2478 // 3) Initialize the reasons tree and navigate away to ensure that everything
2479 // from the old frame has been destroyed.
Riho Isawab1efe68f2021-12-20 01:54:072480 BackForwardCacheCanStoreDocumentResultWithTree can_store_result =
Yuzu Saijoef104a02022-05-13 17:04:282481 web_contents()
2482 ->GetController()
2483 .GetBackForwardCache()
2484 .GetCurrentBackForwardCacheEligibility(rfh.get());
Fergal Daly306fc3f2021-12-23 09:58:302485 ASSERT_TRUE(NavigateToURL(shell(), url_b));
2486 ASSERT_TRUE(rfh.WaitUntilRenderFrameDeleted());
Riho Isawab1efe68f2021-12-20 01:54:072487
2488 // 4) Check IsSameOrigin() and GetUrl().
2489 // a
2490 EXPECT_THAT(*can_store_result.tree_reasons,
2491 MatchesTreeResult(/*same_origin=*/true,
Fergal Daly306fc3f2021-12-23 09:58:302492 /*url=*/url_a));
Riho Isawab1efe68f2021-12-20 01:54:072493 // a->a
Fergal Daly306fc3f2021-12-23 09:58:302494 EXPECT_THAT(*can_store_result.tree_reasons->GetChildren().at(0),
2495 MatchesTreeResult(/*same_origin=*/true,
2496 /*url=*/url_subframe_a));
Riho Isawab1efe68f2021-12-20 01:54:072497 // a->b
Fergal Daly306fc3f2021-12-23 09:58:302498 EXPECT_THAT(*can_store_result.tree_reasons->GetChildren().at(1),
2499 MatchesTreeResult(/*same_origin=*/false,
2500 /*url=*/url_subframe_b));
Riho Isawab1efe68f2021-12-20 01:54:072501 // a->c
Fergal Daly306fc3f2021-12-23 09:58:302502 EXPECT_THAT(*can_store_result.tree_reasons->GetChildren().at(2),
2503 MatchesTreeResult(/*same_origin=*/false,
2504 /*url=*/url_subframe_c));
Riho Isawab1efe68f2021-12-20 01:54:072505
2506 // 5) Check that the blocking reasons match.
2507 // a
2508 EXPECT_THAT(can_store_result.tree_reasons->GetDocumentResult(),
2509 MatchesDocumentResult(
Maksim Moskvitin13f5b972023-05-15 16:24:592510 NotRestoredReasons({NotRestoredReason::kBlocklistedFeatures}),
Riho Isawab1efe68f2021-12-20 01:54:072511 BlockListedFeatures(
Maksim Moskvitin13f5b972023-05-15 16:24:592512 {blink::scheduler::WebSchedulerTrackedFeature::kDummy})));
Riho Isawab1efe68f2021-12-20 01:54:072513 // a->a
2514 EXPECT_THAT(
2515 can_store_result.tree_reasons->GetChildren().at(0)->GetDocumentResult(),
Yuzu Saijodcabe562022-04-25 06:06:392516 MatchesDocumentResult(NotRestoredReasons(),
Riho Isawab1efe68f2021-12-20 01:54:072517 BlockListedFeatures(BlockListedFeatures())));
2518 // a->b
2519 EXPECT_THAT(
2520 can_store_result.tree_reasons->GetChildren().at(1)->GetDocumentResult(),
2521 MatchesDocumentResult(
Maksim Moskvitin13f5b972023-05-15 16:24:592522 NotRestoredReasons({NotRestoredReason::kBlocklistedFeatures}),
Riho Isawab1efe68f2021-12-20 01:54:072523 BlockListedFeatures(
Maksim Moskvitin13f5b972023-05-15 16:24:592524 {blink::scheduler::WebSchedulerTrackedFeature::kDummy})));
Riho Isawab1efe68f2021-12-20 01:54:072525 // a->c
2526 EXPECT_THAT(
2527 can_store_result.tree_reasons->GetChildren().at(2)->GetDocumentResult(),
Yuzu Saijodcabe562022-04-25 06:06:392528 MatchesDocumentResult(NotRestoredReasons(),
Riho Isawab1efe68f2021-12-20 01:54:072529 BlockListedFeatures(BlockListedFeatures())));
2530}
2531
Yuzu Saijoc3478072022-03-24 06:15:262532// Check the contents of the BackForwardCacheCanStoreTreeResult of a page when
2533// it is evicted.
2534IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
2535 TreeResultEvictionMainFrame) {
2536 ASSERT_TRUE(embedded_test_server()->Start());
2537 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
2538 GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
2539
2540 // 1) Navigate to a.
2541 ASSERT_TRUE(NavigateToURL(shell(), url_a));
2542 RenderFrameHostImplWrapper rfh_a(current_frame_host());
2543 rfh_a->GetBackForwardCacheMetrics()->SetObserverForTesting(this);
2544
2545 // 2) Navigate to B and evict A by JavaScript execution.
2546 ASSERT_TRUE(NavigateToURL(shell(), url_b));
2547 EvictByJavaScript(rfh_a.get());
2548 ASSERT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
2549
2550 // 3) Go back to A.
2551 ASSERT_TRUE(HistoryGoBack(web_contents()));
2552 ExpectNotRestored({NotRestoredReason::kJavaScriptExecution}, {}, {}, {}, {},
2553 FROM_HERE);
2554 EXPECT_THAT(GetTreeResult()->GetDocumentResult(),
2555 MatchesDocumentResult(
Maksim Moskvitin13f5b972023-05-15 16:24:592556 NotRestoredReasons({NotRestoredReason::kJavaScriptExecution}),
Yuzu Saijoc3478072022-03-24 06:15:262557 BlockListedFeatures()));
2558}
2559
2560// Check the contents of the BackForwardCacheCanStoreTreeResult of a page when
2561// its subframe is evicted.
2562IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
2563 TreeResultEvictionSubFrame) {
2564 ASSERT_TRUE(embedded_test_server()->Start());
2565 GURL url_a(embedded_test_server()->GetURL(
2566 "a.com", "/cross_site_iframe_factory.html?a(b)"));
2567 GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html"));
2568
2569 // 1) Navigate to A.
2570 ASSERT_TRUE(NavigateToURL(shell(), url_a));
2571 RenderFrameHostImplWrapper rfh_a(current_frame_host());
2572 RenderFrameHostImplWrapper rfh_b(
2573 current_frame_host()->child_at(0)->current_frame_host());
2574 rfh_a->GetBackForwardCacheMetrics()->SetObserverForTesting(this);
2575
2576 // 2) Navigate to C and evict A's subframe B by JavaScript execution.
2577 ASSERT_TRUE(NavigateToURL(shell(), url_c));
2578 EvictByJavaScript(rfh_b.get());
2579 ASSERT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
2580
2581 // 3) Go back to A.
2582 ASSERT_TRUE(HistoryGoBack(web_contents()));
2583 ExpectNotRestored({NotRestoredReason::kJavaScriptExecution}, {}, {}, {}, {},
2584 FROM_HERE);
2585 // Main frame result in the tree is empty.
Yuzu Saijodcabe562022-04-25 06:06:392586 EXPECT_THAT(
2587 GetTreeResult()->GetDocumentResult(),
2588 MatchesDocumentResult(NotRestoredReasons(), BlockListedFeatures()));
Yuzu Saijoc3478072022-03-24 06:15:262589 // Subframe result in the tree contains the reason.
2590 EXPECT_THAT(GetTreeResult()->GetChildren().at(0)->GetDocumentResult(),
2591 MatchesDocumentResult(
Maksim Moskvitin13f5b972023-05-15 16:24:592592 NotRestoredReasons({NotRestoredReason::kJavaScriptExecution}),
Yuzu Saijoc3478072022-03-24 06:15:262593 BlockListedFeatures()));
2594}
2595
2596// Check the contents of the BackForwardCacheCanStoreTreeResult of a page when
2597// its subframe's subframe is evicted.
2598IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
2599 TreeResultEvictionSubFramesSubframe) {
2600 ASSERT_TRUE(embedded_test_server()->Start());
2601 GURL url_a(embedded_test_server()->GetURL(
2602 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
2603 GURL url_d(embedded_test_server()->GetURL("d.com", "/title1.html"));
2604
2605 // 1) Navigate to A.
2606 ASSERT_TRUE(NavigateToURL(shell(), url_a));
2607 RenderFrameHostImplWrapper rfh_a(current_frame_host());
2608 RenderFrameHostImplWrapper rfh_c(current_frame_host()
2609 ->child_at(0)
2610 ->current_frame_host()
2611 ->child_at(0)
2612 ->current_frame_host());
2613 rfh_a->GetBackForwardCacheMetrics()->SetObserverForTesting(this);
2614
2615 // 2) Navigate to D and evict C by JavaScript execution.
2616 ASSERT_TRUE(NavigateToURL(shell(), url_d));
2617 EvictByJavaScript(rfh_c.get());
2618 ASSERT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
2619
2620 // 3) Go back to A.
2621 ASSERT_TRUE(HistoryGoBack(web_contents()));
2622 ExpectNotRestored({NotRestoredReason::kJavaScriptExecution}, {}, {}, {}, {},
2623 FROM_HERE);
2624 // Main frame result in the tree is empty.
Yuzu Saijodcabe562022-04-25 06:06:392625 EXPECT_THAT(
2626 GetTreeResult()->GetDocumentResult(),
2627 MatchesDocumentResult(NotRestoredReasons(), BlockListedFeatures()));
Yuzu Saijoc3478072022-03-24 06:15:262628 // The first level subframe result in the tree is empty.
Yuzu Saijodcabe562022-04-25 06:06:392629 EXPECT_THAT(
2630 GetTreeResult()->GetChildren().at(0)->GetDocumentResult(),
2631 MatchesDocumentResult(NotRestoredReasons(), BlockListedFeatures()));
Yuzu Saijoc3478072022-03-24 06:15:262632 // The second level subframe result in the tree contains the reason.
2633 EXPECT_THAT(GetTreeResult()
2634 ->GetChildren()
2635 .at(0)
2636 ->GetChildren()
2637 .at(0)
2638 ->GetDocumentResult(),
2639 MatchesDocumentResult(
Maksim Moskvitin13f5b972023-05-15 16:24:592640 NotRestoredReasons({NotRestoredReason::kJavaScriptExecution}),
Yuzu Saijoc3478072022-03-24 06:15:262641 BlockListedFeatures()));
2642}
2643
Yuzu Saijo7b7b9a72022-04-20 05:12:082644void BackForwardCacheBrowserTest::InstallUnloadHandlerOnMainFrame() {
2645 EXPECT_TRUE(ExecJs(current_frame_host(), R"(
Minoru Chikamune52d6dff2021-12-27 14:59:532646 localStorage["unload_run_count"] = 0;
2647 window.onunload = () => {
2648 localStorage["unload_run_count"] =
2649 1 + parseInt(localStorage["unload_run_count"]);
2650 };
2651 )"));
Yuzu Saijo7b7b9a72022-04-20 05:12:082652 EXPECT_EQ("0", GetUnloadRunCount());
2653}
Minoru Chikamune52d6dff2021-12-27 14:59:532654
Yuzu Saijo7b7b9a72022-04-20 05:12:082655void BackForwardCacheBrowserTest::InstallUnloadHandlerOnSubFrame() {
2656 TestNavigationObserver navigation_observer(shell()->web_contents(), 1);
2657 EXPECT_TRUE(ExecJs(current_frame_host(), R"(
kouheib3517d7d2021-05-19 07:48:562658 const iframeElement = document.createElement("iframe");
2659 iframeElement.src = "%s";
2660 document.body.appendChild(iframeElement);
Minoru Chikamune52d6dff2021-12-27 14:59:532661 )"));
Yuzu Saijo7b7b9a72022-04-20 05:12:082662 navigation_observer.Wait();
2663 RenderFrameHostImpl* subframe_render_frame_host =
2664 current_frame_host()->child_at(0)->current_frame_host();
2665 EXPECT_TRUE(ExecJs(subframe_render_frame_host, R"(
Minoru Chikamune52d6dff2021-12-27 14:59:532666 localStorage["unload_run_count"] = 0;
2667 window.onunload = () => {
2668 localStorage["unload_run_count"] =
2669 1 + parseInt(localStorage["unload_run_count"]);
2670 };
2671 )"));
Yuzu Saijo7b7b9a72022-04-20 05:12:082672 EXPECT_EQ("0", GetUnloadRunCount());
2673}
Minoru Chikamune52d6dff2021-12-27 14:59:532674
Yuzu Saijo7b7b9a72022-04-20 05:12:082675EvalJsResult BackForwardCacheBrowserTest::GetUnloadRunCount() {
2676 return GetLocalStorage(current_frame_host(), "unload_run_count");
2677}
kouheib3517d7d2021-05-19 07:48:562678
Fergal Daly9909e092022-07-06 14:09:342679bool BackForwardCacheBrowserTest::AddBlocklistedFeature(RenderFrameHost* rfh) {
Ming-Ying Chung5a60c322022-09-29 04:09:312680 // Add kDummy as blocking feature.
2681 RenderFrameHostImplWrapper rfh_a(rfh);
2682 rfh_a->UseDummyStickyBackForwardCacheDisablingFeatureForTesting();
2683 return true;
Fergal Daly9909e092022-07-06 14:09:342684}
2685
2686void BackForwardCacheBrowserTest::ExpectNotRestoredDueToBlocklistedFeature(
2687 base::Location location) {
Ming-Ying Chung5a60c322022-09-29 04:09:312688 ExpectNotRestored({NotRestoredReason::kBlocklistedFeatures},
2689 {blink::scheduler::WebSchedulerTrackedFeature::kDummy}, {},
2690 {}, {}, location);
Fergal Daly9909e092022-07-06 14:09:342691}
2692
Ming-Ying Chunga8e8a2e2022-08-16 06:04:502693const ukm::TestAutoSetUkmRecorder& BackForwardCacheBrowserTest::ukm_recorder() {
2694 return *ukm_recorder_;
2695}
2696
2697const base::HistogramTester& BackForwardCacheBrowserTest::histogram_tester() {
2698 return *histogram_tester_;
Ming-Ying Chungee9b9ea2022-08-15 08:24:252699}
2700
Fergal Daly97c4e592023-02-28 10:27:332701// Ensure that psges with unload are only allowed to enter back/forward cache by
2702// default on Android.
2703IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, UnloadAllowedFlag) {
2704#if BUILDFLAG(IS_ANDROID)
2705 ASSERT_TRUE(BackForwardCacheImpl::IsUnloadAllowed());
2706#else
2707 ASSERT_FALSE(BackForwardCacheImpl::IsUnloadAllowed());
2708#endif
2709}
2710
Fergal Daly9909e092022-07-06 14:09:342711IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
2712 FrameWithBlocklistedFeatureNotCached) {
2713 ASSERT_TRUE(embedded_test_server()->Start());
2714
2715 // Navigate to a page that contains a blocklisted feature.
2716 EXPECT_TRUE(NavigateToURL(
2717 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
2718
2719 RenderFrameHostWrapper rfh(current_frame_host());
2720
2721 ASSERT_TRUE(AddBlocklistedFeature(rfh.get()));
2722
2723 // Navigate away.
2724 EXPECT_TRUE(NavigateToURL(
2725 shell(), embedded_test_server()->GetURL("b.com", "/title1.html")));
2726
2727 // The page with the unsupported feature should be deleted (not cached).
2728 ASSERT_TRUE(rfh.WaitUntilRenderFrameDeleted());
2729
2730 // Go back.
2731 ASSERT_TRUE(HistoryGoBack(web_contents()));
2732 ExpectNotRestoredDueToBlocklistedFeature(FROM_HERE);
2733}
2734
2735IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
2736 SubframeWithBlocklistedFeatureNotCached) {
2737 ASSERT_TRUE(embedded_test_server()->Start());
2738
2739 // Navigate to a page with an iframe that contains a blocklisted feature.
2740 EXPECT_TRUE(NavigateToURL(
2741 shell(), embedded_test_server()->GetURL(
2742 "a.com", "/cross_site_iframe_factory.html?a(b)")));
2743
2744 RenderFrameHostWrapper rfh(
2745 current_frame_host()->child_at(0)->current_frame_host());
2746
2747 ASSERT_TRUE(AddBlocklistedFeature(rfh.get()));
2748
2749 // Navigate away.
2750 EXPECT_TRUE(NavigateToURL(
2751 shell(), embedded_test_server()->GetURL("b.com", "/title1.html")));
2752
2753 // The page with the unsupported feature should be deleted (not cached).
2754 ASSERT_TRUE(rfh.WaitUntilRenderFrameDeleted());
2755
2756 // Go back.
2757 ASSERT_TRUE(HistoryGoBack(web_contents()));
2758 ExpectNotRestoredDueToBlocklistedFeature(FROM_HERE);
2759}
2760
Fergal Daly97c4e592023-02-28 10:27:332761class BackForwardCacheBrowserUnloadHandlerTest
2762 : public BackForwardCacheBrowserTest,
Kurumi Mutob3137e82024-01-09 12:28:022763 public ::testing::WithParamInterface<
Kurumi Muto863fd9a2024-02-06 11:51:322764 std::tuple<bool, bool, bool, TestFrameType>> {
Fergal Daly97c4e592023-02-28 10:27:332765 public:
2766 void SetUpCommandLine(base::CommandLine* command_line) override {
Fergal Daly97c4e592023-02-28 10:27:332767 if (IsUnloadAllowed()) {
Kurumi Mutob3137e82024-01-09 12:28:022768 EnableFeatureAndSetParams(kBackForwardCacheUnloadAllowed, "", "");
Fergal Daly97c4e592023-02-28 10:27:332769 } else {
Kurumi Mutob3137e82024-01-09 12:28:022770 DisableFeature(kBackForwardCacheUnloadAllowed);
Fergal Daly97c4e592023-02-28 10:27:332771 }
Kurumi Mutob3137e82024-01-09 12:28:022772 if (IsUnloadBlocklisted()) {
2773 EnableFeatureAndSetParams(blink::features::kUnloadBlocklisted, "", "");
2774 } else {
2775 DisableFeature(blink::features::kUnloadBlocklisted);
2776 }
Kurumi Muto863fd9a2024-02-06 11:51:322777 if (IsUnloadDeprecationOptedOut()) {
2778 EnableFeatureAndSetParams(blink::features::kDeprecateUnloadOptOut, "",
2779 "");
2780 } else {
2781 DisableFeature(blink::features::kDeprecateUnloadOptOut);
2782 }
2783
Kurumi Mutob3137e82024-01-09 12:28:022784 BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
Fergal Daly97c4e592023-02-28 10:27:332785 }
2786
2787 bool IsUnloadAllowed() { return std::get<0>(GetParam()); }
Kurumi Mutob3137e82024-01-09 12:28:022788 bool IsUnloadBlocklisted() { return std::get<1>(GetParam()); }
Kurumi Muto863fd9a2024-02-06 11:51:322789 bool IsUnloadDeprecationOptedOut() { return std::get<2>(GetParam()); }
Fergal Daly97c4e592023-02-28 10:27:332790
Kurumi Muto863fd9a2024-02-06 11:51:322791 TestFrameType GetTestFrameType() { return std::get<3>(GetParam()); }
Fergal Daly97c4e592023-02-28 10:27:332792
2793 private:
2794 base::test::ScopedFeatureList scoped_feature_list_;
2795};
2796
Kurumi Muto863fd9a2024-02-06 11:51:322797// Ensure that unload handlers in main frames and subframes block caching,
2798// depending on unload deprecation status and OS.
Fergal Daly97c4e592023-02-28 10:27:332799IN_PROC_BROWSER_TEST_P(BackForwardCacheBrowserUnloadHandlerTest,
2800 UnloadHandlerPresent) {
kouheib3517d7d2021-05-19 07:48:562801 ASSERT_TRUE(embedded_test_server()->Start());
2802
Minoru Chikamune52d6dff2021-12-27 14:59:532803 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
2804 GURL url_b(embedded_test_server()->GetURL("b.com", "/title2.html"));
kouheib3517d7d2021-05-19 07:48:562805
2806 // 1) Navigate to A.
2807 EXPECT_TRUE(NavigateToURL(shell(), url_a));
Fergal Daly97c4e592023-02-28 10:27:332808
Fergal Dalyfbb93e42025-02-03 07:01:072809 BackForwardCacheCanStoreDocumentResult::NotRestoredReasons
Kurumi Muto863fd9a2024-02-06 11:51:322810 expected_blocking_reasons;
2811 std::vector<blink::scheduler::WebSchedulerTrackedFeature>
2812 expected_blocklisted_reason;
2813 if (IsUnloadBlocklisted()) {
Fergal Dalyfbb93e42025-02-03 07:01:072814 expected_blocking_reasons.Put(
Kurumi Muto863fd9a2024-02-06 11:51:322815 BackForwardCacheMetrics::NotRestoredReason::kBlocklistedFeatures);
2816 expected_blocklisted_reason.push_back(
2817 blink::scheduler::WebSchedulerTrackedFeature::kUnloadHandler);
2818 }
Fergal Daly97c4e592023-02-28 10:27:332819 switch (GetTestFrameType()) {
2820 case content::TestFrameType::kMainFrame:
2821 InstallUnloadHandlerOnMainFrame();
Fergal Dalyfbb93e42025-02-03 07:01:072822 expected_blocking_reasons.Put(BackForwardCacheMetrics::NotRestoredReason::
2823 kUnloadHandlerExistsInMainFrame);
Fergal Daly97c4e592023-02-28 10:27:332824 break;
2825 case content::TestFrameType::kSubFrame:
2826 InstallUnloadHandlerOnSubFrame();
Fergal Dalyfbb93e42025-02-03 07:01:072827 expected_blocking_reasons.Put(BackForwardCacheMetrics::NotRestoredReason::
2828 kUnloadHandlerExistsInSubFrame);
Fergal Daly97c4e592023-02-28 10:27:332829 break;
2830 default:
Peter Boströmfc7ddc182024-10-31 19:37:212831 NOTREACHED();
Fergal Daly97c4e592023-02-28 10:27:332832 }
kouheib3517d7d2021-05-19 07:48:562833
2834 // 2) Navigate to B.
2835 EXPECT_TRUE(NavigateToURL(shell(), url_b));
2836
2837 // 3) Go back.
Fergal Daly5495b0e2021-11-19 02:03:282838 ASSERT_TRUE(HistoryGoBack(web_contents()));
kouheib3517d7d2021-05-19 07:48:562839
Kurumi Muto863fd9a2024-02-06 11:51:322840 bool unload_never_blocks = IsUnloadAllowed();
2841 bool unload_deprecated_and_not_opted_out =
Sandor Majora9a29ad52025-02-20 16:00:142842 (base::FeatureList::IsEnabled(network::features::kDeprecateUnload) &&
Kurumi Muto863fd9a2024-02-06 11:51:322843 !IsUnloadDeprecationOptedOut());
2844 if (unload_never_blocks || unload_deprecated_and_not_opted_out) {
Kurumi Mutob3137e82024-01-09 12:28:022845 // Pages with unload handlers are eligible for bfcache only if it is
Kurumi Muto863fd9a2024-02-06 11:51:322846 // specifically allowed (happens on Android). Also, when unload is
2847 // deprecated and `kDeprecateUnloadOptOut` doesn't override it, unload
2848 // handlers cannot be installed so there should be no blocker for BFCache.
2849 ExpectRestored(FROM_HERE);
2850 EXPECT_EQ("0", GetUnloadRunCount());
Minoru Chikamune75b51fd2021-09-02 15:29:022851 } else {
Kurumi Muto863fd9a2024-02-06 11:51:322852 ExpectNotRestored(expected_blocking_reasons, expected_blocklisted_reason,
2853 {}, {}, {}, FROM_HERE);
2854 EXPECT_EQ("1", GetUnloadRunCount());
kouheib3517d7d2021-05-19 07:48:562855 }
kouheib3517d7d2021-05-19 07:48:562856}
2857
Kurumi Muto863fd9a2024-02-06 11:51:322858// First param: whether unload is allowed or not.
2859// Second one: whether unload is blocklisted or not.
2860// Third one: whether it's opted out from unload deprecation or not.
Fergal Daly97c4e592023-02-28 10:27:332861INSTANTIATE_TEST_SUITE_P(
2862 All,
2863 BackForwardCacheBrowserUnloadHandlerTest,
2864 ::testing::Combine(::testing::Bool(),
Kurumi Mutob3137e82024-01-09 12:28:022865 ::testing::Bool(),
Kurumi Muto863fd9a2024-02-06 11:51:322866 ::testing::Bool(),
Fergal Daly97c4e592023-02-28 10:27:332867 ::testing::Values(TestFrameType::kMainFrame,
2868 TestFrameType::kSubFrame)));
kouheib3517d7d2021-05-19 07:48:562869
Mingyu Lei8c1b9112023-01-18 03:59:012870IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, DisableForRenderFrameHost) {
2871 ASSERT_TRUE(embedded_test_server()->Start());
2872
2873 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
2874 GURL url_b(embedded_test_server()->GetURL("b.com", "/title2.html"));
2875
2876 // 1) Navigate to A.
2877 EXPECT_TRUE(NavigateToURL(shell(), url_a));
2878 RenderFrameHostWrapper rfh_wrapper_a(current_frame_host());
2879
2880 // 2) Navigate to B.
2881 EXPECT_TRUE(NavigateToURL(shell(), url_b));
2882 RenderFrameHostWrapper rfh_wrapper_b(current_frame_host());
2883
2884 // Regardless of whether the source Id is set or not, it shouldn't affect the
2885 // result of the BFCache eviction.
2886 BackForwardCache::DisabledReason test_reason =
2887 BackForwardCacheDisable::DisabledReason(
2888 BackForwardCacheDisable::DisabledReasonId::kUnknown);
2889
2890 // 3) Disable BFCache for A with UKM source Id and go back.
2891 BackForwardCache::DisableForRenderFrameHost(
2892 rfh_wrapper_a.get(), test_reason, ukm::UkmRecorder::GetNewSourceID());
2893 ASSERT_TRUE(HistoryGoBack(web_contents()));
2894 ASSERT_TRUE(rfh_wrapper_a.WaitUntilRenderFrameDeleted());
2895 // Page A should be evicted properly.
2896 ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::
2897 kDisableForRenderFrameHostCalled},
2898 {}, {}, {test_reason}, {}, FROM_HERE);
2899
2900 // 4) Disable BFCache for B without UKM source Id and go forward.
2901 BackForwardCache::DisableForRenderFrameHost(rfh_wrapper_b.get(), test_reason);
2902 ASSERT_TRUE(HistoryGoForward(web_contents()));
2903 ASSERT_TRUE(rfh_wrapper_b.WaitUntilRenderFrameDeleted());
2904 // Page B should be evicted properly.
2905 ExpectNotRestored({BackForwardCacheMetrics::NotRestoredReason::
2906 kDisableForRenderFrameHostCalled},
2907 {}, {}, {test_reason}, {}, FROM_HERE);
2908}
2909
Adithya Srinivasan7fe5ce662021-06-04 21:36:042910namespace {
2911enum class SubframeType { SameSite, CrossSite };
2912}
2913
2914class BackForwardCacheEvictionDueToSubframeNavigationBrowserTest
2915 : public BackForwardCacheBrowserTest,
2916 public ::testing::WithParamInterface<SubframeType> {
2917 public:
2918 // Provides meaningful param names instead of /0 and /1.
2919 static std::string DescribeParams(
2920 const ::testing::TestParamInfo<ParamType>& info) {
2921 switch (info.param) {
2922 case SubframeType::SameSite:
2923 return "SameSite";
2924 case SubframeType::CrossSite:
2925 return "CrossSite";
2926 }
2927 }
Ming-Ying Chung71d0b8e72024-03-04 06:27:282928
2929 protected:
2930 bool UseCrossOriginSubframe() const {
2931 return GetParam() == SubframeType::CrossSite;
2932 }
Adithya Srinivasan7fe5ce662021-06-04 21:36:042933};
2934
2935IN_PROC_BROWSER_TEST_P(
2936 BackForwardCacheEvictionDueToSubframeNavigationBrowserTest,
2937 SubframePendingCommitShouldPreventCache) {
2938 ASSERT_TRUE(embedded_test_server()->Start());
2939 GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
Adithya Srinivasan7fe5ce662021-06-04 21:36:042940 GURL subframe_url = embedded_test_server()->GetURL(
Ming-Ying Chung71d0b8e72024-03-04 06:27:282941 UseCrossOriginSubframe() ? "b.com" : "a.com", "/title1.html");
Adithya Srinivasan7fe5ce662021-06-04 21:36:042942
2943 IsolateOriginsForTesting(embedded_test_server(), web_contents(),
Jose Dapena Paze7447752023-09-20 12:11:122944 std::vector<std::string>{"a.com", "b.com"});
Adithya Srinivasan7fe5ce662021-06-04 21:36:042945
2946 // 1) Navigate to a.com.
2947 EXPECT_TRUE(NavigateToURL(shell(), a_url));
2948 RenderFrameHostImpl* main_frame = current_frame_host();
2949
2950 // 2) Add subframe and wait for empty document to commit.
2951 CreateSubframe(web_contents(), "child", GURL(""), true);
2952
2953 CommitMessageDelayer commit_message_delayer(
2954 web_contents(), subframe_url,
2955 base::BindLambdaForTesting([&](RenderFrameHost*) {
2956 // 5) Test that page cannot be stored in bfcache when subframe is
2957 // pending commit.
Riho Isawab1efe68f2021-12-20 01:54:072958 BackForwardCacheCanStoreDocumentResultWithTree can_store_result =
Adithya Srinivasan7fe5ce662021-06-04 21:36:042959 web_contents()
2960 ->GetController()
2961 .GetBackForwardCache()
Yuzu Saijoef104a02022-05-13 17:04:282962 .GetCurrentBackForwardCacheEligibility(
2963 static_cast<RenderFrameHostImpl*>(main_frame));
Yuzu Saijodcabe562022-04-25 06:06:392964 EXPECT_TRUE(can_store_result.flattened_reasons.HasNotRestoredReason(
Adithya Srinivasan7fe5ce662021-06-04 21:36:042965 BackForwardCacheMetrics::NotRestoredReason::kSubframeIsNavigating));
2966 }));
2967
2968 // 3) Start navigation in subframe to |subframe_url|.
Adithya Srinivasan9f27eb5112023-04-19 20:28:452969 ExecuteScriptAsync(
Adithya Srinivasan7fe5ce662021-06-04 21:36:042970 main_frame,
Adithya Srinivasan9f27eb5112023-04-19 20:28:452971 JsReplace("document.querySelector('#child').src = $1;", subframe_url));
Adithya Srinivasan7fe5ce662021-06-04 21:36:042972 // 4) Wait until subframe navigation is pending commit.
2973 commit_message_delayer.Wait();
2974}
2975
Ming-Ying Chung71d0b8e72024-03-04 06:27:282976// Check that when the main frame gets BFCached while the subframe navigation
2977// deferring NavigationThrottles has already ran, the issue that the subframe
2978// navigation escapes the throttle deferral is addressed.
2979IN_PROC_BROWSER_TEST_P(
2980 BackForwardCacheEvictionDueToSubframeNavigationBrowserTest,
2981 MainFrameCommitFirstAndSubframePendingCommitShouldBeEvicted) {
2982 ASSERT_TRUE(embedded_test_server()->Start());
2983 // Prepare the main frame and the sub frame, where both of them are same-site.
2984 GURL main_url(embedded_test_server()->GetURL(
2985 "a.com", "/cross_site_iframe_factory.html?a(a)"));
2986 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2987 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
2988 FrameTreeNode* child = root->child_at(0);
2989 RenderFrameHostImplWrapper child_rfh(child->current_frame_host());
2990
2991 // Navigate both frames simultaneously.
2992 const std::string subframe_origin =
2993 UseCrossOriginSubframe() ? "b.com" : "a.com";
2994 GURL new_url_1(
2995 embedded_test_server()->GetURL(subframe_origin, "/title1.html"));
2996 GURL new_url_2(
2997 embedded_test_server()->GetURL(subframe_origin, "/title2.html"));
2998 TestNavigationManager manager1(web_contents(), new_url_1);
2999 TestNavigationManager manager2(web_contents(), new_url_2);
3000 auto script = JsReplace("location = $1; frames[0].location = $2;", new_url_1,
3001 new_url_2);
3002 EXPECT_TRUE(ExecJs(web_contents(), script));
3003
3004 // Wait for main frame request, but don't commit it yet. This should create
3005 // a speculative RenderFrameHost.
3006 ASSERT_TRUE(manager1.WaitForRequestStart());
3007
3008 // Wait for subframe request, but don't commit it yet.
3009 ASSERT_TRUE(manager2.WaitForRequestStart());
3010
3011 // Now let the main frame commit.
3012 ASSERT_TRUE(manager1.WaitForNavigationFinished());
3013 // Make sure the main frame is at the new URL.
3014 ASSERT_TRUE(root->current_frame_host()->IsRenderFrameLive());
3015 ASSERT_EQ(new_url_1, root->current_frame_host()->GetLastCommittedURL());
3016
3017 // The subframe should be gone now: it should have been evicted from BFCache
3018 // because the subframe is still navigating; otherwise, this will cause a
3019 // crash.
3020 ASSERT_TRUE(manager2.WaitForNavigationFinished());
3021 EXPECT_TRUE(child_rfh.IsDestroyed());
3022 ASSERT_TRUE(HistoryGoBack(web_contents()));
3023 ExpectNotRestored(
3024 {BackForwardCacheMetrics::NotRestoredReason::kSubframeIsNavigating}, {},
3025 {}, {}, {}, FROM_HERE);
3026}
3027
Adithya Srinivasan7fe5ce662021-06-04 21:36:043028INSTANTIATE_TEST_SUITE_P(
3029 All,
3030 BackForwardCacheEvictionDueToSubframeNavigationBrowserTest,
3031 ::testing::Values(SubframeType::SameSite, SubframeType::CrossSite),
3032 &BackForwardCacheEvictionDueToSubframeNavigationBrowserTest::
3033 DescribeParams);
3034
murakinonokabb2a6c12024-01-18 05:49:053035namespace {
3036enum class SubframeNavigationType { WithoutURLLoader, WithURLLoader };
3037}
3038
Nathan Memmottf54d81f2024-12-11 18:56:213039// Test for pages which has subframe(s) with ongoing navigation(s).
murakinonokabb2a6c12024-01-18 05:49:053040class BackForwardCacheWithSubframeNavigationBrowserTest
murakinonoka97a8f042024-01-10 09:17:073041 : public BackForwardCacheBrowserTest {
3042 protected:
murakinonokabb2a6c12024-01-18 05:49:053043 void SetUpOnMainThread() override {
3044 BackForwardCacheBrowserTest::SetUpOnMainThread();
3045 ASSERT_TRUE(embedded_test_server()->Start());
3046 }
3047
murakinonoka97a8f042024-01-10 09:17:073048 void SetUpCommandLine(base::CommandLine* command_line) override {
Fergal Dalyb1258112025-07-15 03:00:513049 EnableCacheSize(2, std::nullopt);
murakinonoka97a8f042024-01-10 09:17:073050 BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
3051 }
3052
3053 // Start a subframe navigation and pause it when we get the confirmation
3054 // dialog triggered by beforeunload event, which is before
murakinonokabb2a6c12024-01-18 05:49:053055 // WillCommitWithoutUrlLoader or WillStartRequest.
murakinonoka97a8f042024-01-10 09:17:073056 void NavigateSubframeAndPauseAtBeforeUnload(
3057 BeforeUnloadBlockingDelegate& beforeunload_pauser,
3058 RenderFrameHostImpl* sub_rfh,
murakinonokabb2a6c12024-01-18 05:49:053059 const GURL& subframe_navigate_url,
Tom Sepezb55f2f12024-10-02 21:54:333060 std::string_view iframe_id) {
murakinonoka97a8f042024-01-10 09:17:073061 ASSERT_TRUE(ExecJs(sub_rfh, R"(
3062 window.addEventListener('beforeunload', e =>
3063 e.returnValue='blocked'
3064 );)"));
3065
3066 // Start a subframe navigation which will trigger the beforeunload dialog
3067 // that pauses that navigation. Using `BeginNavigateIframeToURL` is
3068 // necessary here, since we pause this navigation on beforeunload event. So,
3069 // we don't want to wait for the navigation to finish.
murakinonokabb2a6c12024-01-18 05:49:053070 BeginNavigateIframeToURL(web_contents(), iframe_id, subframe_navigate_url);
murakinonoka97a8f042024-01-10 09:17:073071 beforeunload_pauser.Wait();
3072 }
3073
3074 // Start a subframe navigation and pause it before `DidCommitNavigation`.
Rakina Zata Amnic06ef3a42024-03-11 20:41:353075 void NavigateSubframeAndPauseAtDidCommit(FrameTreeNode* ftn,
murakinonoka97a8f042024-01-10 09:17:073076 const GURL& subframe_navigate_url) {
Jiacheng Guo4bdd0be2024-06-11 23:35:213077 // Enforce the creation of speculative RFH to correctly wait for
3078 // the commit event.
3079 SpeculativeRenderFrameHostObserver observer(web_contents(),
3080 subframe_navigate_url);
murakinonoka97a8f042024-01-10 09:17:073081 // We have to pause a navigation before `DidCommitNavigation`, so we don't
3082 // want to wait for the navigation to finish.
Rakina Zata Amnic06ef3a42024-03-11 20:41:353083 ASSERT_TRUE(BeginNavigateToURLFromRenderer(ftn, subframe_navigate_url));
Jiacheng Guo02a0423a2024-06-20 02:03:563084 // Navigation without a URL loader shall always create the speculative RFH
3085 // immediately or never create one.
3086 // The same-site navigation to the subframe will not create a new
3087 // speculative RFH if render document is not enabled for subframes.
3088 if (subframe_navigate_url.SchemeIsHTTPOrHTTPS() &&
3089 ShouldCreateNewRenderFrameHostOnSameSiteNavigation(
3090 /*is_main_frame=*/false, /*is_local_root=*/true)) {
3091 observer.Wait();
3092 }
murakinonoka97a8f042024-01-10 09:17:073093
Rakina Zata Amnic06ef3a42024-03-11 20:41:353094 // Wait until the navigation is pending commit. Note that the navigation
3095 // might use a speculative RenderFrameHost, so use that if necessary.
3096 RenderFrameHostImpl* speculative_rfh =
3097 ftn->render_manager()->speculative_frame_host();
3098 CommitNavigationPauser commit_pauser(
3099 speculative_rfh ? speculative_rfh : ftn->current_frame_host());
murakinonoka97a8f042024-01-10 09:17:073100 commit_pauser.WaitForCommitAndPause();
3101 }
3102
murakinonokabb2a6c12024-01-18 05:49:053103 // Put a page which has a subframe with a navigation which hasn't reached the
3104 // "pending commit" stage nor sent a network request into BackForwardCache and
3105 // confirm the subframe navigation has been deferred.
3106 void BFCachePageWithSubframeNavigationBeforeDidStartNavigation(
3107 const GURL& main_frame_navigate_url,
3108 const GURL& subframe_navigate_url,
murakinonoka97a8f042024-01-10 09:17:073109 RenderFrameHostImplWrapper& sub_rfh,
3110 TestNavigationManager& subframe_navigation_manager,
Tom Sepezb55f2f12024-10-02 21:54:333111 std::string_view iframe_id) {
murakinonoka97a8f042024-01-10 09:17:073112 FrameTreeNode* child_ftn =
3113 web_contents()->GetPrimaryFrameTree().root()->child_at(0);
3114 {
3115 BeforeUnloadBlockingDelegate beforeunload_pauser(web_contents());
3116 NavigateSubframeAndPauseAtBeforeUnload(beforeunload_pauser, sub_rfh.get(),
murakinonokabb2a6c12024-01-18 05:49:053117 subframe_navigate_url, iframe_id);
murakinonoka97a8f042024-01-10 09:17:073118
3119 // Subframe navigation is ongoing, so `NavigateToURL` cannot be used since
3120 // this function waits for all frames including subframe to finish
3121 // loading.
murakinonokabb2a6c12024-01-18 05:49:053122 ASSERT_TRUE(NavigateToURLFromRenderer(sub_rfh->GetMainFrame(),
3123 main_frame_navigate_url));
murakinonoka97a8f042024-01-10 09:17:073124
murakinonokabb2a6c12024-01-18 05:49:053125 // The subframe navigation hasn't reached the "pending commit" stage nor
3126 // sent a network request, so the page is eligible for BackForwardCache.
murakinonoka97a8f042024-01-10 09:17:073127 EXPECT_TRUE(sub_rfh->GetMainFrame()->IsInBackForwardCache());
3128 EXPECT_TRUE(sub_rfh->IsInBackForwardCache());
3129 }
3130 web_contents()->SetDelegate(shell());
3131
3132 // Wait until the subframe navigation is deferred.
3133 ASSERT_TRUE(
3134 subframe_navigation_manager.WaitForFirstYieldAfterDidStartNavigation());
3135 NavigationRequest* child_navigation = child_ftn->navigation_request();
3136 ASSERT_NE(child_navigation, nullptr);
3137 EXPECT_TRUE(child_navigation->IsDeferredForTesting());
3138 }
3139};
3140
murakinonokabb2a6c12024-01-18 05:49:053141class BackForwardCacheWithSubframeNavigationWithParamBrowserTest
3142 : public BackForwardCacheWithSubframeNavigationBrowserTest,
3143 public ::testing::WithParamInterface<SubframeNavigationType> {
3144 public:
3145 // Provides meaningful param names instead of /0 and /1.
3146 static std::string DescribeParams(
3147 const ::testing::TestParamInfo<ParamType>& info) {
3148 switch (info.param) {
3149 case SubframeNavigationType::WithoutURLLoader:
3150 return "WithoutURLLoader";
3151 case SubframeNavigationType::WithURLLoader:
3152 return "WithURLLoader";
3153 }
3154 }
3155};
3156
murakinonoka97a8f042024-01-10 09:17:073157// Confirm that BackForwardCache is blocked when there is only 1 navigation and
3158// it's pending commit.
murakinonokabb2a6c12024-01-18 05:49:053159IN_PROC_BROWSER_TEST_P(
3160 BackForwardCacheWithSubframeNavigationWithParamBrowserTest,
Jiacheng Guo02a0423a2024-06-20 02:03:563161 SubframeNavigationWithPendingCommitShouldPreventCache) {
murakinonoka97a8f042024-01-10 09:17:073162 const GURL main_url(embedded_test_server()->GetURL(
3163 "a.com", "/cross_site_iframe_factory.html?a(b)"));
3164 const GURL subframe_url = embedded_test_server()->GetURL(
3165 "b.com", "/cross_site_iframe_factory.html?b()");
3166 const GURL navigate_url(
3167 embedded_test_server()->GetURL("c.com", "/title1.html"));
murakinonokabb2a6c12024-01-18 05:49:053168 const GURL subframe_navigate_url =
3169 GetParam() == SubframeNavigationType::WithURLLoader
3170 ? embedded_test_server()->GetURL("b.com", "/title1.html")
3171 : GURL("about:blank");
murakinonoka97a8f042024-01-10 09:17:073172
3173 // Navigate to a page with a cross site iframe.
3174 ASSERT_TRUE(NavigateToURL(shell(), main_url));
Rakina Zata Amnic06ef3a42024-03-11 20:41:353175 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
3176
murakinonoka97a8f042024-01-10 09:17:073177 RenderFrameHostImplWrapper main_rfh(current_frame_host());
Jiacheng Guo4bdd0be2024-06-11 23:35:213178 FrameTreeNode* child_node = main_rfh.get()->child_at(0);
3179 RenderFrameHostImplWrapper sub_rfh(child_node->current_frame_host());
murakinonoka97a8f042024-01-10 09:17:073180
3181 // Pause subframe's navigation before `DidCommitNavigation`.
Jiacheng Guo4bdd0be2024-06-11 23:35:213182 NavigateSubframeAndPauseAtDidCommit(child_node, subframe_navigate_url);
murakinonoka97a8f042024-01-10 09:17:073183
3184 // Subframe navigation is ongoing, so `NavigateToURL` cannot be used since
3185 // this function waits for all frames including subframe to finish loading.
3186 ASSERT_TRUE(NavigateToURLFromRenderer(main_rfh.get(), navigate_url));
3187
3188 // Subframe navigation has reached the "pending commit" stage, so the page is
3189 // not eligible for BackForwardCache.
3190 EXPECT_TRUE(main_rfh.WaitUntilRenderFrameDeleted());
3191 EXPECT_TRUE(sub_rfh.WaitUntilRenderFrameDeleted());
3192
3193 // Navigate back.
3194 ASSERT_TRUE(HistoryGoBack(web_contents()));
3195 ExpectNotRestored({NotRestoredReason::kSubframeIsNavigating}, {}, {}, {}, {},
3196 FROM_HERE);
3197
3198 // Confirm that subframe's url didn't change.
3199 EXPECT_EQ(subframe_url, current_frame_host()->child_at(0)->current_url());
3200}
3201
3202// Confirm that BackForwardCache is blocked when there are 2 navigations, 1 not
3203// pending commit yet, and 1 pending commit.
3204IN_PROC_BROWSER_TEST_F(
murakinonokabb2a6c12024-01-18 05:49:053205 BackForwardCacheWithSubframeNavigationBrowserTest,
Jiacheng Guo02a0423a2024-06-20 02:03:563206 MultipleSubframeNavigationWithBeforeAndPendingCommitShouldPreventCache) {
murakinonoka97a8f042024-01-10 09:17:073207 // This test relies on the main frame and the iframe to live in different
3208 // processes. This allows one renderer process to proceed a navigation while
3209 // the other renderer process is busy executing its beforeunload handler.
3210 if (!AreAllSitesIsolatedForTesting()) {
3211 GTEST_SKIP() << "Site isolation is not enabled!";
3212 }
murakinonoka97a8f042024-01-10 09:17:073213 const GURL main_url(embedded_test_server()->GetURL(
3214 "a.com", "/cross_site_iframe_factory.html?a(b,c)"));
3215 const GURL subframe_b_url = embedded_test_server()->GetURL(
3216 "b.com", "/cross_site_iframe_factory.html?b()");
3217 const GURL subframe_c_url = embedded_test_server()->GetURL(
3218 "c.com", "/cross_site_iframe_factory.html?c()");
3219 const GURL navigate_url(
3220 embedded_test_server()->GetURL("d.com", "/title1.html"));
3221 const GURL subframe_navigate_url = GURL("about:blank");
3222
3223 // Navigate to a page with two cross site iframes.
3224 ASSERT_TRUE(NavigateToURL(shell(), main_url));
3225 RenderFrameHostImplWrapper main_rfh(current_frame_host());
3226 RenderFrameHostImplWrapper sub_rfh_b(
3227 main_rfh.get()->child_at(0)->current_frame_host());
3228 RenderFrameHostImplWrapper sub_rfh_c(
3229 main_rfh.get()->child_at(1)->current_frame_host());
3230
3231 {
3232 // The subframe_b itself does have a dialog-showing beforeunload handler.
3233 // Pause subframe_b's navigation when we get the confirmation dialog
3234 // triggered by beforeunload event.
3235 BeforeUnloadBlockingDelegate beforeunload_pauser(web_contents());
3236 NavigateSubframeAndPauseAtBeforeUnload(beforeunload_pauser, sub_rfh_b.get(),
3237 subframe_navigate_url,
3238 /*iframe_id=*/"child-0");
3239
3240 // Pause subframe_c's navigation before `DidCommitNavigation`.
Rakina Zata Amnic06ef3a42024-03-11 20:41:353241 NavigateSubframeAndPauseAtDidCommit(main_rfh.get()->child_at(1),
3242 subframe_navigate_url);
murakinonoka97a8f042024-01-10 09:17:073243
3244 // Subframe navigation is ongoing, so `NavigateToURL` cannot be used since
3245 // this function waits for all frames including subframe to finish loading.
3246 ASSERT_TRUE(NavigateToURLFromRenderer(main_rfh.get(), navigate_url));
3247
3248 // The subframe_c's navigation already started committing, so the page is
3249 // not eligible for BackForwardCache.
3250 EXPECT_TRUE(main_rfh.WaitUntilRenderFrameDeleted());
3251 EXPECT_TRUE(sub_rfh_b.WaitUntilRenderFrameDeleted());
3252 EXPECT_TRUE(sub_rfh_c.WaitUntilRenderFrameDeleted());
3253 }
3254 web_contents()->SetDelegate(shell());
3255
3256 // Navigate back.
3257 ASSERT_TRUE(HistoryGoBack(web_contents()));
3258 ExpectNotRestored({NotRestoredReason::kSubframeIsNavigating}, {}, {}, {}, {},
3259 FROM_HERE);
3260
3261 // Confirm that subframe's url didn't change.
3262 EXPECT_EQ(subframe_b_url, current_frame_host()->child_at(0)->current_url());
3263 EXPECT_EQ(subframe_c_url, current_frame_host()->child_at(1)->current_url());
3264}
3265
murakinonokabb2a6c12024-01-18 05:49:053266// Confirm that BackForwardCache is blocked when there are 2 navigations, 1 has
3267// not sent a network request yet, and 1 has already sent request.
murakinonoka97a8f042024-01-10 09:17:073268IN_PROC_BROWSER_TEST_F(
murakinonokabb2a6c12024-01-18 05:49:053269 BackForwardCacheWithSubframeNavigationBrowserTest,
3270 MultipleSubframeNavigationWithBeforeAndAfterSendingRequestShouldPreventCache) {
3271 // This test relies on the main frame and the iframe to live in different
3272 // processes. This allows one renderer process to proceed a navigation while
3273 // the other renderer process is busy executing its beforeunload handler.
3274 if (!AreAllSitesIsolatedForTesting()) {
3275 GTEST_SKIP() << "Site isolation is not enabled!";
3276 }
3277 const GURL main_url(embedded_test_server()->GetURL(
3278 "a.com", "/cross_site_iframe_factory.html?a(b,c)"));
3279 const GURL subframe_b_url = embedded_test_server()->GetURL(
3280 "b.com", "/cross_site_iframe_factory.html?b()");
3281 const GURL subframe_c_url = embedded_test_server()->GetURL(
3282 "c.com", "/cross_site_iframe_factory.html?c()");
3283 const GURL navigate_url(
3284 embedded_test_server()->GetURL("d.com", "/title1.html"));
3285 const GURL subframe_b_navigate_url(
3286 embedded_test_server()->GetURL("b.com", "/title1.html"));
3287 const GURL subframe_c_navigate_url(
3288 embedded_test_server()->GetURL("c.com", "/title1.html"));
3289
3290 // Navigate to a page with two cross site iframes.
3291 ASSERT_TRUE(NavigateToURL(shell(), main_url));
3292 RenderFrameHostImplWrapper main_rfh(current_frame_host());
3293 RenderFrameHostImplWrapper sub_rfh_b(
3294 main_rfh.get()->child_at(0)->current_frame_host());
3295 RenderFrameHostImplWrapper sub_rfh_c(
3296 main_rfh.get()->child_at(1)->current_frame_host());
3297
3298 // Pause a subframe_b navigation on `WillStartRequest` before sending a
3299 // network request.
3300 TestNavigationManager subframe_b_navigation_manager(web_contents(),
3301 subframe_b_navigate_url);
3302 ASSERT_TRUE(
3303 BeginNavigateToURLFromRenderer(sub_rfh_b.get(), subframe_b_navigate_url));
3304 ASSERT_TRUE(subframe_b_navigation_manager.WaitForRequestStart());
3305
3306 // Pause a subframe_c navigation on `WillProcessResponse` after sending a
3307 // network request.
3308 TestNavigationManager subframe_c_navigation_manager(web_contents(),
3309 subframe_c_navigate_url);
3310 ASSERT_TRUE(
3311 BeginNavigateToURLFromRenderer(sub_rfh_c.get(), subframe_c_navigate_url));
3312 ASSERT_TRUE(subframe_c_navigation_manager.WaitForResponse());
3313
3314 // Subframe navigation is ongoing, so `NavigateToURL` cannot be used since
3315 // this function waits for all frames including subframe to finish loading.
3316 ASSERT_TRUE(NavigateToURLFromRenderer(main_rfh.get(), navigate_url));
3317
3318 // The subframe_c's navigation has already sent a network request, so the page
3319 // is not eligible for BackForwardCache.
3320 EXPECT_TRUE(main_rfh.WaitUntilRenderFrameDeleted());
3321 EXPECT_TRUE(sub_rfh_b.WaitUntilRenderFrameDeleted());
3322 EXPECT_TRUE(sub_rfh_c.WaitUntilRenderFrameDeleted());
3323 EXPECT_TRUE(subframe_b_navigation_manager.WaitForNavigationFinished());
3324 EXPECT_TRUE(subframe_c_navigation_manager.WaitForNavigationFinished());
3325 EXPECT_FALSE(subframe_b_navigation_manager.was_committed());
3326 EXPECT_FALSE(subframe_c_navigation_manager.was_committed());
3327
3328 // Navigate back.
3329 ASSERT_TRUE(HistoryGoBack(web_contents()));
3330 ExpectNotRestored({NotRestoredReason::kSubframeIsNavigating}, {}, {}, {}, {},
3331 FROM_HERE);
3332
3333 // Confirm that subframe's url didn't change.
3334 EXPECT_EQ(subframe_b_url, current_frame_host()->child_at(0)->current_url());
3335 EXPECT_EQ(subframe_c_url, current_frame_host()->child_at(1)->current_url());
3336}
3337
3338// Confirm that subframe navigation which needs url loader that has already sent
3339// a network request should block BackForwardCache.
3340IN_PROC_BROWSER_TEST_F(
3341 BackForwardCacheWithSubframeNavigationBrowserTest,
3342 SubframeNavigationWithUrlLoaderAfterSendingRequestShouldPreventCache) {
murakinonoka97a8f042024-01-10 09:17:073343 const GURL main_url(embedded_test_server()->GetURL(
3344 "a.com", "/cross_site_iframe_factory.html?a(b)"));
3345 const GURL subframe_url = embedded_test_server()->GetURL(
3346 "b.com", "/cross_site_iframe_factory.html?b()");
3347 const GURL navigate_url(
3348 embedded_test_server()->GetURL("c.com", "/title1.html"));
3349 const GURL subframe_navigate_url(
3350 embedded_test_server()->GetURL("b.com", "/title2.html"));
3351
3352 // Navigate to a page with a cross site iframe.
3353 ASSERT_TRUE(NavigateToURL(shell(), main_url));
3354 RenderFrameHostImplWrapper main_rfh(current_frame_host());
3355 RenderFrameHostImplWrapper sub_rfh(
3356 main_rfh.get()->child_at(0)->current_frame_host());
murakinonoka97a8f042024-01-10 09:17:073357 TestNavigationManager subframe_navigation_manager(web_contents(),
3358 subframe_navigate_url);
3359 ASSERT_TRUE(
3360 BeginNavigateToURLFromRenderer(sub_rfh.get(), subframe_navigate_url));
murakinonokabb2a6c12024-01-18 05:49:053361
3362 // Pause the subframe navigation on `WillProcessResponse`.
3363 ASSERT_TRUE(subframe_navigation_manager.WaitForResponse());
murakinonoka97a8f042024-01-10 09:17:073364
3365 // Subframe navigation is ongoing, so `NavigateToURL` cannot be used since
3366 // this function waits for all frames including subframe to finish loading.
3367 ASSERT_TRUE(NavigateToURLFromRenderer(main_rfh.get(), navigate_url));
3368
murakinonokabb2a6c12024-01-18 05:49:053369 // Subframe navigation has already sent a network request, so the page is not
3370 // eligible for BackForwardCache.
murakinonoka97a8f042024-01-10 09:17:073371 EXPECT_TRUE(main_rfh.WaitUntilRenderFrameDeleted());
3372 EXPECT_TRUE(sub_rfh.WaitUntilRenderFrameDeleted());
3373 EXPECT_FALSE(subframe_navigation_manager.was_committed());
3374
3375 // Navigate back.
3376 ASSERT_TRUE(HistoryGoBack(web_contents()));
3377 ExpectNotRestored({NotRestoredReason::kSubframeIsNavigating}, {}, {}, {}, {},
3378 FROM_HERE);
3379
3380 // Confirm that subframe's url didn't change.
3381 EXPECT_EQ(subframe_url, current_frame_host()->child_at(0)->current_url());
3382}
3383
murakinonokabb2a6c12024-01-18 05:49:053384// Confirm that subframe navigation which needs url loader that hasn't sent a
3385// network request should not block BackForwardCache.
3386IN_PROC_BROWSER_TEST_F(
3387 BackForwardCacheWithSubframeNavigationBrowserTest,
3388 SubframeNavigationWithUrlLoaderBeforeSendingRequestShouldNotPreventCache) {
3389 // This test relies on the main frame and the iframe to live in different
3390 // processes. This allows one renderer process to proceed a navigation while
3391 // the other renderer process is busy executing its beforeunload handler.
3392 if (!AreAllSitesIsolatedForTesting()) {
3393 GTEST_SKIP() << "Site isolation is not enabled!";
3394 }
3395 const GURL main_url(embedded_test_server()->GetURL(
3396 "a.com", "/cross_site_iframe_factory.html?a(b)"));
3397 const GURL subframe_url = embedded_test_server()->GetURL(
3398 "b.com", "/cross_site_iframe_factory.html?b()");
3399 const GURL navigate_url(
3400 embedded_test_server()->GetURL("c.com", "/title1.html"));
3401 const GURL subframe_navigate_url(
3402 embedded_test_server()->GetURL("b.com", "/title1.html"));
3403
3404 // Navigate to a page with a cross site iframe.
3405 ASSERT_TRUE(NavigateToURL(shell(), main_url));
3406 RenderFrameHostImplWrapper main_rfh(current_frame_host());
3407 RenderFrameHostImplWrapper sub_rfh(
3408 main_rfh.get()->child_at(0)->current_frame_host());
3409
3410 // Put a page which has a subframe with a URLLoader navigation which hasn't
3411 // sent a network request into BackForwardCache. The iframe itself
3412 // does have a dialog-showing beforeunload handler.
3413 TestNavigationManager subframe_navigation_manager(web_contents(),
3414 subframe_navigate_url);
3415 BFCachePageWithSubframeNavigationBeforeDidStartNavigation(
3416 navigate_url, subframe_navigate_url, sub_rfh, subframe_navigation_manager,
3417 /*iframe_id=*/"child-0");
3418
3419 // Navigate back.
3420 TestNavigationObserver back_load_observer(shell()->web_contents());
3421 web_contents()->GetController().GoBack();
3422 back_load_observer.WaitForNavigationFinished();
3423 ASSERT_FALSE(main_rfh->IsInBackForwardCache());
3424
3425 // Wait until the resumed subframe navigation finishes.
3426 EXPECT_TRUE(subframe_navigation_manager.WaitForNavigationFinished());
3427 EXPECT_TRUE(subframe_navigation_manager.was_successful());
3428 EXPECT_EQ(subframe_navigate_url,
3429 current_frame_host()->child_at(0)->current_url());
3430}
3431
murakinonoka97a8f042024-01-10 09:17:073432// Confirm that subframe no-url loader navigation (e.g., about:blank) in
3433// bfcached page is deferred and then resumed when the page is navigated back.
3434IN_PROC_BROWSER_TEST_F(
murakinonokabb2a6c12024-01-18 05:49:053435 BackForwardCacheWithSubframeNavigationBrowserTest,
murakinonoka97a8f042024-01-10 09:17:073436 SubframeNavigationWithoutUrlLoaderBeforeCommitShouldNotPreventCache) {
3437 // This test relies on the main frame and the iframe to live in different
3438 // processes. This allows one renderer process to proceed a navigation while
3439 // the other renderer process is busy executing its beforeunload handler.
3440 if (!AreAllSitesIsolatedForTesting()) {
3441 GTEST_SKIP() << "Site isolation is not enabled!";
3442 }
murakinonoka97a8f042024-01-10 09:17:073443 const GURL main_url(embedded_test_server()->GetURL(
3444 "a.com", "/cross_site_iframe_factory.html?a(b)"));
3445 const GURL navigate_url(
3446 embedded_test_server()->GetURL("c.com", "/title1.html"));
3447 const GURL subframe_navigate_url = GURL("about:blank");
3448
3449 // Navigate to a page with a cross site iframe.
3450 ASSERT_TRUE(NavigateToURL(shell(), main_url));
3451 RenderFrameHostImplWrapper main_rfh(current_frame_host());
3452 RenderFrameHostImplWrapper sub_rfh(
3453 main_rfh.get()->child_at(0)->current_frame_host());
3454
3455 // Put a page which has a subframe with a no-URLLoader navigation which hasn't
3456 // reached the "pending commit" stage into BackForwardCache. The iframe itself
3457 // does have a dialog-showing beforeunload handler.
3458 TestNavigationManager subframe_navigation_manager(web_contents(),
3459 subframe_navigate_url);
murakinonokabb2a6c12024-01-18 05:49:053460 BFCachePageWithSubframeNavigationBeforeDidStartNavigation(
3461 navigate_url, subframe_navigate_url, sub_rfh, subframe_navigation_manager,
murakinonoka97a8f042024-01-10 09:17:073462 /*iframe_id=*/"child-0");
3463
3464 // Navigate back.
3465 ASSERT_TRUE(HistoryGoBack(web_contents()));
3466 ASSERT_FALSE(main_rfh->IsInBackForwardCache());
3467
3468 // Confirm the deferred navigation was resumed and subframe's url changed.
3469 EXPECT_TRUE(subframe_navigation_manager.WaitForNavigationFinished());
3470 EXPECT_TRUE(subframe_navigation_manager.was_successful());
3471 EXPECT_EQ(subframe_navigate_url,
3472 current_frame_host()->child_at(0)->current_url());
3473}
3474
3475// Confirm that we don't resume a subframe navigation when an unrelated BFCached
3476// page gets restored.
murakinonokabb2a6c12024-01-18 05:49:053477IN_PROC_BROWSER_TEST_P(
3478 BackForwardCacheWithSubframeNavigationWithParamBrowserTest,
murakinonoka97a8f042024-01-10 09:17:073479 SubframeNavigationShouldNotBeResumedWhenUnrelatedPageRestored) {
3480 // This test relies on the main frame and the iframe to live in different
3481 // processes. This allows one renderer process to proceed a navigation while
3482 // the other renderer process is busy executing its beforeunload handler.
3483 if (!AreAllSitesIsolatedForTesting()) {
3484 GTEST_SKIP() << "Site isolation is not enabled!";
3485 }
murakinonoka97a8f042024-01-10 09:17:073486 const GURL main_url_a(embedded_test_server()->GetURL(
3487 "a.com", "/cross_site_iframe_factory.html?a(b)"));
3488 const GURL navigate_url_c(
3489 embedded_test_server()->GetURL("c.com", "/title1.html"));
3490 const GURL navigate_url_d(
3491 embedded_test_server()->GetURL("d.com", "/title1.html"));
murakinonokabb2a6c12024-01-18 05:49:053492 const GURL subframe_navigate_url =
3493 GetParam() == SubframeNavigationType::WithURLLoader
3494 ? embedded_test_server()->GetURL("b.com", "/title1.html")
3495 : GURL("about:blank");
murakinonoka97a8f042024-01-10 09:17:073496
3497 // Navigate to a page with a cross site iframe.
3498 ASSERT_TRUE(NavigateToURL(shell(), main_url_a));
3499 RenderFrameHostImplWrapper main_rfh_a(current_frame_host());
3500 RenderFrameHostImplWrapper sub_rfh_b(
3501 main_rfh_a.get()->child_at(0)->current_frame_host());
3502
murakinonokabb2a6c12024-01-18 05:49:053503 // Put a page which has a subframe with a navigation which hasn't reached the
3504 // "pending commit" stage or sent a network request into BackForwardCache.
murakinonoka97a8f042024-01-10 09:17:073505 TestNavigationManager subframe_navigation_manager(web_contents(),
3506 subframe_navigate_url);
murakinonokabb2a6c12024-01-18 05:49:053507 BFCachePageWithSubframeNavigationBeforeDidStartNavigation(
3508 navigate_url_c, subframe_navigate_url, sub_rfh_b,
3509 subframe_navigation_manager,
murakinonoka97a8f042024-01-10 09:17:073510 /*iframe_id=*/"child-0");
3511
3512 // Navigate away.
3513 // Currently, `main_rfh_a` is in BFCache and we are on `navigate_url_c`. Then,
3514 // we will navigate to `navigate_url_d` which will put `main_rfh_c` in
3515 // BFCache.
3516 RenderFrameHostImplWrapper main_rfh_c(current_frame_host());
3517 ASSERT_TRUE(NavigateToURL(shell(), navigate_url_d));
3518 ASSERT_TRUE(main_rfh_c->IsInBackForwardCache());
3519
3520 // Navigate back to `main_rfh_c` and restore that from BFCache, while
3521 // `main_rfh_a` is still in BFCache.
3522 ASSERT_TRUE(HistoryGoBack(web_contents()));
3523 ASSERT_EQ(main_rfh_c.get(), current_frame_host());
3524 ASSERT_TRUE(main_rfh_a->IsInBackForwardCache());
3525
3526 // Confirm the subframe's deferred navigation is not committed.
3527 EXPECT_FALSE(subframe_navigation_manager.was_committed());
3528
3529 // Navigate back to `main_rfh_a`.
murakinonokabb2a6c12024-01-18 05:49:053530 TestNavigationObserver back_load_observer(shell()->web_contents());
3531 web_contents()->GetController().GoBack();
3532 back_load_observer.WaitForNavigationFinished();
murakinonoka97a8f042024-01-10 09:17:073533 ASSERT_FALSE(main_rfh_a->IsInBackForwardCache());
3534
3535 // Confirm the deferred navigation was resumed and subframe's url changed.
3536 EXPECT_TRUE(subframe_navigation_manager.WaitForNavigationFinished());
3537 EXPECT_TRUE(subframe_navigation_manager.was_successful());
3538 EXPECT_EQ(subframe_navigate_url,
3539 current_frame_host()->child_at(0)->current_url());
3540}
3541
murakinonokabb2a6c12024-01-18 05:49:053542// Evict the bfcached page which has a subframe with a deferred navigation and
3543// confirm the subframe'url didn't change when the page is navigated back.
3544IN_PROC_BROWSER_TEST_P(
3545 BackForwardCacheWithSubframeNavigationWithParamBrowserTest,
3546 EvictBFCachedPageWithDeferredSubframeNavigationBeforeCommit) {
murakinonoka97a8f042024-01-10 09:17:073547 // This test relies on the main frame and the iframe to live in different
3548 // processes. This allows one renderer process to proceed a navigation while
3549 // the other renderer process is busy executing its beforeunload handler.
3550 if (!AreAllSitesIsolatedForTesting()) {
3551 GTEST_SKIP() << "Site isolation is not enabled!";
3552 }
murakinonoka97a8f042024-01-10 09:17:073553 const GURL main_url(embedded_test_server()->GetURL(
3554 "a.com", "/cross_site_iframe_factory.html?a(b)"));
3555 const GURL subframe_url = embedded_test_server()->GetURL(
3556 "b.com", "/cross_site_iframe_factory.html?b()");
3557 const GURL navigate_url(
3558 embedded_test_server()->GetURL("c.com", "/title1.html"));
murakinonokabb2a6c12024-01-18 05:49:053559 const GURL subframe_navigate_url =
3560 GetParam() == SubframeNavigationType::WithURLLoader
3561 ? embedded_test_server()->GetURL("b.com", "/title1.html")
3562 : GURL("about:blank");
murakinonoka97a8f042024-01-10 09:17:073563
3564 // Navigate to a page with a cross site iframe.
3565 ASSERT_TRUE(NavigateToURL(shell(), main_url));
3566 RenderFrameHostImplWrapper main_rfh(current_frame_host());
3567 RenderFrameHostImplWrapper sub_rfh(
3568 main_rfh.get()->child_at(0)->current_frame_host());
3569
murakinonokabb2a6c12024-01-18 05:49:053570 // Put a page which has a subframe with a navigation which hasn't reached the
3571 // "pending commit" stage or sent a network request into BackForwardCache. The
3572 // iframe itself does have a dialog-showing beforeunload handler.
murakinonoka97a8f042024-01-10 09:17:073573 TestNavigationManager subframe_navigation_manager(web_contents(),
3574 subframe_navigate_url);
murakinonokabb2a6c12024-01-18 05:49:053575 BFCachePageWithSubframeNavigationBeforeDidStartNavigation(
3576 navigate_url, subframe_navigate_url, sub_rfh, subframe_navigation_manager,
murakinonoka97a8f042024-01-10 09:17:073577 /*iframe_id=*/"child-0");
3578
3579 // Flush the cache and evict the previously BFCached page.
3580 web_contents()->GetController().GetBackForwardCache().Flush();
3581 ASSERT_TRUE(main_rfh.WaitUntilRenderFrameDeleted());
3582 ASSERT_TRUE(sub_rfh.WaitUntilRenderFrameDeleted());
3583
3584 // Confirm the subframe's deferred navigation has finished and was not
3585 // committed.
3586 EXPECT_TRUE(subframe_navigation_manager.WaitForNavigationFinished());
3587 EXPECT_FALSE(subframe_navigation_manager.was_committed());
3588
3589 // Navigate back.
3590 ASSERT_TRUE(HistoryGoBack(web_contents()));
3591
3592 // Confirm that subframe's url didn't change.
3593 EXPECT_EQ(subframe_url, current_frame_host()->child_at(0)->current_url());
3594}
3595
murakinonokabb2a6c12024-01-18 05:49:053596INSTANTIATE_TEST_SUITE_P(
3597 All,
3598 BackForwardCacheWithSubframeNavigationWithParamBrowserTest,
3599 ::testing::Values(SubframeNavigationType::WithoutURLLoader,
3600 SubframeNavigationType::WithURLLoader),
3601 &BackForwardCacheWithSubframeNavigationWithParamBrowserTest::
3602 DescribeParams);
3603
Gyuyoung Kim940115f2021-11-05 02:25:353604class BackForwardCacheFencedFrameBrowserTest
3605 : public BackForwardCacheBrowserTest {
3606 public:
3607 BackForwardCacheFencedFrameBrowserTest() = default;
3608 ~BackForwardCacheFencedFrameBrowserTest() override = default;
3609 BackForwardCacheFencedFrameBrowserTest(
3610 const BackForwardCacheFencedFrameBrowserTest&) = delete;
3611
3612 BackForwardCacheFencedFrameBrowserTest& operator=(
3613 const BackForwardCacheFencedFrameBrowserTest&) = delete;
3614
3615 void SetUpCommandLine(base::CommandLine* command_line) override {
3616 BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
3617 fenced_frame_helper_ = std::make_unique<test::FencedFrameTestHelper>();
3618 }
3619
3620 test::FencedFrameTestHelper& fenced_frame_test_helper() {
3621 return *fenced_frame_helper_;
3622 }
3623
3624 private:
3625 std::unique_ptr<test::FencedFrameTestHelper> fenced_frame_helper_;
3626};
3627
3628IN_PROC_BROWSER_TEST_F(BackForwardCacheFencedFrameBrowserTest,
3629 FencedFramePageNotStoredInBackForwardCache) {
3630 ASSERT_TRUE(embedded_test_server()->Start());
3631 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
Dominic Farolino667161a92021-11-20 19:29:023632 GURL url_b(
3633 embedded_test_server()->GetURL("b.com", "/fenced_frames/title1.html"));
3634 GURL url_c(
3635 embedded_test_server()->GetURL("c.com", "/fenced_frames/title1.html"));
Gyuyoung Kim940115f2021-11-05 02:25:353636
3637 // 1) Navigate to A.
3638 EXPECT_TRUE(NavigateToURL(shell(), url_a));
3639
3640 // 2) Create a fenced frame.
3641 content::RenderFrameHostImpl* fenced_frame_host =
3642 static_cast<content::RenderFrameHostImpl*>(
3643 fenced_frame_test_helper().CreateFencedFrame(
Dave Tapuska327c06c92022-06-13 20:31:513644 web_contents()->GetPrimaryMainFrame(), url_b));
Gyuyoung Kim940115f2021-11-05 02:25:353645 RenderFrameHostWrapper fenced_frame_host_wrapper(fenced_frame_host);
3646
3647 // 3) Navigate to C on the fenced frame host.
3648 fenced_frame_test_helper().NavigateFrameInFencedFrameTree(fenced_frame_host,
3649 url_c);
3650 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
3651
3652 if (!fenced_frame_host_wrapper.IsRenderFrameDeleted())
3653 EXPECT_FALSE(fenced_frame_host->IsInBackForwardCache());
3654}
3655
Yuzu Saijocaba5be2021-11-30 11:35:553656IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
3657 RendererInitiatedNavigateToSameUrl) {
3658 ASSERT_TRUE(embedded_test_server()->Start());
3659 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
3660 GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
3661
3662 // 1) Navigate to A.
3663 ASSERT_TRUE(NavigateToURL(shell(), url_a));
3664 RenderFrameHostImplWrapper rfh_a(current_frame_host());
3665
3666 // 2) Navigate to B.
3667 ASSERT_TRUE(NavigateToURL(shell(), url_b));
3668 RenderFrameHostImplWrapper rfh_b(current_frame_host());
3669
3670 // 3) Navigate to B again, renderer initiated.
3671 ASSERT_TRUE(NavigateToURLFromRenderer(rfh_b.get(), url_b));
Rakina Zata Amni4cab0822023-10-26 12:28:243672 RenderFrameHostImplWrapper rfh_b2(current_frame_host());
3673
3674 // This is treated as replacement, and the previous B page did not get into
3675 // back/forward cache.
3676 if (ShouldCreateNewHostForAllFrames()) {
3677 EXPECT_TRUE(rfh_b.WaitUntilRenderFrameDeleted());
3678 } else {
3679 EXPECT_FALSE(rfh_b->IsInBackForwardCache());
3680 EXPECT_EQ(rfh_b.get(), rfh_b2.get());
3681 }
Yuzu Saijocaba5be2021-11-30 11:35:553682
3683 // 4) Go back. Make sure we go back to A instead of B and restore from
3684 // bfcache.
3685 ASSERT_TRUE(HistoryGoBack(shell()->web_contents()));
3686 EXPECT_EQ(current_frame_host(), rfh_a.get());
Rakina Zata Amni4cab0822023-10-26 12:28:243687 EXPECT_TRUE(rfh_b2.get()->IsInBackForwardCache());
Yuzu Saijocaba5be2021-11-30 11:35:553688 ExpectRestored(FROM_HERE);
3689
3690 // 5) Go forward and restore from bfcache.
3691 ASSERT_TRUE(HistoryGoForward(shell()->web_contents()));
Rakina Zata Amni4cab0822023-10-26 12:28:243692 EXPECT_EQ(current_frame_host(), rfh_b2.get());
Yuzu Saijocaba5be2021-11-30 11:35:553693 ExpectRestored(FROM_HERE);
3694}
3695
Fergal Dalyf5a50ab2021-11-09 05:36:183696// BEFORE ADDING A NEW TEST HERE
3697// Read the note at the top about the other files you could add it to.
Arthur Sonzogni620cec62018-12-13 13:08:573698} // namespace content