Avi Drissman | 4e1b7bc3 | 2022-09-15 14:03:50 | [diff] [blame] | 1 | // Copyright 2021 The Chromium Authors |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #ifndef CONTENT_BROWSER_BACK_FORWARD_CACHE_BROWSERTEST_H_ |
| 6 | #define CONTENT_BROWSER_BACK_FORWARD_CACHE_BROWSERTEST_H_ |
| 7 | |
| 8 | #include <memory> |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 9 | #include <optional> |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 10 | |
Fergal Daly | 5495b0e | 2021-11-19 02:03:28 | [diff] [blame] | 11 | #include "base/compiler_specific.h" |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 12 | #include "base/feature_list.h" |
| 13 | #include "base/hash/hash.h" |
| 14 | #include "base/test/scoped_feature_list.h" |
Fergal Daly | 432aa7c | 2022-06-14 07:30:54 | [diff] [blame] | 15 | #include "base/test/scoped_logging_settings.h" |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 16 | #include "components/ukm/test_ukm_recorder.h" |
Ming-Ying Chung | ee9b9ea | 2022-08-15 08:24:25 | [diff] [blame] | 17 | #include "content/browser/back_forward_cache_test_util.h" |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 18 | #include "content/browser/renderer_host/page_lifecycle_state_manager.h" |
| 19 | #include "content/browser/renderer_host/render_frame_host_impl.h" |
| 20 | #include "content/browser/renderer_host/render_frame_host_manager.h" |
| 21 | #include "content/public/browser/render_frame_host.h" |
| 22 | #include "content/public/browser/web_contents_observer.h" |
| 23 | #include "content/public/test/content_browser_test.h" |
| 24 | #include "content/public/test/content_mock_cert_verifier.h" |
| 25 | #include "content/test/content_browser_test_utils_internal.h" |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 26 | #include "testing/gmock/include/gmock/gmock.h" |
Dave Tapuska | ef8b158 | 2024-10-23 21:49:38 | [diff] [blame] | 27 | #include "third_party/blink/public/mojom/back_forward_cache_not_restored_reasons.mojom.h" |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 28 | |
| 29 | namespace content { |
| 30 | |
Yuzu Saijo | dcabe56 | 2022-04-25 06:06:39 | [diff] [blame] | 31 | using NotRestoredReasons = |
| 32 | BackForwardCacheCanStoreDocumentResult::NotRestoredReasons; |
Yuzu Saijo | c347807 | 2022-03-24 06:15:26 | [diff] [blame] | 33 | using NotRestoredReason = BackForwardCacheMetrics::NotRestoredReason; |
| 34 | |
Fergal Daly | 25b5180 | 2022-11-28 06:01:24 | [diff] [blame] | 35 | using ReasonsMatcher = testing::Matcher< |
| 36 | const blink::mojom::BackForwardCacheNotRestoredReasonsPtr&>; |
| 37 | using SameOriginMatcher = testing::Matcher< |
| 38 | const blink::mojom::SameOriginBfcacheNotRestoredDetailsPtr&>; |
Kurumi Muto | 576c0a0 | 2024-02-08 04:17:32 | [diff] [blame] | 39 | using BlockingDetailsReasonsMatcher = |
| 40 | testing::Matcher<const blink::mojom::BFCacheBlockingDetailedReasonPtr&>; |
Kurumi Muto | 0cf1280 | 2024-02-21 08:36:42 | [diff] [blame] | 41 | using SourceLocationMatcher = |
| 42 | testing::Matcher<const blink::mojom::ScriptSourceLocationPtr&>; |
rubberyuzu | 15d29d3 | 2023-07-28 05:22:24 | [diff] [blame] | 43 | using BlockingDetailsMatcher = |
| 44 | testing::Matcher<const blink::mojom::BlockingDetailsPtr&>; |
Fergal Daly | 25b5180 | 2022-11-28 06:01:24 | [diff] [blame] | 45 | |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 46 | // Match RenderFrameHostImpl* that are in the BackForwardCache. |
| 47 | MATCHER(InBackForwardCache, "") { |
| 48 | return arg->IsInBackForwardCache(); |
| 49 | } |
| 50 | |
Takashi Toyoshima | e40aa1e | 2022-03-30 08:32:16 | [diff] [blame] | 51 | // Match RenderFrameDeletedObserver* which observed deletion of the RenderFrame. |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 52 | MATCHER(Deleted, "") { |
| 53 | return arg->deleted(); |
| 54 | } |
| 55 | |
| 56 | // Helper function to pass an initializer list to the EXPECT_THAT macro. This is |
| 57 | // indeed the identity function. |
| 58 | std::initializer_list<RenderFrameHostImpl*> Elements( |
| 59 | std::initializer_list<RenderFrameHostImpl*> t); |
| 60 | |
Fergal Daly | 7e0c67bd | 2023-02-18 02:49:03 | [diff] [blame] | 61 | enum class TestFrameType { |
| 62 | kMainFrame, |
| 63 | kSubFrame, |
| 64 | kSubFrameOfSubframe, |
| 65 | }; |
| 66 | |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 67 | // Test about the BackForwardCache. |
Yuzu Saijo | c347807 | 2022-03-24 06:15:26 | [diff] [blame] | 68 | class BackForwardCacheBrowserTest |
| 69 | : public ContentBrowserTest, |
| 70 | public WebContentsObserver, |
Ming-Ying Chung | ee9b9ea | 2022-08-15 08:24:25 | [diff] [blame] | 71 | public BackForwardCacheMetrics::TestObserver, |
| 72 | public BackForwardCacheMetricsTestMatcher { |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 73 | public: |
| 74 | BackForwardCacheBrowserTest(); |
| 75 | ~BackForwardCacheBrowserTest() override; |
| 76 | |
Yuzu Saijo | c347807 | 2022-03-24 06:15:26 | [diff] [blame] | 77 | // TestObserver: |
| 78 | void NotifyNotRestoredReasons( |
| 79 | std::unique_ptr<BackForwardCacheCanStoreTreeResult> tree_result) override; |
| 80 | |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 81 | protected: |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 82 | void SetUpCommandLine(base::CommandLine* command_line) override; |
| 83 | |
| 84 | void SetUpInProcessBrowserTestFixture() override; |
| 85 | |
| 86 | void TearDownInProcessBrowserTestFixture() override; |
| 87 | |
| 88 | void SetupFeaturesAndParameters(); |
| 89 | |
Fergal Daly | 7e9b4caa | 2025-07-17 13:56:09 | [diff] [blame] | 90 | // Enabled and disable only takes effect once per feature and each param can |
| 91 | // only be set once. This allows subclasses to set values and then call |
| 92 | // `SetUpCommandLine` and not have them overridden. |
Daniel Cheng | c9ead4b | 2022-10-05 03:54:29 | [diff] [blame] | 93 | void EnableFeatureAndSetParams(const base::Feature& feature, |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 94 | std::string param_name, |
| 95 | std::string param_value); |
Daniel Cheng | c9ead4b | 2022-10-05 03:54:29 | [diff] [blame] | 96 | void DisableFeature(const base::Feature& feature); |
Fergal Daly | b125811 | 2025-07-15 03:00:51 | [diff] [blame] | 97 | // Convenience method for setting up cache-sizes. |
| 98 | void EnableCacheSize(std::optional<int> cache_size, |
| 99 | std::optional<int> foreground_cache_size); |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 100 | |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 101 | void SetUpOnMainThread() override; |
| 102 | |
| 103 | void TearDownOnMainThread() override; |
| 104 | |
| 105 | WebContentsImpl* web_contents() const; |
| 106 | |
| 107 | RenderFrameHostImpl* current_frame_host(); |
| 108 | |
| 109 | RenderFrameHostManager* render_frame_host_manager(); |
| 110 | |
| 111 | std::string DepictFrameTree(FrameTreeNode* node); |
| 112 | |
Ramon Cano Aparicio | 6797d4e | 2025-01-10 16:09:02 | [diff] [blame] | 113 | bool HistogramContainsIntValue(base::HistogramBase::Sample32 sample, |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 114 | std::vector<base::Bucket> histogram_values); |
| 115 | |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 116 | void EvictByJavaScript(RenderFrameHostImpl* rfh); |
| 117 | |
| 118 | void StartRecordingEvents(RenderFrameHostImpl* rfh); |
| 119 | |
| 120 | void MatchEventList(RenderFrameHostImpl* rfh, |
Matt Menke | 0500b4f4 | 2022-06-28 17:06:58 | [diff] [blame] | 121 | base::Value list, |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 122 | base::Location location = base::Location::Current()); |
| 123 | |
| 124 | // Creates a minimal HTTPS server, accessible through https_server(). |
| 125 | // Returns a pointer to the server. |
| 126 | net::EmbeddedTestServer* CreateHttpsServer(); |
| 127 | |
| 128 | net::EmbeddedTestServer* https_server(); |
| 129 | |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 130 | // Do not fail this test if a message from a renderer arrives at the browser |
| 131 | // for a cached page. |
| 132 | void DoNotFailForUnexpectedMessagesWhileCached(); |
| 133 | |
| 134 | // Navigates to a page at |page_url| with an img element with src set to |
| 135 | // "image.png". |
| 136 | RenderFrameHostImpl* NavigateToPageWithImage(const GURL& page_url); |
| 137 | |
| 138 | void AcquireKeyboardLock(RenderFrameHostImpl* rfh); |
| 139 | |
| 140 | void ReleaseKeyboardLock(RenderFrameHostImpl* rfh); |
| 141 | |
Fergal Daly | 7a05b426 | 2022-03-15 09:18:40 | [diff] [blame] | 142 | // Start a navigation to |url| but block it on an error. If |history_offset| |
| 143 | // is not 0, then the navigation will be a history navigation and this will |
| 144 | // assert that the URL after navigation is |url|. |
| 145 | void NavigateAndBlock(GURL url, int history_offset); |
| 146 | |
Yuzu Saijo | c347807 | 2022-03-24 06:15:26 | [diff] [blame] | 147 | static testing::Matcher<BackForwardCacheCanStoreDocumentResult> |
Yuzu Saijo | dcabe56 | 2022-04-25 06:06:39 | [diff] [blame] | 148 | MatchesDocumentResult(testing::Matcher<NotRestoredReasons> not_stored, |
Yuzu Saijo | c347807 | 2022-03-24 06:15:26 | [diff] [blame] | 149 | BlockListedFeatures block_listed); |
| 150 | |
Yuzu Saijo | e12e7ae | 2022-08-22 05:56:55 | [diff] [blame] | 151 | ReasonsMatcher MatchesNotRestoredReasons( |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 152 | const std::optional<testing::Matcher<std::string>>& id, |
| 153 | const std::optional<testing::Matcher<std::string>>& name, |
| 154 | const std::optional<testing::Matcher<std::string>>& src, |
Kurumi Muto | 576c0a0 | 2024-02-08 04:17:32 | [diff] [blame] | 155 | const std::vector<BlockingDetailsReasonsMatcher>& reasons, |
Arthur Sonzogni | c686e8f | 2024-01-11 08:36:37 | [diff] [blame] | 156 | const std::optional<SameOriginMatcher>& same_origin_details); |
Kurumi Muto | 576c0a0 | 2024-02-08 04:17:32 | [diff] [blame] | 157 | |
Yuzu Saijo | e12e7ae | 2022-08-22 05:56:55 | [diff] [blame] | 158 | SameOriginMatcher MatchesSameOriginDetails( |
Luke Gu | 990c227b | 2024-03-07 00:36:38 | [diff] [blame] | 159 | const testing::Matcher<GURL>& url, |
Yuzu Saijo | e12e7ae | 2022-08-22 05:56:55 | [diff] [blame] | 160 | const std::vector<ReasonsMatcher>& children); |
| 161 | |
Kurumi Muto | 576c0a0 | 2024-02-08 04:17:32 | [diff] [blame] | 162 | // Used in tests that ensure source location is sent to the renderer side from |
| 163 | // the browser one |
| 164 | BlockingDetailsReasonsMatcher MatchesDetailedReason( |
| 165 | const testing::Matcher<std::string>& name, |
Kurumi Muto | 0cf1280 | 2024-02-21 08:36:42 | [diff] [blame] | 166 | const std::optional<SourceLocationMatcher>& source); |
Kurumi Muto | 576c0a0 | 2024-02-08 04:17:32 | [diff] [blame] | 167 | |
| 168 | // Used in tests that ensure source location is sent to the browser side from |
| 169 | // the renderer one. |
rubberyuzu | 15d29d3 | 2023-07-28 05:22:24 | [diff] [blame] | 170 | BlockingDetailsMatcher MatchesBlockingDetails( |
Kurumi Muto | 0cf1280 | 2024-02-21 08:36:42 | [diff] [blame] | 171 | const std::optional<SourceLocationMatcher>& source); |
| 172 | |
| 173 | SourceLocationMatcher MatchesSourceLocation( |
Luke Gu | d2d21b84 | 2024-03-19 09:49:41 | [diff] [blame] | 174 | const testing::Matcher<GURL>& url, |
Kurumi Muto | 0cf1280 | 2024-02-21 08:36:42 | [diff] [blame] | 175 | const testing::Matcher<std::string>& function_name, |
| 176 | const testing::Matcher<uint64_t>& line_number, |
| 177 | const testing::Matcher<uint64_t>& column_number); |
rubberyuzu | 15d29d3 | 2023-07-28 05:22:24 | [diff] [blame] | 178 | |
Yuzu Saijo | c347807 | 2022-03-24 06:15:26 | [diff] [blame] | 179 | // Access the tree result of NotRestoredReason for the last main frame |
| 180 | // navigation. |
| 181 | BackForwardCacheCanStoreTreeResult* GetTreeResult() { |
| 182 | return tree_result_.get(); |
| 183 | } |
| 184 | |
Yuzu Saijo | 7b7b9a7 | 2022-04-20 05:12:08 | [diff] [blame] | 185 | void InstallUnloadHandlerOnMainFrame(); |
| 186 | void InstallUnloadHandlerOnSubFrame(); |
| 187 | EvalJsResult GetUnloadRunCount(); |
| 188 | |
Fergal Daly | 9909e09 | 2022-07-06 14:09:34 | [diff] [blame] | 189 | // Adds a blocklisted feature to the document to prevent caching. Currently |
| 190 | // this means adding a plugin. We expect that plugins will never become |
| 191 | // cacheable, so this should be stable (at least until plugins cease to |
| 192 | // exist). If you need the feature to be sticky, then |
| 193 | // `RenderFrameHostImpl::UseDummyStickyBackForwardCacheDisablingFeatureForTesting` |
| 194 | // provides that. |
| 195 | [[nodiscard]] bool AddBlocklistedFeature(RenderFrameHost* rfh); |
| 196 | // Check that the document was not restored for the reason added by |
| 197 | // `AddBlocklistedFeature`. |
| 198 | void ExpectNotRestoredDueToBlocklistedFeature(base::Location location); |
| 199 | |
Ming-Ying Chung | a8e8a2e | 2022-08-16 06:04:50 | [diff] [blame] | 200 | const ukm::TestAutoSetUkmRecorder& ukm_recorder() override; |
| 201 | const base::HistogramTester& histogram_tester() override; |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 202 | |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 203 | private: |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 204 | content::ContentMockCertVerifier mock_cert_verifier_; |
| 205 | |
| 206 | base::test::ScopedFeatureList feature_list_; |
Fergal Daly | 432aa7c | 2022-06-14 07:30:54 | [diff] [blame] | 207 | logging::ScopedVmoduleSwitches vmodule_switches_; |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 208 | |
| 209 | FrameTreeVisualizer visualizer_; |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 210 | std::unique_ptr<net::EmbeddedTestServer> https_server_; |
Daniel Cheng | c9ead4b | 2022-10-05 03:54:29 | [diff] [blame] | 211 | std::map<base::test::FeatureRef, std::map<std::string, std::string>> |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 212 | features_with_params_; |
Daniel Cheng | ccd5b41 | 2022-10-05 06:20:45 | [diff] [blame] | 213 | std::vector<base::test::FeatureRef> disabled_features_; |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 214 | |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 215 | std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_; |
Ming-Ying Chung | a8e8a2e | 2022-08-16 06:04:50 | [diff] [blame] | 216 | std::unique_ptr<base::HistogramTester> histogram_tester_; |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 217 | |
Yuzu Saijo | c347807 | 2022-03-24 06:15:26 | [diff] [blame] | 218 | // Store the tree result of NotRestoredReasons for the last main frame |
| 219 | // navigation. |
| 220 | std::unique_ptr<BackForwardCacheCanStoreTreeResult> tree_result_; |
| 221 | |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 222 | // Whether we should fail the test if a message arrived at the browser from a |
| 223 | // renderer for a bfcached page. |
| 224 | bool fail_for_unexpected_messages_while_cached_ = true; |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 225 | }; |
| 226 | |
Fergal Daly | 973dc9e | 2021-11-19 09:38:17 | [diff] [blame] | 227 | class HighCacheSizeBackForwardCacheBrowserTest |
| 228 | : public BackForwardCacheBrowserTest { |
| 229 | protected: |
| 230 | void SetUpCommandLine(base::CommandLine* command_line) override; |
| 231 | |
| 232 | // The number of pages the BackForwardCache can hold per tab. |
| 233 | // The number 5 was picked since Android ASAN trybot failed to keep more than |
| 234 | // 6 pages in memory. |
| 235 | const size_t kBackForwardCacheSize = 5; |
| 236 | }; |
| 237 | |
Fergal Daly | 97c4e59 | 2023-02-28 10:27:33 | [diff] [blame] | 238 | // Test that enables the BackForwardCacheAllowUnload flag. |
| 239 | class BackForwardCacheUnloadBrowserTest : public BackForwardCacheBrowserTest { |
| 240 | public: |
| 241 | void SetUpCommandLine(base::CommandLine* command_line) override; |
| 242 | |
| 243 | private: |
| 244 | base::test::ScopedFeatureList scoped_feature_list_; |
| 245 | }; |
| 246 | |
Hajime Hoshi | a12414a | 2021-12-08 08:21:23 | [diff] [blame] | 247 | // An implementation of PageLifecycleStateManager::TestDelegate for testing. |
| 248 | class PageLifecycleStateManagerTestDelegate |
| 249 | : public PageLifecycleStateManager::TestDelegate { |
| 250 | public: |
| 251 | explicit PageLifecycleStateManagerTestDelegate( |
| 252 | PageLifecycleStateManager* manager); |
| 253 | |
| 254 | ~PageLifecycleStateManagerTestDelegate() override; |
| 255 | |
| 256 | // Waits for the renderer finishing to set the state of being in back/forward |
| 257 | // cache. |
Fergal Daly | 52ba1e1 | 2022-10-20 07:53:00 | [diff] [blame] | 258 | [[nodiscard]] bool WaitForInBackForwardCacheAck(); |
Hajime Hoshi | a12414a | 2021-12-08 08:21:23 | [diff] [blame] | 259 | |
| 260 | void OnStoreInBackForwardCacheSent(base::OnceClosure cb); |
| 261 | void OnDisableJsEvictionSent(base::OnceClosure cb); |
| 262 | void OnRestoreFromBackForwardCacheSent(base::OnceClosure cb); |
| 263 | |
| 264 | private: |
| 265 | // PageLifecycleStateManager::TestDelegate: |
| 266 | void OnLastAcknowledgedStateChanged( |
| 267 | const blink::mojom::PageLifecycleState& old_state, |
| 268 | const blink::mojom::PageLifecycleState& new_state) override; |
| 269 | void OnUpdateSentToRenderer( |
| 270 | const blink::mojom::PageLifecycleState& new_state) override; |
| 271 | void OnDeleted() override; |
| 272 | |
Arthur Sonzogni | 4c9cdac | 2022-06-13 17:22:56 | [diff] [blame] | 273 | raw_ptr<PageLifecycleStateManager, DanglingUntriaged> manager_; |
Hajime Hoshi | a12414a | 2021-12-08 08:21:23 | [diff] [blame] | 274 | base::OnceClosure store_in_back_forward_cache_sent_; |
| 275 | base::OnceClosure store_in_back_forward_cache_ack_received_; |
| 276 | base::OnceClosure restore_from_back_forward_cache_sent_; |
| 277 | base::OnceClosure disable_eviction_sent_; |
| 278 | }; |
| 279 | |
Fergal Daly | 19b90489 | 2023-10-31 06:04:35 | [diff] [blame] | 280 | // Gets the value of a key in local storage by evaluating JS. Use |
| 281 | // `WaitForLocalStorage` if you are dealing with multiple renderer processes. |
Fergal Daly | 2c7bc405 | 2021-12-23 14:42:22 | [diff] [blame] | 282 | EvalJsResult GetLocalStorage(RenderFrameHostImpl* rfh, std::string key); |
| 283 | |
Fergal Daly | 19b90489 | 2023-10-31 06:04:35 | [diff] [blame] | 284 | // Because we are dealing with multiple renderer processes and the storage |
| 285 | // service, we sometimes need to wait for the storage changes to show up the |
| 286 | // renderer. See https://crbug.com/1494646. |
| 287 | // Returns whether the expected value was found (so timeouts can be recognized). |
| 288 | [[nodiscard]] bool WaitForLocalStorage(RenderFrameHostImpl* rfh, |
| 289 | std::string key, |
| 290 | std::string expected_value); |
| 291 | |
Fergal Daly | f5a50ab | 2021-11-09 05:36:18 | [diff] [blame] | 292 | } // namespace content |
| 293 | |
| 294 | #endif // CONTENT_BROWSER_BACK_FORWARD_CACHE_BROWSERTEST_H_ |