blob: 1f46aded6951864ff1d6f2145b4e6857ffe77b2a [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2014 The Chromium Authors
clamyf1ccb4d2015-01-28 17:40:382// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
avib7348942015-12-25 20:57:105#include <stdint.h>
6
Arthur Sonzognid5ce01f72024-12-13 13:35:287#include <array>
Peter Boströmdd7e40ec2021-04-05 20:40:108#include <memory>
Arthur Sonzognic686e8f2024-01-11 08:36:379#include <optional>
Peter Kasting472500482024-11-21 18:36:2510#include <string>
11#include <string_view>
Peter Boströmdd7e40ec2021-04-05 20:40:1012
clamyf1ccb4d2015-01-28 17:40:3813#include "base/command_line.h"
Lei Zhangd4f2c7ad2021-05-13 20:10:1214#include "base/containers/contains.h"
Alex Ilincf96655e12019-11-21 16:13:5215#include "base/files/file_util.h"
Arthur Sonzogniece30c62018-06-28 09:57:2916#include "base/files/scoped_temp_dir.h"
Avi Drissmanadac21992023-01-11 23:46:3917#include "base/functional/bind.h"
18#include "base/functional/callback.h"
Gabriel Charette9f60dd12020-03-06 20:48:0419#include "base/memory/ptr_util.h"
Keishi Hattori0e45c022021-11-27 09:25:5220#include "base/memory/raw_ptr.h"
Khushal Sagarb13c1512024-04-23 21:51:3721#include "base/metrics/statistics_recorder.h"
danakjc9e8e132020-12-03 00:48:1822#include "base/run_loop.h"
Nidhi Jaju05d098d2022-06-16 00:50:5923#include "base/strings/strcat.h"
clamyf1ccb4d2015-01-28 17:40:3824#include "base/strings/stringprintf.h"
Devon Loehrc0138d8d2025-03-04 01:11:4225#include "base/strings/to_string.h"
clamyb39c17ca2016-02-29 13:33:2626#include "base/strings/utf_string_conversions.h"
Sean Mahere672a662023-01-09 21:42:2827#include "base/task/single_thread_task_runner.h"
Guido Urdanetaef4e91942020-11-09 15:06:2428#include "base/test/bind.h"
jamd5226dd2017-06-01 02:34:3329#include "base/test/scoped_feature_list.h"
Jiacheng Guo6dcc29c2024-07-26 00:28:4030#include "base/test/test_mock_time_task_runner.h"
Nick Burrisc367efe2019-07-24 18:45:4331#include "base/test/test_timeouts.h"
Joshua Thomaseb084802025-03-27 14:23:1132#include "base/test/values_test_util.h"
Charlie Harrisone3457f52018-11-07 19:19:3333#include "base/threading/thread_restrictions.h"
Charlie Reisc091912e2025-06-11 16:27:4434#include "base/time/time.h"
Claudio DeSouza11cb1cb602023-04-17 14:09:2635#include "base/uuid.h"
Chris Fredricksond72b1892025-07-10 22:04:4236#include "base/values.h"
arthursonzognibe56263582018-03-14 11:36:1737#include "build/build_config.h"
Khushal Sagar4a44b722024-04-15 14:40:0938#include "cc/test/pixel_test_utils.h"
Ali Beyadb21baf5f2022-03-31 00:58:0639#include "components/ukm/test_ukm_recorder.h"
Aaron Colwell4696a7b2019-11-07 22:28:5640#include "content/browser/browser_url_handler_impl.h"
nasko5d30e832017-05-24 23:43:0241#include "content/browser/child_process_security_policy_impl.h"
danakj10f32372020-09-15 22:25:1642#include "content/browser/renderer_host/navigation_request.h"
Sharon Yang417a5df2024-04-23 17:57:1543#include "content/browser/renderer_host/navigation_state_keep_alive.h"
Fergal Daly0bda4ab2023-04-19 05:45:0344#include "content/browser/renderer_host/render_frame_host_impl.h"
Patrick Monettea45111342024-09-24 00:33:2945#include "content/browser/renderer_host/spare_render_process_host_manager_impl.h"
Jason Lin218737052023-11-21 08:11:3946#include "content/browser/site_instance_impl.h"
clamyf1ccb4d2015-01-28 17:40:3847#include "content/browser/web_contents/web_contents_impl.h"
[email protected]ee8ae332020-01-29 03:49:4548#include "content/common/content_navigation_policy.h"
Daniel Chengd08a43a2023-03-16 05:10:5049#include "content/common/features.h"
50#include "content/common/frame_messages.mojom.h"
Daniel Cheng42cb7e9f52021-02-23 01:54:4551#include "content/common/navigation_client.mojom-forward.h"
arthursonzogni73fe3212020-11-17 13:24:0752#include "content/common/navigation_client.mojom.h"
Arthur Sonzogniece30c62018-06-28 09:57:2953#include "content/public/browser/browser_context.h"
Eric Seckler8652dcd52018-09-20 10:42:2854#include "content/public/browser/browser_task_traits.h"
Gabriel Charette790754c2018-03-16 21:32:5955#include "content/public/browser/browser_thread.h"
Aran Gilman249eb122019-12-02 23:32:4656#include "content/public/browser/browser_url_handler.h"
Ryan Sturmaa05092f2018-10-21 03:56:5457#include "content/public/browser/content_browser_client.h"
Arthur Sonzogniece30c62018-06-28 09:57:2958#include "content/public/browser/download_manager_delegate.h"
jamd5226dd2017-06-01 02:34:3359#include "content/public/browser/navigation_controller.h"
Yoichi Osatoe0400142023-05-30 19:35:4060#include "content/public/browser/network_service_util.h"
tom836aa6d2021-09-28 19:40:1661#include "content/public/browser/page_navigator.h"
Lukasz Anforowiczf0fc1ca2021-07-16 15:03:1562#include "content/public/browser/render_frame_host.h"
Jason Lin218737052023-11-21 08:11:3963#include "content/public/browser/storage_partition_config.h"
clamyf1ccb4d2015-01-28 17:40:3864#include "content/public/browser/web_contents.h"
Hans Wennborg5ffd1392019-10-16 11:00:0265#include "content/public/common/content_client.h"
jamd5226dd2017-06-01 02:34:3366#include "content/public/common/content_features.h"
clamyf1ccb4d2015-01-28 17:40:3867#include "content/public/common/content_switches.h"
Sharon Yang9d7d5772024-02-16 20:08:4168#include "content/public/common/result_codes.h"
meacerce6b66032016-06-02 20:56:0569#include "content/public/common/url_constants.h"
Rakina Zata Amni348fe2b2021-07-09 05:28:5270#include "content/public/test/back_forward_cache_util.h"
Peter Kasting919ce652020-05-07 10:22:3671#include "content/public/test/browser_test.h"
clamyf1ccb4d2015-01-28 17:40:3872#include "content/public/test/browser_test_utils.h"
73#include "content/public/test/content_browser_test.h"
Scott Violet99861992023-02-08 01:20:1274#include "content/public/test/content_browser_test_content_browser_client.h"
clamyf1ccb4d2015-01-28 17:40:3875#include "content/public/test/content_browser_test_utils.h"
Arthur Sonzognib9ab5102021-08-09 17:52:4176#include "content/public/test/content_mock_cert_verifier.h"
Charlie Harrisone3457f52018-11-07 19:19:3377#include "content/public/test/download_test_observer.h"
sbinglerff91a352025-02-06 19:34:1878#include "content/public/test/fenced_frame_test_util.h"
Stephen Chenneybccdd0302019-08-15 12:21:2879#include "content/public/test/hit_test_region_observer.h"
jamcb4ae152017-05-19 01:35:5180#include "content/public/test/navigation_handle_observer.h"
Lukasz Anforowiczb36b4e22019-06-11 18:37:0381#include "content/public/test/no_renderer_crashes_assertion.h"
Khushal Sagar4a44b722024-04-15 14:40:0982#include "content/public/test/slow_http_response.h"
Joshua Thomaseb084802025-03-27 14:23:1183#include "content/public/test/test_devtools_protocol_client.h"
Erik Anderson5d151f132024-08-14 06:18:0584#include "content/public/test/test_frame_navigation_observer.h"
clamyf1ccb4d2015-01-28 17:40:3885#include "content/public/test/test_navigation_observer.h"
Arthur Sonzognid8143332018-11-29 13:38:0086#include "content/public/test/test_navigation_throttle.h"
87#include "content/public/test/test_navigation_throttle_inserter.h"
John Delaney9f33f2202021-02-22 22:03:4488#include "content/public/test/test_utils.h"
Matt Menked5dc1a22020-03-13 18:46:1489#include "content/public/test/url_loader_monitor.h"
clamyf1ccb4d2015-01-28 17:40:3890#include "content/shell/browser/shell.h"
Arthur Sonzogniece30c62018-06-28 09:57:2991#include "content/shell/browser/shell_download_manager_delegate.h"
nasko5d30e832017-05-24 23:43:0292#include "content/test/content_browser_test_utils_internal.h"
Arthur Hemery2a0a28be2019-03-06 17:51:3693#include "content/test/did_commit_navigation_interceptor.h"
Aaron Colwell4696a7b2019-11-07 22:28:5694#include "content/test/fake_network_url_loader_factory.h"
Khushal Sagar4a44b722024-04-15 14:40:0995#include "content/test/render_document_feature.h"
danakj79bd493f2021-02-10 20:20:3896#include "content/test/task_runner_deferring_throttle.h"
Julie Jeongeun Kim144f8192020-01-18 06:59:0397#include "content/test/test_render_frame_host_factory.h"
Julie Jeongeun Kimed2e5ba72019-09-12 10:14:1798#include "mojo/public/cpp/bindings/pending_associated_remote.h"
Miyoung Shin2be9da72019-09-04 09:04:1099#include "mojo/public/cpp/bindings/pending_receiver.h"
Shivani Sharmad8e6e932019-07-23 17:16:59100#include "net/base/features.h"
nasko1a139222017-05-26 17:30:16101#include "net/base/load_flags.h"
clamyf1ccb4d2015-01-28 17:40:38102#include "net/dns/mock_host_resolver.h"
John Abd-El-Malekf071beb2018-01-30 18:03:24103#include "net/test/embedded_test_server/controllable_http_response.h"
clamyf1ccb4d2015-01-28 17:40:38104#include "net/test/embedded_test_server/embedded_test_server.h"
Daniel Chengf762a4c52020-09-29 05:50:45105#include "net/test/embedded_test_server/http_response.h"
clamy62b271d2015-04-16 11:54:57106#include "net/test/url_request/url_request_failed_job.h"
Yue Ru Sun03a6990a2020-10-24 18:04:31107#include "services/metrics/public/cpp/ukm_source_id.h"
Yutaka Hiranod8789f92018-01-30 09:59:51108#include "services/network/public/cpp/features.h"
arthursonzognie4135822020-12-15 10:32:16109#include "services/network/public/cpp/web_sandbox_flags.h"
Shivani Sharma1b5366c62019-07-16 20:08:13110#include "services/network/public/mojom/url_loader.mojom.h"
Patrick Monettec5201262024-10-09 18:36:57111#include "testing/gmock/include/gmock/gmock.h"
112#include "testing/gtest/include/gtest/gtest.h"
Scott Violeta0a72ce2020-04-22 21:46:41113#include "third_party/blink/public/common/loader/url_loader_throttle.h"
Sharon Yang9d7d5772024-02-16 20:08:41114#include "third_party/blink/public/mojom/frame/remote_frame.mojom-test-utils.h"
Fergal Daly0bda4ab2023-04-19 05:45:03115#include "third_party/blink/public/mojom/frame/sudden_termination_disabler_type.mojom-shared.h"
David Bokan3592c7d2024-03-27 21:34:19116#include "third_party/blink/public/mojom/navigation/navigation_params.mojom-shared.h"
tom836aa6d2021-09-28 19:40:16117#include "ui/base/page_transition_types.h"
clamyf1ccb4d2015-01-28 17:40:38118#include "url/gurl.h"
Charlie Reis56cedcb2024-01-18 20:41:31119#include "url/url_constants.h"
Michael Thiessen2add7d442020-02-05 13:49:38120#include "url/url_util.h"
clamyf1ccb4d2015-01-28 17:40:38121
122namespace content {
123
clamyd3bfdb02018-07-12 13:52:18124namespace {
Arthur Hemery2a0a28be2019-03-06 17:51:36125class InterceptAndCancelDidCommitProvisionalLoad
126 : public DidCommitNavigationInterceptor {
clamyd3bfdb02018-07-12 13:52:18127 public:
128 explicit InterceptAndCancelDidCommitProvisionalLoad(WebContents* web_contents)
Arthur Hemery2a0a28be2019-03-06 17:51:36129 : DidCommitNavigationInterceptor(web_contents) {}
clamyd3bfdb02018-07-12 13:52:18130 ~InterceptAndCancelDidCommitProvisionalLoad() override {}
131
132 void Wait(size_t number_of_messages) {
133 while (intercepted_messages_.size() < number_of_messages) {
Peter Boströmdd7e40ec2021-04-05 20:40:10134 loop_ = std::make_unique<base::RunLoop>();
clamyd3bfdb02018-07-12 13:52:18135 loop_->Run();
136 }
137 }
138
Ali Hijazie63cbaf62023-12-20 19:29:35139 const std::vector<raw_ptr<NavigationRequest, VectorExperimental>>&
140 intercepted_navigations() const {
Arthur Hemery2a0a28be2019-03-06 17:51:36141 return intercepted_navigations_;
142 }
143
arthursonzogni73fe3212020-11-17 13:24:07144 const std::vector<mojom::DidCommitProvisionalLoadParamsPtr>&
clamyd3bfdb02018-07-12 13:52:18145 intercepted_messages() const {
146 return intercepted_messages_;
147 }
148
clamyd3bfdb02018-07-12 13:52:18149 protected:
Arthur Hemery2a0a28be2019-03-06 17:51:36150 bool WillProcessDidCommitNavigation(
clamyd3bfdb02018-07-12 13:52:18151 RenderFrameHost* render_frame_host,
Arthur Hemery2a0a28be2019-03-06 17:51:36152 NavigationRequest* navigation_request,
arthursonzogni73fe3212020-11-17 13:24:07153 mojom::DidCommitProvisionalLoadParamsPtr* params,
Lukasz Anforowicz3cfc1efd2019-02-22 02:29:29154 mojom::DidCommitProvisionalLoadInterfaceParamsPtr* interface_params)
Oksana Zhuravlova8b88e572019-01-07 21:54:00155 override {
Arthur Hemery2a0a28be2019-03-06 17:51:36156 intercepted_navigations_.push_back(navigation_request);
arthursonzogni73fe3212020-11-17 13:24:07157 intercepted_messages_.push_back(std::move(*params));
Takashi Toyoshimae9c959ce92025-05-20 19:18:38158 if (loop_) {
clamyd3bfdb02018-07-12 13:52:18159 loop_->Quit();
Takashi Toyoshimae9c959ce92025-05-20 19:18:38160 }
clamyd3bfdb02018-07-12 13:52:18161 // Do not send the message to the RenderFrameHostImpl.
162 return false;
163 }
164
Arthur Hemery2a0a28be2019-03-06 17:51:36165 // Note: Do not dereference the intercepted_navigations_, they are used as
166 // indices in the RenderFrameHostImpl and not for themselves.
Ali Hijazie63cbaf62023-12-20 19:29:35167 std::vector<raw_ptr<NavigationRequest, VectorExperimental>>
168 intercepted_navigations_;
arthursonzogni73fe3212020-11-17 13:24:07169 std::vector<mojom::DidCommitProvisionalLoadParamsPtr> intercepted_messages_;
clamyd3bfdb02018-07-12 13:52:18170 std::unique_ptr<base::RunLoop> loop_;
171};
172
Julie Jeongeun Kim144f8192020-01-18 06:59:03173class RenderFrameHostImplForHistoryBackInterceptor
174 : public RenderFrameHostImpl {
Arthur Sonzognia7d715a2018-09-20 16:11:13175 public:
Julie Jeongeun Kim144f8192020-01-18 06:59:03176 using RenderFrameHostImpl::RenderFrameHostImpl;
Arthur Sonzognia7d715a2018-09-20 16:11:13177
Yoav Weiss8c573952022-11-17 17:35:13178 void GoToEntryAtOffset(int32_t offset,
179 bool has_user_gesture,
Charlie Reisc091912e2025-06-11 16:27:44180 base::TimeTicks actual_navigation_start,
Arthur Sonzognic686e8f2024-01-11 08:36:37181 std::optional<blink::scheduler::TaskAttributionId>
Yoav Weiss2cd2eaf2023-01-13 07:03:23182 soft_navigation_heuristics_task_id) override {
Takashi Toyoshimae9c959ce92025-05-20 19:18:38183 if (quit_handler_) {
Julie Jeongeun Kim144f8192020-01-18 06:59:03184 std::move(quit_handler_).Run();
Takashi Toyoshimae9c959ce92025-05-20 19:18:38185 }
Arthur Sonzognia7d715a2018-09-20 16:11:13186 }
187
Julie Jeongeun Kim144f8192020-01-18 06:59:03188 void set_quit_handler(base::OnceClosure handler) {
189 quit_handler_ = std::move(handler);
190 }
Arthur Sonzognia7d715a2018-09-20 16:11:13191
192 private:
Julie Jeongeun Kim144f8192020-01-18 06:59:03193 friend class RenderFrameHostFactoryForHistoryBackInterceptor;
194 base::OnceClosure quit_handler_;
195};
196
197class RenderFrameHostFactoryForHistoryBackInterceptor
198 : public TestRenderFrameHostFactory {
199 protected:
200 std::unique_ptr<RenderFrameHostImpl> CreateRenderFrameHost(
201 SiteInstance* site_instance,
202 scoped_refptr<RenderViewHostImpl> render_view_host,
203 RenderFrameHostDelegate* delegate,
204 FrameTree* frame_tree,
205 FrameTreeNode* frame_tree_node,
206 int32_t routing_id,
danakj0bdfacd2021-01-20 19:27:18207 mojo::PendingAssociatedRemote<mojom::Frame> frame_remote,
Chris Hamilton6a8f38d2021-02-19 01:01:09208 const blink::LocalFrameToken& frame_token,
Daniel Cheng284c38942022-09-22 23:30:34209 const blink::DocumentToken& document_token,
Danil Somsikov259aa65f2022-11-11 20:49:44210 base::UnguessableToken devtools_frame_token,
Daniel Cheng22a4add22020-10-30 23:46:06211 bool renderer_initiated_creation,
Harkiran Bolaria8dec6f92021-12-07 14:57:12212 RenderFrameHostImpl::LifecycleStateImpl lifecycle_state,
213 scoped_refptr<BrowsingContextState> browsing_context_state) override {
Julie Jeongeun Kim144f8192020-01-18 06:59:03214 return base::WrapUnique(new RenderFrameHostImplForHistoryBackInterceptor(
215 site_instance, std::move(render_view_host), delegate, frame_tree,
danakj0bdfacd2021-01-20 19:27:18216 frame_tree_node, routing_id, std::move(frame_remote), frame_token,
Danil Somsikov259aa65f2022-11-11 20:49:44217 document_token, devtools_frame_token, renderer_initiated_creation,
218 lifecycle_state, std::move(browsing_context_state),
Abhijeet Kandalkar0d4ff8e2022-09-30 21:08:14219 frame_tree_node->frame_owner_element_type(), frame_tree_node->parent(),
Abhijeet Kandalkar3f29bc42022-09-23 12:39:58220 frame_tree_node->fenced_frame_status()));
Julie Jeongeun Kim144f8192020-01-18 06:59:03221 }
Arthur Sonzognia7d715a2018-09-20 16:11:13222};
223
arthursonzogni55d9c722019-09-11 09:43:16224// Simulate embedders of content/ keeping track of the current visible URL using
225// NavigateStateChanged() and GetVisibleURL() API.
226class EmbedderVisibleUrlTracker : public WebContentsDelegate {
227 public:
228 const GURL& url() { return url_; }
229
230 // WebContentsDelegate's implementation:
231 void NavigationStateChanged(WebContents* source,
232 InvalidateTypes changed_flags) override {
Takashi Toyoshimae9c959ce92025-05-20 19:18:38233 if (!(changed_flags & INVALIDATE_TYPE_URL)) {
arthursonzogni55d9c722019-09-11 09:43:16234 return;
Takashi Toyoshimae9c959ce92025-05-20 19:18:38235 }
arthursonzogni55d9c722019-09-11 09:43:16236 url_ = source->GetVisibleURL();
Takashi Toyoshimae9c959ce92025-05-20 19:18:38237 if (on_url_invalidated_) {
arthursonzogni55d9c722019-09-11 09:43:16238 std::move(on_url_invalidated_).Run();
Takashi Toyoshimae9c959ce92025-05-20 19:18:38239 }
arthursonzogni55d9c722019-09-11 09:43:16240 }
241
242 void WaitUntilUrlInvalidated() {
243 base::RunLoop loop;
244 on_url_invalidated_ = loop.QuitClosure();
245 loop.Run();
246 }
247
248 private:
249 GURL url_;
250 base::OnceClosure on_url_invalidated_;
251};
252
arthursonzogni5a8f5ad2020-09-28 11:55:32253// Helper class. Immediately run a callback when a navigation starts.
Peter Kasting9a7cffca2021-07-28 17:39:16254class DidStartNavigationCallback final : public WebContentsObserver {
arthursonzogni5a8f5ad2020-09-28 11:55:32255 public:
256 explicit DidStartNavigationCallback(
257 WebContents* web_contents,
258 base::OnceCallback<void(NavigationHandle*)> callback)
259 : WebContentsObserver(web_contents), callback_(std::move(callback)) {}
Peter Kasting9a7cffca2021-07-28 17:39:16260 ~DidStartNavigationCallback() override = default;
arthursonzogni5a8f5ad2020-09-28 11:55:32261
262 private:
Peter Kasting9a7cffca2021-07-28 17:39:16263 void DidStartNavigation(NavigationHandle* navigation_handle) override {
Takashi Toyoshimae9c959ce92025-05-20 19:18:38264 if (callback_) {
arthursonzogni5a8f5ad2020-09-28 11:55:32265 std::move(callback_).Run(navigation_handle);
Takashi Toyoshimae9c959ce92025-05-20 19:18:38266 }
arthursonzogni5a8f5ad2020-09-28 11:55:32267 }
268 base::OnceCallback<void(NavigationHandle*)> callback_;
269};
270
Yoav Weiss3e46271d2021-04-30 20:35:21271// Helper class. Immediately run a callback when a navigation finishes.
Peter Kasting9a7cffca2021-07-28 17:39:16272class DidFinishNavigationCallback final : public WebContentsObserver {
Yoav Weiss3e46271d2021-04-30 20:35:21273 public:
274 explicit DidFinishNavigationCallback(
275 WebContents* web_contents,
276 base::OnceCallback<void(NavigationHandle*)> callback)
277 : WebContentsObserver(web_contents), callback_(std::move(callback)) {}
Peter Kasting9a7cffca2021-07-28 17:39:16278 ~DidFinishNavigationCallback() override = default;
Yoav Weiss3e46271d2021-04-30 20:35:21279
280 private:
Peter Kasting9a7cffca2021-07-28 17:39:16281 void DidFinishNavigation(NavigationHandle* navigation_handle) override {
Takashi Toyoshimae9c959ce92025-05-20 19:18:38282 if (callback_) {
Yoav Weiss3e46271d2021-04-30 20:35:21283 std::move(callback_).Run(navigation_handle);
Takashi Toyoshimae9c959ce92025-05-20 19:18:38284 }
Yoav Weiss3e46271d2021-04-30 20:35:21285 }
286 base::OnceCallback<void(NavigationHandle*)> callback_;
287};
288
arthursonzogni55d9c722019-09-11 09:43:16289const char* non_cacheable_html_response =
290 "HTTP/1.1 200 OK\n"
291 "cache-control: no-cache, no-store, must-revalidate\n"
292 "content-type: text/html; charset=UTF-8\n"
293 "\n"
294 "HTML content.";
295
arthursonzognie4135822020-12-15 10:32:16296// Insert a navigation throttle blocking every navigation in its
297// WillProcessResponse handler.
298std::unique_ptr<content::TestNavigationThrottleInserter>
299BlockNavigationWillProcessResponse(WebContentsImpl* web_content) {
300 return std::make_unique<content::TestNavigationThrottleInserter>(
301 web_content,
302 base::BindLambdaForTesting(
Takashi Toyoshimae9c959ce92025-05-20 19:18:38303 [&](NavigationThrottleRegistry& registry) -> void {
304 auto throttle = std::make_unique<TestNavigationThrottle>(registry);
arthursonzognie4135822020-12-15 10:32:16305 throttle->SetResponse(TestNavigationThrottle::WILL_PROCESS_RESPONSE,
306 TestNavigationThrottle::SYNCHRONOUS,
307 NavigationThrottle::BLOCK_RESPONSE);
Takashi Toyoshimae9c959ce92025-05-20 19:18:38308 registry.AddThrottle(std::move(throttle));
arthursonzognie4135822020-12-15 10:32:16309 }));
310}
311
Khushal Sagarb13c1512024-04-23 21:51:37312void WaitForHistogramRecordedInChildProcess(std::string name) {
313 base::HistogramTester histogram_tester;
314
315 while (true) {
316 if (!histogram_tester.GetAllSamples(name).empty()) {
317 return;
318 }
319
320 // Retry fetching the histogram since it's not populated yet.
321 content::FetchHistogramsFromChildProcesses();
322 base::StatisticsRecorder::ImportProvidedHistogramsSync();
323 base::RunLoop().RunUntilIdle();
324 }
325}
326
clamyd3bfdb02018-07-12 13:52:18327} // namespace
328
Arthur Sonzogni57d111142018-09-19 15:40:13329// Test about navigation.
arthursonzogni3a4ca9f2017-12-07 17:58:34330// If you don't need a custom embedded test server, please use the next class
Arthur Sonzogni57d111142018-09-19 15:40:13331// below (NavigationBrowserTest), it will automatically start the
arthursonzogni3a4ca9f2017-12-07 17:58:34332// default server.
Minggang Wang83f7f3712019-11-16 11:28:16333class NavigationBaseBrowserTest : public ContentBrowserTest {
Ken Rockot28910122019-10-10 19:07:58334 public:
Minggang Wang83f7f3712019-11-16 11:28:16335 NavigationBaseBrowserTest() {}
Ken Rockot28910122019-10-10 19:07:58336
Ali Beyadb21baf5f2022-03-31 00:58:06337 void PreRunTestOnMainThread() override {
338 ContentBrowserTest::PreRunTestOnMainThread();
339 test_ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
340 }
341
342 const ukm::TestAutoSetUkmRecorder& test_ukm_recorder() const {
343 return *test_ukm_recorder_;
344 }
345
clamyf1ccb4d2015-01-28 17:40:38346 protected:
clamyf1ccb4d2015-01-28 17:40:38347 void SetUpOnMainThread() override {
348 host_resolver()->AddRule("*", "127.0.0.1");
arthursonzogni3a4ca9f2017-12-07 17:58:34349 }
arthursonzognia26fa9a2020-11-03 10:16:47350
351 WebContentsImpl* web_contents() const {
352 return static_cast<WebContentsImpl*>(shell()->web_contents());
353 }
354
Carlos Caballero15caeeb2021-10-27 09:57:55355 FrameTreeNode* main_frame() {
356 return web_contents()->GetPrimaryFrameTree().root();
357 }
arthursonzognia26fa9a2020-11-03 10:16:47358
359 RenderFrameHostImpl* current_frame_host() {
360 return main_frame()->current_frame_host();
361 }
Ali Beyadb21baf5f2022-03-31 00:58:06362
363 private:
364 std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder_;
arthursonzogni3a4ca9f2017-12-07 17:58:34365};
366
Arthur Sonzogni57d111142018-09-19 15:40:13367class NavigationBrowserTest : public NavigationBaseBrowserTest {
arthursonzogni3a4ca9f2017-12-07 17:58:34368 protected:
369 void SetUpOnMainThread() override {
Arthur Sonzogni57d111142018-09-19 15:40:13370 NavigationBaseBrowserTest::SetUpOnMainThread();
svaldezc3a9a172015-11-03 22:01:33371 ASSERT_TRUE(embedded_test_server()->Start());
clamyf1ccb4d2015-01-28 17:40:38372 }
Shivani Sharmad8e6e932019-07-23 17:16:59373};
374
Julie Jeongeun Kim144f8192020-01-18 06:59:03375class NavigationGoToEntryAtOffsetBrowserTest : public NavigationBrowserTest {
376 public:
377 void SetQuitHandlerForGoToEntryAtOffset(base::OnceClosure handler) {
378 RenderFrameHostImplForHistoryBackInterceptor* render_frame_host =
379 static_cast<RenderFrameHostImplForHistoryBackInterceptor*>(
arthursonzognia26fa9a2020-11-03 10:16:47380 current_frame_host());
Julie Jeongeun Kim144f8192020-01-18 06:59:03381 render_frame_host->set_quit_handler(std::move(handler));
382 }
383
384 private:
385 RenderFrameHostFactoryForHistoryBackInterceptor render_frame_host_factory_;
386};
387
Matt Menke6e2bf4e2021-03-05 14:45:45388class NetworkIsolationNavigationBrowserTest : public ContentBrowserTest {
Ken Rockot28910122019-10-10 19:07:58389 public:
Matt Menke6e2bf4e2021-03-05 14:45:45390 NetworkIsolationNavigationBrowserTest() = default;
Ken Rockot28910122019-10-10 19:07:58391
392 protected:
393 void SetUpOnMainThread() override {
Shivani Sharmad8e6e932019-07-23 17:16:59394 ASSERT_TRUE(embedded_test_server()->Start());
395 ContentBrowserTest::SetUpOnMainThread();
396 }
clamyf1ccb4d2015-01-28 17:40:38397};
398
Dominic Farolino191ccfc52019-09-19 17:49:42399class NavigationBrowserTestReferrerPolicy
arthursonzognia26fa9a2020-11-03 10:16:47400 : public NavigationBrowserTest,
Dominic Farolino191ccfc52019-09-19 17:49:42401 public ::testing::WithParamInterface<network::mojom::ReferrerPolicy> {
402 protected:
Dominic Farolino191ccfc52019-09-19 17:49:42403 network::mojom::ReferrerPolicy GetReferrerPolicy() const {
404 return GetParam();
405 }
406};
407
408INSTANTIATE_TEST_SUITE_P(
Ilia Samsonov188d9c82019-12-09 16:03:07409 All,
Dominic Farolino191ccfc52019-09-19 17:49:42410 NavigationBrowserTestReferrerPolicy,
Dominic Farolino361353df2020-03-03 06:01:10411 ::testing::Values(
412 network::mojom::ReferrerPolicy::kAlways,
413 network::mojom::ReferrerPolicy::kDefault,
414 network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade,
415 network::mojom::ReferrerPolicy::kNever,
416 network::mojom::ReferrerPolicy::kOrigin,
417 network::mojom::ReferrerPolicy::kOriginWhenCrossOrigin,
418 network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin,
419 network::mojom::ReferrerPolicy::kSameOrigin,
420 network::mojom::ReferrerPolicy::kStrictOrigin));
Dominic Farolino191ccfc52019-09-19 17:49:42421
Joshua Thomas81febb32025-06-17 18:16:32422struct FrameAncestorTestData {
423 std::string_view inner_host;
424 std::string_view intermediate_host;
425 std::string_view expected_header_value;
426 std::string_view expected_header_value_for_redirect;
427};
428
429// Parameterized test suite that tests the behavior of IsolationInfo's
430// `frame_ancestor_relation` variable on navigations having multiple frame
431// ancestors. The parameters are `FrameAncetorTestData` objects, which provide
432// strings determining the origins for the innermost iframe, `inner_host`, as
433// well as an `intermediate_host`, which sets the origin of an iframe between
434// the top frame and the innermost frame.
435class FrameAncestorNavigationBrowserTest
436 : public NavigationBrowserTest,
437 public ::testing::WithParamInterface<FrameAncestorTestData> {
438 public:
439 FrameAncestorNavigationBrowserTest() {
440 features_.InitAndEnableFeature(network::features::kFrameAncestorsHeader);
441 }
442
443 void SetUpOnMainThread() override {
444 embedded_https_test_server().SetSSLConfig(
445 net::EmbeddedTestServer::CERT_TEST_NAMES);
446 embedded_https_test_server().RegisterRequestMonitor(
447 base::BindLambdaForTesting(
448 [&](const net::test_server::HttpRequest& request) {
449 base::AutoLock lock(lock_);
450 observed_request_headers_.emplace_back(request.GetURL().path(),
451 request.headers);
452 }));
453 NavigationBaseBrowserTest::SetUpOnMainThread();
454 ASSERT_TRUE(embedded_https_test_server().Start());
455 }
456
457 std::vector<std::pair<std::string, net::test_server::HttpRequest::HeaderMap>>
458 observed_request_headers() const {
459 base::AutoLock lock(lock_);
460 return observed_request_headers_;
461 }
462
463 // Navigates the innermost frame to the given URL. (The web_contents is
464 // assumed to be showing a page containing an iframe that contains another
465 // iframe.)
466 void NavigateNestedFrameTo(const GURL& url) {
467 content::TestNavigationObserver load_observer(web_contents());
468 ASSERT_TRUE(ExecJs(
469 ChildFrameAt(main_frame(), 0),
470 base::StringPrintf("document.body.querySelector('iframe').src = '%s';",
471 url.spec().c_str())));
472 load_observer.Wait();
473 }
474
475 std::string_view intermediate_host() const {
476 return GetParam().intermediate_host;
477 }
478 std::string_view inner_host() const { return GetParam().inner_host; }
479 std::string_view expected_relation() const {
480 return GetParam().expected_header_value;
481 }
482 std::string_view expected_relation_for_redirect() const {
483 return GetParam().expected_header_value_for_redirect;
484 }
485
486 using HeaderMapMatchers = std::initializer_list<
487 testing::Matcher<std::pair<std::string, std::string>>>;
488
489 private:
490 mutable base::Lock lock_;
491 std::vector<std::pair<std::string, net::test_server::HttpRequest::HeaderMap>>
492 observed_request_headers_ GUARDED_BY(lock_);
493 base::test::ScopedFeatureList features_;
494};
495
496IN_PROC_BROWSER_TEST_P(FrameAncestorNavigationBrowserTest,
497 NestedSubframeFrameAncestorRelation) {
498 GURL starting_page(embedded_https_test_server().GetURL(
499 "a.test", "/page_with_blank_iframe_tree.html"));
500 EXPECT_TRUE(NavigateToURL(web_contents(), starting_page));
501
502 GURL intermediate_url(embedded_https_test_server().GetURL(
503 intermediate_host(), "/page_with_blank_iframe.html"));
504
505 // Perform intermediary navigation.
506 EXPECT_TRUE(NavigateIframeToURL(web_contents(), "f1", intermediate_url));
507
508 GURL inner_url(
509 embedded_https_test_server().GetURL(inner_host(), "/test1.html"));
510 URLLoaderMonitor monitor({inner_url});
511
512 // Navigate inner iframe.
513 NavigateNestedFrameTo(inner_url);
514 monitor.WaitForUrls();
515
516 EXPECT_THAT(
517 observed_request_headers(),
518 Contains(Pair(
519 inner_url.path(),
520 testing::IsSupersetOf<HeaderMapMatchers>({
521 testing::Pair("Sec-Fetch-Frame-Ancestors", expected_relation()),
522 }))));
523}
524
525IN_PROC_BROWSER_TEST_P(FrameAncestorNavigationBrowserTest, SubframeRedirect) {
526 GURL starting_page(
527 embedded_https_test_server().GetURL("a.test", "/empty.html"));
528 EXPECT_TRUE(NavigateToURL(web_contents(), starting_page));
529
530 GURL inner_url(
531 embedded_https_test_server().GetURL(inner_host(), "/test2.html"));
532
533 GURL redirecting_url(embedded_https_test_server().GetURL(
534 intermediate_host(), "/server-redirect?" + inner_url.spec()));
535
536 TestNavigationObserver load_observer(web_contents());
537 // Create a subframe that redirects to a page at `inner_host` via
538 // `intermediate_host`.
539 const char subframe_request_script[] = R"(
540 let iframe = document.createElement('iframe');
541 iframe.src = $1;
542 document.body.appendChild(iframe);
543 )";
544 ASSERT_TRUE(ExecJs(main_frame(),
545 JsReplace(subframe_request_script, redirecting_url)));
546 load_observer.Wait();
547
548 EXPECT_THAT(observed_request_headers(),
549 Contains(Pair(inner_url.path(),
550 testing::IsSupersetOf<HeaderMapMatchers>({
551 testing::Pair("Sec-Fetch-Frame-Ancestors",
552 expected_relation_for_redirect()),
553 }))));
554}
555
556IN_PROC_BROWSER_TEST_P(FrameAncestorNavigationBrowserTest, TopFrameRedirect) {
557 GURL inner_url(
558 embedded_https_test_server().GetURL(inner_host(), "/test1.html"));
559
560 GURL redirecting_url(embedded_https_test_server().GetURL(
561 intermediate_host(), "/server-redirect?" + inner_url.spec()));
562
563 TestNavigationObserver load_observer(web_contents());
564 // Navigate the top frame to a page at `inner_host` via a redirect.
565 NavigateToURLBlockUntilNavigationsComplete(
566 web_contents(), redirecting_url, 1,
567 /*ignore_uncommitted_navigations=*/false);
568 load_observer.Wait();
569
570 // The header should use the same-origin value for all main frame requests
571 // since it is same-origin with itself.
572 EXPECT_THAT(observed_request_headers(),
573 Contains(Pair(
574 inner_url.path(),
575 testing::IsSupersetOf<HeaderMapMatchers>({
576 testing::Pair("Sec-Fetch-Frame-Ancestors", "same-origin"),
577 }))));
578}
579
580IN_PROC_BROWSER_TEST_P(FrameAncestorNavigationBrowserTest,
581 SubresourceRedirect) {
582 GURL starting_page(
583 embedded_https_test_server().GetURL("a.test", "/empty.html"));
584 EXPECT_TRUE(NavigateToURL(web_contents(), starting_page));
585
586 // Create a subresource request to intermediate_host that redirects to an
587 // image hosted at inner_host.
588 GURL inner_url(
589 embedded_https_test_server().GetURL(inner_host(), "/blank.jpg"));
590 GURL redirecting_url(embedded_https_test_server().GetURL(
591 intermediate_host(), "/server-redirect?" + inner_url.spec()));
592
593 const char subresource_request_script[] = R"(
594 new Promise(function (resolve, reject) {
595 var img = document.createElement('img');
596 img.src = $1;
597 img.onload = _ => resolve('OK');
598 img.onerror = e => resolve('ERR: ' + e);
599 });
600 )";
601
602 ASSERT_TRUE(ExecJs(main_frame(),
603 JsReplace(subresource_request_script, redirecting_url)));
604
605 EXPECT_THAT(observed_request_headers(),
606 Contains(Pair(inner_url.path(),
607 testing::IsSupersetOf<HeaderMapMatchers>({
608 testing::Pair("Sec-Fetch-Frame-Ancestors",
609 expected_relation_for_redirect()),
610 }))));
611}
612
613INSTANTIATE_TEST_SUITE_P(
614 ,
615 FrameAncestorNavigationBrowserTest,
616 ::testing::Values(
617 FrameAncestorTestData{"a.test", "a.test", "same-origin", "same-origin"},
618 FrameAncestorTestData{"a.test", "other.a.test", "same-site",
619 "same-origin"},
620 FrameAncestorTestData{"a.test", "b.test", "cross-site", "same-origin"},
621 FrameAncestorTestData{"other.a.test", "a.test", "same-site",
622 "same-site"},
623 FrameAncestorTestData{"other.a.test", "other.a.test", "same-site",
624 "same-site"},
625 FrameAncestorTestData{"other.a.test", "b.test", "cross-site",
626 "same-site"},
627 FrameAncestorTestData{"b.test", "a.test", "cross-site", "cross-site"},
628 FrameAncestorTestData{"b.test", "other.a.test", "cross-site",
629 "cross-site"},
630 FrameAncestorTestData{"b.test", "b.test", "cross-site", "cross-site"}));
631
Arthur Sonzogni57d111142018-09-19 15:40:13632// Ensure that browser initiated basic navigations work.
Minggang Wang83f7f3712019-11-16 11:28:16633IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, BrowserInitiatedNavigations) {
clamyf1ccb4d2015-01-28 17:40:38634 // Perform a navigation with no live renderer.
635 {
arthursonzognia26fa9a2020-11-03 10:16:47636 TestNavigationObserver observer(web_contents());
clamyf1ccb4d2015-01-28 17:40:38637 GURL url(embedded_test_server()->GetURL("/title1.html"));
Alex Moshchukaeb20fe32019-09-25 17:40:01638 EXPECT_TRUE(NavigateToURL(shell(), url));
clamyf1ccb4d2015-01-28 17:40:38639 EXPECT_EQ(url, observer.last_navigation_url());
640 EXPECT_TRUE(observer.last_navigation_succeeded());
Nasko Oskov93e7c55c2018-12-19 01:59:29641 EXPECT_FALSE(observer.last_initiator_origin().has_value());
Antonio Sartori9a82f6f32020-12-14 09:22:45642 EXPECT_FALSE(observer.last_initiator_frame_token().has_value());
643 EXPECT_EQ(ChildProcessHost::kInvalidUniqueID,
644 observer.last_initiator_process_id());
clamyf1ccb4d2015-01-28 17:40:38645 }
646
arthursonzognia26fa9a2020-11-03 10:16:47647 RenderFrameHost* initial_rfh = current_frame_host();
clamyf1ccb4d2015-01-28 17:40:38648
649 // Perform a same site navigation.
650 {
arthursonzognia26fa9a2020-11-03 10:16:47651 TestNavigationObserver observer(web_contents());
clamyf1ccb4d2015-01-28 17:40:38652 GURL url(embedded_test_server()->GetURL("/title2.html"));
Alex Moshchukaeb20fe32019-09-25 17:40:01653 EXPECT_TRUE(NavigateToURL(shell(), url));
clamyf1ccb4d2015-01-28 17:40:38654 EXPECT_EQ(url, observer.last_navigation_url());
655 EXPECT_TRUE(observer.last_navigation_succeeded());
Nasko Oskov93e7c55c2018-12-19 01:59:29656 EXPECT_FALSE(observer.last_initiator_origin().has_value());
Antonio Sartori9a82f6f32020-12-14 09:22:45657 EXPECT_FALSE(observer.last_initiator_frame_token().has_value());
658 EXPECT_EQ(ChildProcessHost::kInvalidUniqueID,
659 observer.last_initiator_process_id());
clamyf1ccb4d2015-01-28 17:40:38660 }
661
arthursonzognia26fa9a2020-11-03 10:16:47662 RenderFrameHost* second_rfh = current_frame_host();
Rakina Zata Amni7a96ab382020-07-31 12:38:56663
664 if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) {
665 // If same-site ProactivelySwapBrowsingInstance or main-frame RenderDocument
666 // is enabled, the navigation will result in a new RFH.
667 EXPECT_NE(initial_rfh, second_rfh);
668 } else {
669 EXPECT_EQ(initial_rfh, second_rfh);
670 }
clamyf1ccb4d2015-01-28 17:40:38671
672 // Perform a cross-site navigation.
673 {
arthursonzognia26fa9a2020-11-03 10:16:47674 TestNavigationObserver observer(web_contents());
clamyf1ccb4d2015-01-28 17:40:38675 GURL url = embedded_test_server()->GetURL("foo.com", "/title3.html");
Alex Moshchukaeb20fe32019-09-25 17:40:01676 EXPECT_TRUE(NavigateToURL(shell(), url));
clamyf1ccb4d2015-01-28 17:40:38677 EXPECT_EQ(url, observer.last_navigation_url());
678 EXPECT_TRUE(observer.last_navigation_succeeded());
Nasko Oskov93e7c55c2018-12-19 01:59:29679 EXPECT_FALSE(observer.last_initiator_origin().has_value());
Antonio Sartori9a82f6f32020-12-14 09:22:45680 EXPECT_FALSE(observer.last_initiator_frame_token().has_value());
681 EXPECT_EQ(ChildProcessHost::kInvalidUniqueID,
682 observer.last_initiator_process_id());
clamyf1ccb4d2015-01-28 17:40:38683 }
684
685 // The RenderFrameHost should have changed.
arthursonzognia26fa9a2020-11-03 10:16:47686 EXPECT_NE(second_rfh, current_frame_host());
Ali Beyadb21baf5f2022-03-31 00:58:06687
688 // Check the UKM for navigation responses received.
689 EXPECT_EQ(3u, test_ukm_recorder()
690 .GetEntriesByName("Navigation.ReceivedResponse")
691 .size());
clamyf1ccb4d2015-01-28 17:40:38692}
693
Arthur Sonzogni57d111142018-09-19 15:40:13694// Ensure that renderer initiated same-site navigations work.
Minggang Wang83f7f3712019-11-16 11:28:16695IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
clamyf1ccb4d2015-01-28 17:40:38696 RendererInitiatedSameSiteNavigation) {
697 // Perform a navigation with no live renderer.
698 {
arthursonzognia26fa9a2020-11-03 10:16:47699 TestNavigationObserver observer(web_contents());
clamyf1ccb4d2015-01-28 17:40:38700 GURL url(embedded_test_server()->GetURL("/simple_links.html"));
Alex Moshchukaeb20fe32019-09-25 17:40:01701 EXPECT_TRUE(NavigateToURL(shell(), url));
clamyf1ccb4d2015-01-28 17:40:38702 EXPECT_EQ(url, observer.last_navigation_url());
703 EXPECT_TRUE(observer.last_navigation_succeeded());
Nasko Oskov93e7c55c2018-12-19 01:59:29704 EXPECT_FALSE(observer.last_initiator_origin().has_value());
Antonio Sartori9a82f6f32020-12-14 09:22:45705 EXPECT_FALSE(observer.last_initiator_frame_token().has_value());
706 EXPECT_EQ(ChildProcessHost::kInvalidUniqueID,
707 observer.last_initiator_process_id());
clamyf1ccb4d2015-01-28 17:40:38708 }
709
arthursonzognia26fa9a2020-11-03 10:16:47710 RenderFrameHost* initial_rfh = current_frame_host();
Dave Tapuskae9b7c0f72023-11-06 16:38:01711 auto initial_rfh_global_token = initial_rfh->GetGlobalFrameToken();
Rakina Zata Amni7a96ab382020-07-31 12:38:56712
clamyf1ccb4d2015-01-28 17:40:38713 // Simulate clicking on a same-site link.
714 {
arthursonzognia26fa9a2020-11-03 10:16:47715 TestNavigationObserver observer(web_contents());
clamyf1ccb4d2015-01-28 17:40:38716 GURL url(embedded_test_server()->GetURL("/title2.html"));
Avi Drissmanc91bd8e2021-04-19 23:58:44717 EXPECT_EQ(true, EvalJs(shell(), "clickSameSiteLink();"));
arthursonzognia26fa9a2020-11-03 10:16:47718 EXPECT_TRUE(WaitForLoadStop(web_contents()));
clamyf1ccb4d2015-01-28 17:40:38719 EXPECT_EQ(url, observer.last_navigation_url());
720 EXPECT_TRUE(observer.last_navigation_succeeded());
John Delaneyf43556d2020-05-04 23:19:06721
arthursonzognia26fa9a2020-11-03 10:16:47722 EXPECT_EQ(current_frame_host()->GetLastCommittedOrigin(),
Nasko Oskov93e7c55c2018-12-19 01:59:29723 observer.last_initiator_origin());
Rakina Zata Amni7a96ab382020-07-31 12:38:56724
Antonio Sartori9a82f6f32020-12-14 09:22:45725 EXPECT_TRUE(observer.last_initiator_frame_token().has_value());
Rakina Zata Amni7a96ab382020-07-31 12:38:56726 if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) {
727 // If same-site ProactivelySwapBrowsingInstance or main-frame
728 // RenderDocument is enabled, the navigation will result in a new RFH, so
729 // we need to compare with |initial_rfh|.
arthursonzognia26fa9a2020-11-03 10:16:47730 EXPECT_NE(current_frame_host(), initial_rfh);
Dave Tapuskae9b7c0f72023-11-06 16:38:01731 EXPECT_EQ(initial_rfh_global_token.frame_token,
Antonio Sartori9a82f6f32020-12-14 09:22:45732 observer.last_initiator_frame_token().value());
Dave Tapuskae9b7c0f72023-11-06 16:38:01733 EXPECT_EQ(initial_rfh_global_token.child_id,
734 observer.last_initiator_process_id());
Rakina Zata Amni7a96ab382020-07-31 12:38:56735 } else {
arthursonzognia26fa9a2020-11-03 10:16:47736 EXPECT_EQ(current_frame_host(), initial_rfh);
Antonio Sartori9a82f6f32020-12-14 09:22:45737 EXPECT_EQ(current_frame_host()->GetFrameToken(),
738 observer.last_initiator_frame_token().value());
Emily Andrewsd15fd762024-12-10 20:41:54739 EXPECT_EQ(current_frame_host()->GetProcess()->GetDeprecatedID(),
Antonio Sartori9a82f6f32020-12-14 09:22:45740 observer.last_initiator_process_id());
Rakina Zata Amni7a96ab382020-07-31 12:38:56741 }
clamyf1ccb4d2015-01-28 17:40:38742 }
743
arthursonzognia26fa9a2020-11-03 10:16:47744 RenderFrameHost* second_rfh = current_frame_host();
Rakina Zata Amni7a96ab382020-07-31 12:38:56745
746 if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) {
747 // If same-site ProactivelySwapBrowsingInstance or main-frame RenderDocument
748 // is enabled, the navigation will result in a new RFH.
749 EXPECT_NE(initial_rfh, second_rfh);
750 } else {
751 EXPECT_EQ(initial_rfh, second_rfh);
752 }
clamyf1ccb4d2015-01-28 17:40:38753}
754
Arthur Sonzogni57d111142018-09-19 15:40:13755// Ensure that renderer initiated cross-site navigations work.
Minggang Wang83f7f3712019-11-16 11:28:16756IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
clamyf1ccb4d2015-01-28 17:40:38757 RendererInitiatedCrossSiteNavigation) {
758 // Perform a navigation with no live renderer.
759 {
arthursonzognia26fa9a2020-11-03 10:16:47760 TestNavigationObserver observer(web_contents());
clamyf1ccb4d2015-01-28 17:40:38761 GURL url(embedded_test_server()->GetURL("/simple_links.html"));
Alex Moshchukaeb20fe32019-09-25 17:40:01762 EXPECT_TRUE(NavigateToURL(shell(), url));
clamyf1ccb4d2015-01-28 17:40:38763 EXPECT_EQ(url, observer.last_navigation_url());
764 EXPECT_TRUE(observer.last_navigation_succeeded());
765 }
766
arthursonzognia26fa9a2020-11-03 10:16:47767 RenderFrameHost* initial_rfh = current_frame_host();
Nasko Oskov93e7c55c2018-12-19 01:59:29768 url::Origin initial_origin = initial_rfh->GetLastCommittedOrigin();
Dave Tapuskae9b7c0f72023-11-06 16:38:01769 auto initial_rfh_global_token = initial_rfh->GetGlobalFrameToken();
clamyf1ccb4d2015-01-28 17:40:38770
771 // Simulate clicking on a cross-site link.
772 {
arthursonzognia26fa9a2020-11-03 10:16:47773 TestNavigationObserver observer(web_contents());
Avi Drissmanc91bd8e2021-04-19 23:58:44774 const char kReplacePortNumber[] = "setPortNumber(%d);";
avib7348942015-12-25 20:57:10775 uint16_t port_number = embedded_test_server()->port();
clamyf1ccb4d2015-01-28 17:40:38776 GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html");
Avi Drissmanc91bd8e2021-04-19 23:58:44777 EXPECT_EQ(true, EvalJs(shell(), base::StringPrintf(kReplacePortNumber,
778 port_number)));
779 EXPECT_EQ(true, EvalJs(shell(), "clickCrossSiteLink();"));
arthursonzognia26fa9a2020-11-03 10:16:47780 EXPECT_TRUE(WaitForLoadStop(web_contents()));
clamyf1ccb4d2015-01-28 17:40:38781 EXPECT_EQ(url, observer.last_navigation_url());
782 EXPECT_TRUE(observer.last_navigation_succeeded());
Nasko Oskov93e7c55c2018-12-19 01:59:29783 EXPECT_EQ(initial_origin, observer.last_initiator_origin().value());
Antonio Sartori9a82f6f32020-12-14 09:22:45784 EXPECT_TRUE(observer.last_initiator_frame_token().has_value());
Dave Tapuskae9b7c0f72023-11-06 16:38:01785 EXPECT_EQ(initial_rfh_global_token.frame_token,
Antonio Sartori9a82f6f32020-12-14 09:22:45786 observer.last_initiator_frame_token().value());
Dave Tapuskae9b7c0f72023-11-06 16:38:01787 EXPECT_EQ(initial_rfh_global_token.child_id,
788 observer.last_initiator_process_id());
clamyf1ccb4d2015-01-28 17:40:38789 }
790
Sharon Yangc7347222025-06-06 21:17:17791 // The RenderFrameHost should have changed unless strict SiteInstances (either
792 // full site isolation or default SiteInstanceGroups) and proactive
793 // BrowsingInstance swaps are both disabled.
794 if (!AreStrictSiteInstancesEnabled() &&
Aaron Colwell5fb878042020-12-17 19:48:44795 !CanCrossSiteNavigationsProactivelySwapBrowsingInstances()) {
arthursonzognia26fa9a2020-11-03 10:16:47796 EXPECT_EQ(initial_rfh, current_frame_host());
Aaron Colwell5fb878042020-12-17 19:48:44797 } else {
798 EXPECT_NE(initial_rfh, current_frame_host());
clamy61dfb232016-02-26 18:08:49799 }
clamyf1ccb4d2015-01-28 17:40:38800}
801
Arthur Sonzogni57d111142018-09-19 15:40:13802// Ensure navigation failures are handled.
Minggang Wang83f7f3712019-11-16 11:28:16803IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, FailedNavigation) {
clamy62b271d2015-04-16 11:54:57804 // Perform a navigation with no live renderer.
805 {
arthursonzognia26fa9a2020-11-03 10:16:47806 TestNavigationObserver observer(web_contents());
clamy62b271d2015-04-16 11:54:57807 GURL url(embedded_test_server()->GetURL("/title1.html"));
Alex Moshchukaeb20fe32019-09-25 17:40:01808 EXPECT_TRUE(NavigateToURL(shell(), url));
clamy62b271d2015-04-16 11:54:57809 EXPECT_EQ(url, observer.last_navigation_url());
810 EXPECT_TRUE(observer.last_navigation_succeeded());
Ali Beyadb21baf5f2022-03-31 00:58:06811 // Check the UKM for navigation responses received.
812 EXPECT_EQ(1u, test_ukm_recorder()
813 .GetEntriesByName("Navigation.ReceivedResponse")
814 .size());
clamy62b271d2015-04-16 11:54:57815 }
816
817 // Now navigate to an unreachable url.
818 {
arthursonzognia26fa9a2020-11-03 10:16:47819 TestNavigationObserver observer(web_contents());
clamyf77e9ff2018-01-18 15:53:43820 GURL error_url(embedded_test_server()->GetURL("/close-socket"));
Gabriel Charettee7cdc5cd2020-05-27 23:35:05821 GetIOThreadTaskRunner({})->PostTask(
822 FROM_HERE, base::BindOnce(&net::URLRequestFailedJob::AddUrlHandler));
Alex Moshchukaeb20fe32019-09-25 17:40:01823 EXPECT_FALSE(NavigateToURL(shell(), error_url));
clamy62b271d2015-04-16 11:54:57824 EXPECT_EQ(error_url, observer.last_navigation_url());
825 NavigationEntry* entry =
arthursonzognia26fa9a2020-11-03 10:16:47826 web_contents()->GetController().GetLastCommittedEntry();
clamy62b271d2015-04-16 11:54:57827 EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType());
Ali Beyadb21baf5f2022-03-31 00:58:06828 // No response on an unreachable URL, so the ReceivedResponse event should
829 // not have increased.
830 EXPECT_EQ(1u, test_ukm_recorder()
831 .GetEntriesByName("Navigation.ReceivedResponse")
832 .size());
clamy62b271d2015-04-16 11:54:57833 }
834}
835
Arthur Sonzogni57d111142018-09-19 15:40:13836// Ensure that browser initiated navigations to view-source URLs works.
Minggang Wang83f7f3712019-11-16 11:28:16837IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
meacerce6b66032016-06-02 20:56:05838 ViewSourceNavigation_BrowserInitiated) {
arthursonzognia26fa9a2020-11-03 10:16:47839 TestNavigationObserver observer(web_contents());
meacerce6b66032016-06-02 20:56:05840 GURL url(embedded_test_server()->GetURL("/title1.html"));
841 GURL view_source_url(content::kViewSourceScheme + std::string(":") +
842 url.spec());
Alex Moshchukaeb20fe32019-09-25 17:40:01843 EXPECT_TRUE(NavigateToURL(shell(), view_source_url));
meacerce6b66032016-06-02 20:56:05844 EXPECT_EQ(url, observer.last_navigation_url());
845 EXPECT_TRUE(observer.last_navigation_succeeded());
846}
847
Arthur Sonzogni57d111142018-09-19 15:40:13848// Ensure that content initiated navigations to view-sources URLs are blocked.
Minggang Wang83f7f3712019-11-16 11:28:16849IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
meacerce6b66032016-06-02 20:56:05850 ViewSourceNavigation_RendererInitiated) {
arthursonzognia26fa9a2020-11-03 10:16:47851 TestNavigationObserver observer(web_contents());
meacerce6b66032016-06-02 20:56:05852 GURL kUrl(embedded_test_server()->GetURL("/simple_links.html"));
Alex Moshchukaeb20fe32019-09-25 17:40:01853 EXPECT_TRUE(NavigateToURL(shell(), kUrl));
meacerce6b66032016-06-02 20:56:05854 EXPECT_EQ(kUrl, observer.last_navigation_url());
855 EXPECT_TRUE(observer.last_navigation_succeeded());
856
arthursonzognia26fa9a2020-11-03 10:16:47857 WebContentsConsoleObserver console_observer(web_contents());
Devlin Cronin90e6b3b2020-01-08 23:43:49858 console_observer.SetPattern(
859 "Not allowed to load local resource: view-source:about:blank");
meacerce6b66032016-06-02 20:56:05860
Avi Drissmanc91bd8e2021-04-19 23:58:44861 EXPECT_EQ(true, EvalJs(web_contents(), "clickViewSourceLink();"));
Fergal Daly7723f9d2022-10-29 07:03:13862 ASSERT_TRUE(console_observer.Wait());
meacerce6b66032016-06-02 20:56:05863 // Original page shouldn't navigate away.
Sharon Yangf08acfc2022-01-04 20:49:01864 EXPECT_EQ(kUrl, web_contents()->GetLastCommittedURL());
meacerce6b66032016-06-02 20:56:05865 EXPECT_FALSE(shell()
866 ->web_contents()
867 ->GetController()
868 .GetLastCommittedEntry()
869 ->IsViewSourceMode());
870}
871
Mike Westb96a52a2019-07-24 17:08:50872// Ensure that content initiated navigations to googlechrome: URLs are blocked.
Minggang Wang83f7f3712019-11-16 11:28:16873IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
Mike Westb96a52a2019-07-24 17:08:50874 GoogleChromeNavigation_RendererInitiated) {
arthursonzognia26fa9a2020-11-03 10:16:47875 TestNavigationObserver observer(web_contents());
Mike Westb96a52a2019-07-24 17:08:50876 GURL kUrl(embedded_test_server()->GetURL("/simple_links.html"));
Alex Moshchukaeb20fe32019-09-25 17:40:01877 EXPECT_TRUE(NavigateToURL(shell(), kUrl));
Mike Westb96a52a2019-07-24 17:08:50878 EXPECT_EQ(kUrl, observer.last_navigation_url());
879 EXPECT_TRUE(observer.last_navigation_succeeded());
880
arthursonzognia26fa9a2020-11-03 10:16:47881 WebContentsConsoleObserver console_observer(web_contents());
Devlin Cronin90e6b3b2020-01-08 23:43:49882 console_observer.SetPattern(
883 "Not allowed to load local resource: googlechrome://");
Mike Westb96a52a2019-07-24 17:08:50884
Avi Drissmanc91bd8e2021-04-19 23:58:44885 EXPECT_EQ(true, EvalJs(web_contents(), "clickGoogleChromeLink();"));
Fergal Daly7723f9d2022-10-29 07:03:13886 ASSERT_TRUE(console_observer.Wait());
Mike Westb96a52a2019-07-24 17:08:50887 // Original page shouldn't navigate away.
Sharon Yangf08acfc2022-01-04 20:49:01888 EXPECT_EQ(kUrl, web_contents()->GetLastCommittedURL());
Mike Westb96a52a2019-07-24 17:08:50889}
890
jam03e86d022016-10-06 01:28:10891// Ensure that closing a page by running its beforeunload handler doesn't hang
892// if there's an ongoing navigation.
Minggang Wang83f7f3712019-11-16 11:28:16893IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, UnloadDuringNavigation) {
John Delaney9f33f2202021-02-22 22:03:44894 WebContentsDestroyedWatcher close_observer(web_contents());
jamcb4ae152017-05-19 01:35:51895 GURL url("chrome://resources/css/tabs.css");
arthursonzognia26fa9a2020-11-03 10:16:47896 NavigationHandleObserver handle_observer(web_contents(), url);
jamcb4ae152017-05-19 01:35:51897 shell()->LoadURL(url);
arthursonzognia26fa9a2020-11-03 10:16:47898 web_contents()->DispatchBeforeUnload(false /* auto_cancel */);
jam03e86d022016-10-06 01:28:10899 close_observer.Wait();
jamcb4ae152017-05-19 01:35:51900 EXPECT_EQ(net::ERR_ABORTED, handle_observer.net_error_code());
jam03e86d022016-10-06 01:28:10901}
902
clamya6d343f92017-03-22 18:20:39903// Ensure that the referrer of a navigation is properly sanitized.
Minggang Wang83f7f3712019-11-16 11:28:16904IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, SanitizeReferrer) {
clamya6d343f92017-03-22 18:20:39905 const GURL kInsecureUrl(embedded_test_server()->GetURL("/title1.html"));
906 const Referrer kSecureReferrer(
907 GURL("https://secure-url.com"),
Richard Li49fe04d2018-10-21 09:07:19908 network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade);
clamya6d343f92017-03-22 18:20:39909
910 // Navigate to an insecure url with a secure referrer with a policy of no
911 // referrer on downgrades. The referrer url should be rewritten right away.
912 NavigationController::LoadURLParams load_params(kInsecureUrl);
913 load_params.referrer = kSecureReferrer;
arthursonzognia26fa9a2020-11-03 10:16:47914 TestNavigationManager manager(web_contents(), kInsecureUrl);
915 web_contents()->GetController().LoadURLWithParams(load_params);
clamya6d343f92017-03-22 18:20:39916 EXPECT_TRUE(manager.WaitForRequestStart());
917
918 // The referrer should have been sanitized.
arthursonzognia26fa9a2020-11-03 10:16:47919 ASSERT_TRUE(main_frame()->navigation_request());
920 EXPECT_EQ(GURL(), main_frame()->navigation_request()->GetReferrer().url);
clamya6d343f92017-03-22 18:20:39921
922 // The navigation should commit without being blocked.
923 EXPECT_TRUE(manager.WaitForResponse());
Fergal Daly83bc3cd2023-01-18 00:22:54924 ASSERT_TRUE(manager.WaitForNavigationFinished());
arthursonzognia26fa9a2020-11-03 10:16:47925 EXPECT_EQ(kInsecureUrl, web_contents()->GetLastCommittedURL());
clamya6d343f92017-03-22 18:20:39926}
927
Dominic Farolino191ccfc52019-09-19 17:49:42928// Ensure the correctness of a navigation request's referrer. This is a
929// regression test for https://crbug.com/1004083.
930IN_PROC_BROWSER_TEST_P(NavigationBrowserTestReferrerPolicy, ReferrerPolicy) {
931 const GURL kDestination(embedded_test_server()->GetURL("/title1.html"));
932 const GURL kReferrerURL(embedded_test_server()->GetURL("/referrer-page"));
933 const url::Origin kReferrerOrigin = url::Origin::Create(kReferrerURL);
934
935 // It is possible that the referrer URL does not match what the policy
936 // demands (e.g., non-empty URL and kNever policy), so we'll test that the
937 // correct referrer is generated, and that the navigation succeeds.
938 const Referrer referrer(kReferrerURL, GetReferrerPolicy());
939
940 // Navigate to a resource whose destination URL is same-origin with the
941 // navigation's referrer. The final referrer should be generated correctly.
942 NavigationController::LoadURLParams load_params(kDestination);
943 load_params.referrer = referrer;
arthursonzognia26fa9a2020-11-03 10:16:47944 TestNavigationManager manager(web_contents(), kDestination);
945 web_contents()->GetController().LoadURLWithParams(load_params);
Dominic Farolino191ccfc52019-09-19 17:49:42946 EXPECT_TRUE(manager.WaitForRequestStart());
947
948 // The referrer should have been sanitized.
arthursonzognia26fa9a2020-11-03 10:16:47949 ASSERT_TRUE(main_frame()->navigation_request());
Dominic Farolino191ccfc52019-09-19 17:49:42950 switch (GetReferrerPolicy()) {
951 case network::mojom::ReferrerPolicy::kAlways:
952 case network::mojom::ReferrerPolicy::kDefault:
953 case network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade:
954 case network::mojom::ReferrerPolicy::kOriginWhenCrossOrigin:
Dominic Farolino361353df2020-03-03 06:01:10955 case network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin:
Dominic Farolino191ccfc52019-09-19 17:49:42956 case network::mojom::ReferrerPolicy::kSameOrigin:
arthursonzognia26fa9a2020-11-03 10:16:47957 EXPECT_EQ(kReferrerURL,
958 main_frame()->navigation_request()->GetReferrer().url);
Dominic Farolino191ccfc52019-09-19 17:49:42959 break;
960 case network::mojom::ReferrerPolicy::kNever:
arthursonzognia26fa9a2020-11-03 10:16:47961 EXPECT_EQ(GURL(), main_frame()->navigation_request()->GetReferrer().url);
Dominic Farolino191ccfc52019-09-19 17:49:42962 break;
963 case network::mojom::ReferrerPolicy::kOrigin:
964 case network::mojom::ReferrerPolicy::kStrictOrigin:
Mohamed Abdelhalimf03d4a22019-10-01 13:34:31965 EXPECT_EQ(kReferrerOrigin.GetURL(),
arthursonzognia26fa9a2020-11-03 10:16:47966 main_frame()->navigation_request()->GetReferrer().url);
Dominic Farolino191ccfc52019-09-19 17:49:42967 break;
968 }
969
970 // The navigation should commit without being blocked.
971 EXPECT_TRUE(manager.WaitForResponse());
Fergal Daly83bc3cd2023-01-18 00:22:54972 ASSERT_TRUE(manager.WaitForNavigationFinished());
arthursonzognia26fa9a2020-11-03 10:16:47973 EXPECT_EQ(kDestination, web_contents()->GetLastCommittedURL());
Dominic Farolino191ccfc52019-09-19 17:49:42974}
975
nasko5d30e832017-05-24 23:43:02976// Test to verify that an exploited renderer process trying to upload a file
977// it hasn't been explicitly granted permissions to is correctly terminated.
Minggang Wang83f7f3712019-11-16 11:28:16978IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, PostUploadIllegalFilePath) {
nasko5d30e832017-05-24 23:43:02979 GURL form_url(
980 embedded_test_server()->GetURL("/form_that_posts_to_echoall.html"));
981 EXPECT_TRUE(NavigateToURL(shell(), form_url));
982
nasko5d30e832017-05-24 23:43:02983 // Prepare a file for the upload form.
Francois Doray21cd53192018-08-21 13:32:35984 base::ScopedAllowBlockingForTesting allow_blocking;
nasko5d30e832017-05-24 23:43:02985 base::ScopedTempDir temp_dir;
986 base::FilePath file_path;
987 std::string file_content("test-file-content");
988 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
989 ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.GetPath(), &file_path));
Lei Zhang9989f272020-05-11 19:21:21990 ASSERT_TRUE(base::WriteFile(file_path, file_content));
nasko5d30e832017-05-24 23:43:02991
Kent Tamura8c9e0562018-11-06 07:02:19992 base::RunLoop run_loop;
nasko5d30e832017-05-24 23:43:02993 // Fill out the form to refer to the test file.
994 std::unique_ptr<FileChooserDelegate> delegate(
Kent Tamura8c9e0562018-11-06 07:02:19995 new FileChooserDelegate(file_path, run_loop.QuitClosure()));
arthursonzognia26fa9a2020-11-03 10:16:47996 web_contents()->SetDelegate(delegate.get());
Avi Drissmanc91bd8e2021-04-19 23:58:44997 EXPECT_TRUE(
998 ExecJs(web_contents(), "document.getElementById('file').click();"));
Kent Tamura8c9e0562018-11-06 07:02:19999 run_loop.Run();
nasko5d30e832017-05-24 23:43:021000
1001 // Ensure that the process is allowed to access to the chosen file and
1002 // does not have access to the other file name.
1003 EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
Emily Andrewsd15fd762024-12-10 20:41:541004 current_frame_host()->GetProcess()->GetDeprecatedID(), file_path));
nasko5d30e832017-05-24 23:43:021005
1006 // Revoke the access to the file and submit the form. The renderer process
1007 // should be terminated.
arthursonzognia26fa9a2020-11-03 10:16:471008 RenderProcessHostBadIpcMessageWaiter process_kill_waiter(
1009 current_frame_host()->GetProcess());
nasko5d30e832017-05-24 23:43:021010 ChildProcessSecurityPolicyImpl* security_policy =
1011 ChildProcessSecurityPolicyImpl::GetInstance();
arthursonzognia26fa9a2020-11-03 10:16:471012 security_policy->RevokeAllPermissionsForFile(
Emily Andrewsd15fd762024-12-10 20:41:541013 current_frame_host()->GetProcess()->GetDeprecatedID(), file_path);
nasko5d30e832017-05-24 23:43:021014
Avi Drissmanc91bd8e2021-04-19 23:58:441015 // Use EvalJs and respond back to the browser process before doing the actual
1016 // submission. This will ensure that the process termination is guaranteed to
1017 // arrive after the response from the executed JavaScript.
Chris Fredricksonb854bbf2023-03-27 17:27:441018 EXPECT_EQ(
1019 true,
1020 EvalJs(
1021 shell(),
1022 "setTimeout(() => document.getElementById('file-form').submit(), 0);"
1023 "true;"));
Lukasz Anforowicz445abd42019-01-25 21:27:271024 EXPECT_EQ(bad_message::ILLEGAL_UPLOAD_PARAMS, process_kill_waiter.Wait());
nasko5d30e832017-05-24 23:43:021025}
1026
nasko5ff4eea2017-05-25 02:51:001027// Test case to verify that redirects to data: URLs are properly disallowed,
1028// even when invoked through a reload.
1029// See https://crbug.com/723796.
Minggang Wang83f7f3712019-11-16 11:28:161030IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
nasko5ff4eea2017-05-25 02:51:001031 VerifyBlockedErrorPageURL_Reload) {
arthursonzognia26fa9a2020-11-03 10:16:471032 NavigationControllerImpl& controller = web_contents()->GetController();
nasko5ff4eea2017-05-25 02:51:001033
1034 GURL start_url(embedded_test_server()->GetURL("/title1.html"));
1035 EXPECT_TRUE(NavigateToURL(shell(), start_url));
1036 EXPECT_EQ(0, controller.GetLastCommittedEntryIndex());
1037
1038 // Navigate to an URL, which redirects to a data: URL, since it is an
1039 // unsafe redirect and will result in a blocked navigation and error page.
1040 GURL redirect_to_blank_url(
1041 embedded_test_server()->GetURL("/server-redirect?data:text/html,Hello!"));
1042 EXPECT_FALSE(NavigateToURL(shell(), redirect_to_blank_url));
1043 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1044 EXPECT_EQ(PAGE_TYPE_ERROR, controller.GetLastCommittedEntry()->GetPageType());
1045
arthursonzognia26fa9a2020-11-03 10:16:471046 TestNavigationObserver reload_observer(web_contents());
Avi Drissmanc91bd8e2021-04-19 23:58:441047 EXPECT_TRUE(ExecJs(shell(), "location.reload()"));
nasko5ff4eea2017-05-25 02:51:001048 reload_observer.Wait();
1049
Charles Reis56a84aa672017-10-26 20:10:421050 // The expectation is that the blocked URL is present in the NavigationEntry,
1051 // and shows up in both GetURL and GetVirtualURL.
nasko5ff4eea2017-05-25 02:51:001052 EXPECT_EQ(1, controller.GetLastCommittedEntryIndex());
1053 EXPECT_FALSE(
1054 controller.GetLastCommittedEntry()->GetURL().SchemeIs(url::kDataScheme));
davidbend894710b2017-06-06 19:28:301055 EXPECT_EQ(redirect_to_blank_url,
Charles Reis56a84aa672017-10-26 20:10:421056 controller.GetLastCommittedEntry()->GetURL());
1057 EXPECT_EQ(redirect_to_blank_url,
davidbend894710b2017-06-06 19:28:301058 controller.GetLastCommittedEntry()->GetVirtualURL());
nasko5ff4eea2017-05-25 02:51:001059}
1060
Alison Gale81f4f2c72024-04-22 19:33:311061// TODO(crbug.com/40924471): Test is flaky on Android, Linux.
NicolĂ¡s Peña15943c92023-11-07 17:14:361062#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
Salvador Guerrerofe4962a2023-07-25 21:44:591063#define MAYBE_BackFollowedByReload DISABLED_BackFollowedByReload
1064#else
1065#define MAYBE_BackFollowedByReload BackFollowedByReload
1066#endif
1067IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, MAYBE_BackFollowedByReload) {
clamyce7a6152017-09-18 16:55:111068 // First, make two history entries.
1069 GURL url1(embedded_test_server()->GetURL("/title1.html"));
1070 GURL url2(embedded_test_server()->GetURL("/title2.html"));
Alex Moshchukaeb20fe32019-09-25 17:40:011071 EXPECT_TRUE(NavigateToURL(shell(), url1));
1072 EXPECT_TRUE(NavigateToURL(shell(), url2));
clamyce7a6152017-09-18 16:55:111073
1074 // Then execute a back navigation in Javascript followed by a reload.
arthursonzognia26fa9a2020-11-03 10:16:471075 TestNavigationObserver navigation_observer(web_contents());
Avi Drissmanc91bd8e2021-04-19 23:58:441076 EXPECT_TRUE(ExecJs(web_contents(), "history.back(); location.reload();"));
clamyce7a6152017-09-18 16:55:111077 navigation_observer.Wait();
1078
1079 // The reload should have cancelled the back navigation, and the last
1080 // committed URL should still be the second URL.
arthursonzognia26fa9a2020-11-03 10:16:471081 EXPECT_EQ(url2, web_contents()->GetLastCommittedURL());
clamyce7a6152017-09-18 16:55:111082}
1083
arthursonzognid23bce42018-02-01 16:49:001084// Test that a navigation response can be entirely fetched, even after the
1085// NavigationURLLoader has been deleted.
Minggang Wang83f7f3712019-11-16 11:28:161086IN_PROC_BROWSER_TEST_F(NavigationBaseBrowserTest,
arthursonzognid23bce42018-02-01 16:49:001087 FetchResponseAfterNavigationURLLoaderDeleted) {
1088 net::test_server::ControllableHttpResponse response(embedded_test_server(),
1089 "/main_document");
1090 ASSERT_TRUE(embedded_test_server()->Start());
1091
1092 // Load a new document.
1093 GURL url(embedded_test_server()->GetURL("/main_document"));
arthursonzognia26fa9a2020-11-03 10:16:471094 TestNavigationManager navigation_manager(web_contents(), url);
arthursonzognid23bce42018-02-01 16:49:001095 shell()->LoadURL(url);
1096
1097 // The navigation starts.
1098 EXPECT_TRUE(navigation_manager.WaitForRequestStart());
1099 navigation_manager.ResumeNavigation();
1100
1101 // A NavigationRequest exists at this point.
arthursonzognia26fa9a2020-11-03 10:16:471102 EXPECT_TRUE(main_frame()->navigation_request());
arthursonzognid23bce42018-02-01 16:49:001103
1104 // The response's headers are received.
1105 response.WaitForRequest();
1106 response.Send(
1107 "HTTP/1.1 200 OK\r\n"
1108 "Content-Type: text/html; charset=utf-8\r\n"
1109 "\r\n"
1110 "...");
1111 EXPECT_TRUE(navigation_manager.WaitForResponse());
1112 navigation_manager.ResumeNavigation();
1113
1114 // The renderer commits the navigation and the browser deletes its
1115 // NavigationRequest.
Fergal Daly83bc3cd2023-01-18 00:22:541116 ASSERT_TRUE(navigation_manager.WaitForNavigationFinished());
arthursonzognia26fa9a2020-11-03 10:16:471117 EXPECT_FALSE(main_frame()->navigation_request());
arthursonzognid23bce42018-02-01 16:49:001118
1119 // The NavigationURLLoader has been deleted by now. Check that the renderer
1120 // can still receive more bytes.
arthursonzognia26fa9a2020-11-03 10:16:471121 DOMMessageQueue dom_message_queue(web_contents());
arthursonzognid23bce42018-02-01 16:49:001122 response.Send(
1123 "<script>window.domAutomationController.send('done');</script>");
1124 std::string done;
1125 EXPECT_TRUE(dom_message_queue.WaitForMessage(&done));
1126 EXPECT_EQ("\"done\"", done);
1127}
1128
Matt Menke6e2bf4e2021-03-05 14:45:451129IN_PROC_BROWSER_TEST_F(NetworkIsolationNavigationBrowserTest,
Shivani Sharma1b5366c62019-07-16 20:08:131130 BrowserNavigationNetworkIsolationKey) {
Josh Karlinbe37f912018-12-07 01:05:141131 GURL url(embedded_test_server()->GetURL("/title1.html"));
Shivani Sharma8ae506c2019-07-21 21:08:271132 url::Origin origin = url::Origin::Create(url);
Matt Menked5dc1a22020-03-13 18:46:141133 URLLoaderMonitor monitor({url});
1134 EXPECT_TRUE(NavigateToURL(shell(), url));
1135 monitor.WaitForUrls();
Josh Karlinbe37f912018-12-07 01:05:141136
Arthur Sonzognic686e8f2024-01-11 08:36:371137 std::optional<network::ResourceRequest> request = monitor.GetRequestInfo(url);
Matt Menked5dc1a22020-03-13 18:46:141138 ASSERT_TRUE(request->trusted_params);
Matt Menke37c850e02020-04-20 22:17:451139 EXPECT_TRUE(net::IsolationInfo::Create(
shivanigithub4e78015f592020-10-21 13:26:231140 net::IsolationInfo::RequestType::kMainFrame, origin, origin,
Chris Fredricksone7ba5212023-09-26 14:54:421141 net::SiteForCookies::FromOrigin(origin))
Matt Menke37c850e02020-04-20 22:17:451142 .IsEqualForTesting(request->trusted_params->isolation_info));
Josh Karlinbe37f912018-12-07 01:05:141143}
1144
Matt Menke6e2bf4e2021-03-05 14:45:451145IN_PROC_BROWSER_TEST_F(NetworkIsolationNavigationBrowserTest,
Matt Menke37c850e02020-04-20 22:17:451146 RenderNavigationIsolationInfo) {
Josh Karlinbe37f912018-12-07 01:05:141147 GURL url(embedded_test_server()->GetURL("/title2.html"));
Shivani Sharma8ae506c2019-07-21 21:08:271148 url::Origin origin = url::Origin::Create(url);
Matt Menked5dc1a22020-03-13 18:46:141149 EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
1150 URLLoaderMonitor monitor({url});
1151 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), url));
1152 monitor.WaitForUrls();
Josh Karlinbe37f912018-12-07 01:05:141153
Arthur Sonzognic686e8f2024-01-11 08:36:371154 std::optional<network::ResourceRequest> request = monitor.GetRequestInfo(url);
Matt Menked5dc1a22020-03-13 18:46:141155 ASSERT_TRUE(request->trusted_params);
Matt Menke37c850e02020-04-20 22:17:451156 EXPECT_TRUE(net::IsolationInfo::Create(
shivanigithub4e78015f592020-10-21 13:26:231157 net::IsolationInfo::RequestType::kMainFrame, origin, origin,
Chris Fredricksone7ba5212023-09-26 14:54:421158 net::SiteForCookies::FromOrigin(origin))
Matt Menke37c850e02020-04-20 22:17:451159 .IsEqualForTesting(request->trusted_params->isolation_info));
Josh Karlinbe37f912018-12-07 01:05:141160}
1161
Matt Menke6e2bf4e2021-03-05 14:45:451162IN_PROC_BROWSER_TEST_F(NetworkIsolationNavigationBrowserTest,
Matt Menke37c850e02020-04-20 22:17:451163 SubframeIsolationInfo) {
Josh Karlinbe37f912018-12-07 01:05:141164 GURL url(embedded_test_server()->GetURL("/page_with_iframe.html"));
1165 GURL iframe_document = embedded_test_server()->GetURL("/title1.html");
Shivani Sharma8ae506c2019-07-21 21:08:271166 url::Origin origin = url::Origin::Create(url);
1167 url::Origin iframe_origin = url::Origin::Create(iframe_document);
Matt Menked5dc1a22020-03-13 18:46:141168 URLLoaderMonitor monitor({iframe_document});
1169 EXPECT_TRUE(NavigateToURL(shell(), url));
1170 monitor.WaitForUrls();
Josh Karlinbe37f912018-12-07 01:05:141171
Arthur Sonzognic686e8f2024-01-11 08:36:371172 std::optional<network::ResourceRequest> main_frame_request =
Matt Menked5dc1a22020-03-13 18:46:141173 monitor.GetRequestInfo(url);
1174 ASSERT_TRUE(main_frame_request.has_value());
1175 ASSERT_TRUE(main_frame_request->trusted_params);
Matt Menke37c850e02020-04-20 22:17:451176 EXPECT_TRUE(net::IsolationInfo::Create(
shivanigithub4e78015f592020-10-21 13:26:231177 net::IsolationInfo::RequestType::kMainFrame, origin, origin,
Joshua Thomas81febb32025-06-17 18:16:321178 net::SiteForCookies::FromOrigin(origin),
1179 /*nonce=*/std::nullopt,
1180 net::NetworkIsolationPartition::kGeneral,
1181 net::IsolationInfo::FrameAncestorRelation::kSameOrigin)
Matt Menke37c850e02020-04-20 22:17:451182 .IsEqualForTesting(
1183 main_frame_request->trusted_params->isolation_info));
Matt Menked5dc1a22020-03-13 18:46:141184
Arthur Sonzognic686e8f2024-01-11 08:36:371185 std::optional<network::ResourceRequest> iframe_request =
Matt Menked5dc1a22020-03-13 18:46:141186 monitor.GetRequestInfo(iframe_document);
1187 ASSERT_TRUE(iframe_request->trusted_params);
Matt Menke37c850e02020-04-20 22:17:451188 EXPECT_TRUE(
Joshua Thomas81febb32025-06-17 18:16:321189 net::IsolationInfo::Create(
1190 net::IsolationInfo::RequestType::kSubFrame, origin, iframe_origin,
1191 net::SiteForCookies::FromOrigin(origin), /*nonce=*/std::nullopt,
1192 net::NetworkIsolationPartition::kGeneral,
1193 net::IsolationInfo::FrameAncestorRelation::kSameOrigin)
Matt Menke37c850e02020-04-20 22:17:451194 .IsEqualForTesting(iframe_request->trusted_params->isolation_info));
Josh Karlinbe37f912018-12-07 01:05:141195}
1196
Steven Bingler184de022019-05-07 14:55:161197// Tests that the initiator is not set for a browser initiated top frame
1198// navigation.
Minggang Wang83f7f3712019-11-16 11:28:161199IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, BrowserNavigationInitiator) {
Steven Bingler184de022019-05-07 14:55:161200 GURL url(embedded_test_server()->GetURL("/title1.html"));
1201
Matt Menked5dc1a22020-03-13 18:46:141202 URLLoaderMonitor monitor;
Steven Bingler184de022019-05-07 14:55:161203
1204 // Perform the actual navigation.
1205 EXPECT_TRUE(NavigateToURL(shell(), url));
Steven Bingler184de022019-05-07 14:55:161206
Arthur Sonzognic686e8f2024-01-11 08:36:371207 std::optional<network::ResourceRequest> request = monitor.GetRequestInfo(url);
Matt Menked5dc1a22020-03-13 18:46:141208 ASSERT_TRUE(request.has_value());
1209 ASSERT_FALSE(request->request_initiator.has_value());
Steven Bingler184de022019-05-07 14:55:161210}
1211
1212// Test that the initiator is set to the starting page when a renderer initiated
1213// navigation goes from the starting page to another page.
Minggang Wang83f7f3712019-11-16 11:28:161214IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, RendererNavigationInitiator) {
Steven Bingler184de022019-05-07 14:55:161215 GURL starting_page(embedded_test_server()->GetURL("a.com", "/title1.html"));
1216 url::Origin starting_page_origin;
1217 starting_page_origin = starting_page_origin.Create(starting_page);
1218
Alex Moshchukaeb20fe32019-09-25 17:40:011219 EXPECT_TRUE(NavigateToURL(shell(), starting_page));
Steven Bingler184de022019-05-07 14:55:161220
1221 GURL url(embedded_test_server()->GetURL("/title2.html"));
1222
Matt Menked5dc1a22020-03-13 18:46:141223 URLLoaderMonitor monitor;
Steven Bingler184de022019-05-07 14:55:161224
1225 // Perform the actual navigation.
1226 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), url));
Steven Bingler184de022019-05-07 14:55:161227
Arthur Sonzognic686e8f2024-01-11 08:36:371228 std::optional<network::ResourceRequest> request = monitor.GetRequestInfo(url);
Matt Menked5dc1a22020-03-13 18:46:141229 ASSERT_TRUE(request.has_value());
1230 EXPECT_EQ(starting_page_origin, request->request_initiator);
Steven Bingler184de022019-05-07 14:55:161231}
1232
1233// Test that the initiator is set to the starting page when a sub frame is
1234// navigated by Javascript from some starting page to another page.
Minggang Wang83f7f3712019-11-16 11:28:161235IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, SubFrameJsNavigationInitiator) {
Steven Bingler184de022019-05-07 14:55:161236 GURL starting_page(embedded_test_server()->GetURL("/frame_tree/top.html"));
Alex Moshchukaeb20fe32019-09-25 17:40:011237 EXPECT_TRUE(NavigateToURL(shell(), starting_page));
Steven Bingler184de022019-05-07 14:55:161238
arthursonzognia26fa9a2020-11-03 10:16:471239 // The main_frame() and subframe should each have a live RenderFrame.
1240 EXPECT_TRUE(main_frame()
1241 ->current_frame_host()
1242 ->render_view_host()
1243 ->IsRenderViewLive());
1244 EXPECT_TRUE(main_frame()->current_frame_host()->IsRenderFrameLive());
Steven Bingler184de022019-05-07 14:55:161245 EXPECT_TRUE(
arthursonzognia26fa9a2020-11-03 10:16:471246 main_frame()->child_at(0)->current_frame_host()->IsRenderFrameLive());
Steven Bingler184de022019-05-07 14:55:161247
1248 GURL url(embedded_test_server()->GetURL("a.com", "/title1.html"));
1249
Matt Menked5dc1a22020-03-13 18:46:141250 URLLoaderMonitor monitor({url});
Steven Bingler184de022019-05-07 14:55:161251 std::string script = "location.href='" + url.spec() + "'";
1252
1253 // Perform the actual navigation.
arthursonzognia26fa9a2020-11-03 10:16:471254 EXPECT_TRUE(ExecJs(main_frame()->child_at(0)->current_frame_host(), script));
Steven Bingler184de022019-05-07 14:55:161255
arthursonzognia26fa9a2020-11-03 10:16:471256 EXPECT_TRUE(main_frame()
1257 ->current_frame_host()
1258 ->render_view_host()
1259 ->IsRenderViewLive());
1260 EXPECT_TRUE(main_frame()->current_frame_host()->IsRenderFrameLive());
Steven Bingler184de022019-05-07 14:55:161261 EXPECT_TRUE(
arthursonzognia26fa9a2020-11-03 10:16:471262 main_frame()->child_at(0)->current_frame_host()->IsRenderFrameLive());
Steven Bingler184de022019-05-07 14:55:161263
1264 url::Origin starting_page_origin;
1265 starting_page_origin = starting_page_origin.Create(starting_page);
1266
Matt Menked5dc1a22020-03-13 18:46:141267 monitor.WaitForUrls();
Arthur Sonzognic686e8f2024-01-11 08:36:371268 std::optional<network::ResourceRequest> request = monitor.GetRequestInfo(url);
Matt Menked5dc1a22020-03-13 18:46:141269 EXPECT_EQ(starting_page_origin, request->request_initiator);
Steven Bingler184de022019-05-07 14:55:161270}
1271
1272// Test that the initiator is set to the starting page when a sub frame,
1273// selected by Id, is navigated by Javascript from some starting page to another
1274// page.
Minggang Wang83f7f3712019-11-16 11:28:161275IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
Steven Bingler184de022019-05-07 14:55:161276 SubframeNavigationByTopFrameInitiator) {
1277 // Go to a page on a.com with an iframe that is on b.com
1278 GURL starting_page(embedded_test_server()->GetURL(
1279 "a.com", "/cross_site_iframe_factory.html?a(b)"));
Alex Moshchukaeb20fe32019-09-25 17:40:011280 EXPECT_TRUE(NavigateToURL(shell(), starting_page));
Steven Bingler184de022019-05-07 14:55:161281
arthursonzognia26fa9a2020-11-03 10:16:471282 // The main_frame and subframe should each have a live RenderFrame.
1283 EXPECT_TRUE(main_frame()
1284 ->current_frame_host()
1285 ->render_view_host()
1286 ->IsRenderViewLive());
1287 EXPECT_TRUE(main_frame()->current_frame_host()->IsRenderFrameLive());
Steven Bingler184de022019-05-07 14:55:161288 EXPECT_TRUE(
arthursonzognia26fa9a2020-11-03 10:16:471289 main_frame()->child_at(0)->current_frame_host()->IsRenderFrameLive());
Steven Bingler184de022019-05-07 14:55:161290
1291 GURL url(embedded_test_server()->GetURL("c.com", "/title1.html"));
1292
Matt Menked5dc1a22020-03-13 18:46:141293 URLLoaderMonitor monitor;
Steven Bingler184de022019-05-07 14:55:161294
1295 // Perform the actual navigation.
arthursonzognia26fa9a2020-11-03 10:16:471296 NavigateIframeToURL(web_contents(), "child-0", url);
Steven Bingler184de022019-05-07 14:55:161297
arthursonzognia26fa9a2020-11-03 10:16:471298 EXPECT_TRUE(main_frame()
1299 ->current_frame_host()
1300 ->render_view_host()
1301 ->IsRenderViewLive());
1302 EXPECT_TRUE(main_frame()->current_frame_host()->IsRenderFrameLive());
Steven Bingler184de022019-05-07 14:55:161303 EXPECT_TRUE(
arthursonzognia26fa9a2020-11-03 10:16:471304 main_frame()->child_at(0)->current_frame_host()->IsRenderFrameLive());
Steven Bingler184de022019-05-07 14:55:161305
1306 url::Origin starting_page_origin;
1307 starting_page_origin = starting_page_origin.Create(starting_page);
1308
Arthur Sonzognic686e8f2024-01-11 08:36:371309 std::optional<network::ResourceRequest> request = monitor.GetRequestInfo(url);
Matt Menked5dc1a22020-03-13 18:46:141310 ASSERT_TRUE(request.has_value());
1311 EXPECT_EQ(starting_page_origin, request->request_initiator);
Steven Bingler184de022019-05-07 14:55:161312}
1313
John Delaneyf43556d2020-05-04 23:19:061314IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
1315 RendererInitiatedCrossSiteNewWindowInitator) {
1316 GURL url(embedded_test_server()->GetURL("/simple_links.html"));
1317 EXPECT_TRUE(NavigateToURL(shell(), url));
1318
Dave Tapuskae9b7c0f72023-11-06 16:38:011319 auto initiator_global_token = current_frame_host()->GetGlobalFrameToken();
John Delaneyf43556d2020-05-04 23:19:061320
1321 // Simulate clicking on a cross-site link.
1322 {
Avi Drissmanc91bd8e2021-04-19 23:58:441323 const char kReplacePortNumber[] = "setPortNumber(%d);";
John Delaneyf43556d2020-05-04 23:19:061324 uint16_t port_number = embedded_test_server()->port();
Peter Kastingeb8c3ce2021-08-20 04:39:351325 url = embedded_test_server()->GetURL("foo.com", "/title2.html");
Avi Drissmanc91bd8e2021-04-19 23:58:441326 EXPECT_TRUE(
1327 ExecJs(shell(), base::StringPrintf(kReplacePortNumber, port_number)));
John Delaneyf43556d2020-05-04 23:19:061328
John Delaney107a4002021-01-14 01:13:371329 TestNavigationObserver observer(url);
1330 observer.StartWatchingNewWebContents();
Avi Drissmanc91bd8e2021-04-19 23:58:441331 EXPECT_EQ(true, EvalJs(shell(), "clickCrossSiteNewWindowLink();"));
John Delaneyf43556d2020-05-04 23:19:061332
John Delaneyf43556d2020-05-04 23:19:061333 observer.Wait();
1334 EXPECT_EQ(url, observer.last_navigation_url());
1335 EXPECT_TRUE(observer.last_navigation_succeeded());
Antonio Sartori9a82f6f32020-12-14 09:22:451336 EXPECT_TRUE(observer.last_initiator_frame_token().has_value());
Dave Tapuskae9b7c0f72023-11-06 16:38:011337 EXPECT_EQ(initiator_global_token.frame_token,
Antonio Sartori9a82f6f32020-12-14 09:22:451338 observer.last_initiator_frame_token().value());
Dave Tapuskae9b7c0f72023-11-06 16:38:011339 EXPECT_EQ(initiator_global_token.child_id,
1340 observer.last_initiator_process_id());
John Delaneyf43556d2020-05-04 23:19:061341 }
1342}
1343
John Delaney8623c642021-01-06 17:37:071344// Ensure that renderer initiated navigations which have the opener suppressed
1345// work.
1346IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
1347 RendererInitiatedNewWindowNoOpenerNavigation) {
1348 GURL url(embedded_test_server()->GetURL("/simple_links.html"));
1349 EXPECT_TRUE(NavigateToURL(shell(), url));
1350
1351 RenderFrameHost* initial_rfh = current_frame_host();
1352 url::Origin initial_origin = initial_rfh->GetLastCommittedOrigin();
Dave Tapuskae9b7c0f72023-11-06 16:38:011353 auto initiator_global_token = initial_rfh->GetGlobalFrameToken();
John Delaney8623c642021-01-06 17:37:071354
1355 // Simulate clicking on a cross-site link which has rel="noopener".
1356 {
Avi Drissmanc91bd8e2021-04-19 23:58:441357 const char kReplacePortNumber[] = "setPortNumber(%d);";
John Delaney8623c642021-01-06 17:37:071358 uint16_t port_number = embedded_test_server()->port();
Peter Kastingeb8c3ce2021-08-20 04:39:351359 url = embedded_test_server()->GetURL("foo.com", "/title2.html");
Avi Drissmanc91bd8e2021-04-19 23:58:441360 EXPECT_TRUE(
1361 ExecJs(shell(), base::StringPrintf(kReplacePortNumber, port_number)));
John Delaney8623c642021-01-06 17:37:071362
John Delaney107a4002021-01-14 01:13:371363 TestNavigationObserver observer(url);
1364 observer.StartWatchingNewWebContents();
Avi Drissmanc91bd8e2021-04-19 23:58:441365 EXPECT_EQ(true, EvalJs(shell(), "clickCrossSiteNewWindowNoOpenerLink();"));
John Delaney8623c642021-01-06 17:37:071366
John Delaney8623c642021-01-06 17:37:071367 observer.Wait();
1368
1369 EXPECT_EQ(url, observer.last_navigation_url());
1370 EXPECT_TRUE(observer.last_navigation_succeeded());
1371 EXPECT_EQ(initial_origin, observer.last_initiator_origin().value());
1372 EXPECT_TRUE(observer.last_initiator_frame_token().has_value());
Dave Tapuskae9b7c0f72023-11-06 16:38:011373 EXPECT_EQ(initiator_global_token.frame_token,
John Delaney8623c642021-01-06 17:37:071374 observer.last_initiator_frame_token().value());
Dave Tapuskae9b7c0f72023-11-06 16:38:011375 EXPECT_EQ(initiator_global_token.child_id,
1376 observer.last_initiator_process_id());
John Delaney8623c642021-01-06 17:37:071377 }
1378}
1379
John Delaneyf43556d2020-05-04 23:19:061380IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
1381 RendererInitiatedWithSubframeInitator) {
1382 GURL url(embedded_test_server()->GetURL(
1383 "a.com", "/cross_site_iframe_factory.html?a(a())"));
1384 EXPECT_TRUE(NavigateToURL(shell(), url));
1385
1386 GURL subframe_url =
1387 embedded_test_server()->GetURL("a.com", "/simple_links.html");
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201388 EXPECT_TRUE(
1389 NavigateToURLFromRenderer(main_frame()->child_at(0), subframe_url));
John Delaneyf43556d2020-05-04 23:19:061390
1391 RenderFrameHostImpl* subframe_rfh =
arthursonzognia26fa9a2020-11-03 10:16:471392 current_frame_host()->child_at(0)->current_frame_host();
Dave Tapuskae9b7c0f72023-11-06 16:38:011393 auto initiator_global_token = subframe_rfh->GetGlobalFrameToken();
John Delaneyf43556d2020-05-04 23:19:061394
1395 // Simulate clicking on a cross-site link.
1396 {
Avi Drissmanc91bd8e2021-04-19 23:58:441397 const char kReplacePortNumber[] = "setPortNumber(%d);";
John Delaneyf43556d2020-05-04 23:19:061398 uint16_t port_number = embedded_test_server()->port();
Peter Kastingeb8c3ce2021-08-20 04:39:351399 url = embedded_test_server()->GetURL("foo.com", "/title2.html");
Avi Drissmanc91bd8e2021-04-19 23:58:441400 EXPECT_TRUE(ExecJs(subframe_rfh,
1401 base::StringPrintf(kReplacePortNumber, port_number)));
John Delaneyf43556d2020-05-04 23:19:061402
John Delaney107a4002021-01-14 01:13:371403 TestNavigationObserver observer(url);
1404 observer.StartWatchingNewWebContents();
Avi Drissmanc91bd8e2021-04-19 23:58:441405 EXPECT_EQ(true, EvalJs(subframe_rfh, "clickCrossSiteNewWindowLink();"));
John Delaneyf43556d2020-05-04 23:19:061406
John Delaneyf43556d2020-05-04 23:19:061407 observer.Wait();
1408 EXPECT_EQ(url, observer.last_navigation_url());
1409 EXPECT_TRUE(observer.last_navigation_succeeded());
Antonio Sartori9a82f6f32020-12-14 09:22:451410 EXPECT_TRUE(observer.last_initiator_frame_token().has_value());
Dave Tapuskae9b7c0f72023-11-06 16:38:011411 EXPECT_EQ(initiator_global_token.frame_token,
Antonio Sartori9a82f6f32020-12-14 09:22:451412 observer.last_initiator_frame_token().value());
Dave Tapuskae9b7c0f72023-11-06 16:38:011413 EXPECT_EQ(initiator_global_token.child_id,
1414 observer.last_initiator_process_id());
John Delaneyf43556d2020-05-04 23:19:061415 }
1416}
1417
1418IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
1419 InitiatorFrameStateConsistentAtDidStartNavigation) {
1420 GURL form_page_url(embedded_test_server()->GetURL(
1421 "a.com", "/form_that_posts_to_echoall.html"));
1422 EXPECT_TRUE(NavigateToURL(shell(), form_page_url));
1423
1424 // Give the form an action that will navigate to a slow page.
1425 GURL form_action_url(embedded_test_server()->GetURL("b.com", "/slow?100"));
1426 EXPECT_TRUE(
1427 ExecJs(shell(), JsReplace("document.getElementById('form').action = $1",
1428 form_action_url)));
1429
1430 // Open a new window that can be targeted by the form submission.
arthursonzognia26fa9a2020-11-03 10:16:471431 WebContents* form_contents = web_contents();
John Delaneyf43556d2020-05-04 23:19:061432 ShellAddedObserver new_shell_observer;
1433 EXPECT_TRUE(ExecJs(shell(), "window.open('about:blank', 'target_frame');"));
1434 WebContents* popup_contents = new_shell_observer.GetShell()->web_contents();
1435
1436 EXPECT_TRUE(
1437 ExecJs(form_contents,
1438 "document.getElementById('form').target = 'target_frame';"));
1439
1440 TestNavigationManager popup_manager(popup_contents, form_action_url);
1441 TestNavigationManager form_manager(
1442 form_contents, embedded_test_server()->GetURL("a.com", "/title2.html"));
1443
1444 // Submit the form and navigate the form's page.
1445 EXPECT_TRUE(ExecJs(form_contents, "window.location.href = 'title2.html'"));
1446 EXPECT_TRUE(
1447 ExecJs(form_contents, "document.getElementById('form').submit();"));
1448
1449 // The form page's navigation should start prior to the form navigation.
1450 EXPECT_TRUE(form_manager.WaitForRequestStart());
1451 EXPECT_FALSE(popup_manager.GetNavigationHandle());
1452
1453 // When the navigation starts for the popup, ensure that the original page has
1454 // not finished navigating. If this was not the case, we could not make any
1455 // statements on the validity of initiator state during a navigation.
1456 // Navigation handles are only available prior to DidFinishNavigation().
1457 EXPECT_TRUE(popup_manager.WaitForRequestStart());
1458 EXPECT_TRUE(form_manager.GetNavigationHandle());
1459}
1460
1461IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
1462 RendererInitiatedMiddleClickInitator) {
1463 GURL url(embedded_test_server()->GetURL("/simple_links.html"));
1464 EXPECT_TRUE(NavigateToURL(shell(), url));
1465
Dave Tapuskae9b7c0f72023-11-06 16:38:011466 auto initiator_global_token = current_frame_host()->GetGlobalFrameToken();
John Delaneyf43556d2020-05-04 23:19:061467
1468 // Simulate middle-clicking on a cross-site link.
1469 {
Avi Drissmanc91bd8e2021-04-19 23:58:441470 const char kReplacePortNumber[] = "setPortNumber(%d);";
John Delaneyf43556d2020-05-04 23:19:061471 uint16_t port_number = embedded_test_server()->port();
Peter Kastingeb8c3ce2021-08-20 04:39:351472 url = embedded_test_server()->GetURL("foo.com", "/title2.html");
Avi Drissmanc91bd8e2021-04-19 23:58:441473 EXPECT_TRUE(
1474 ExecJs(shell(), base::StringPrintf(kReplacePortNumber, port_number)));
John Delaneyf43556d2020-05-04 23:19:061475
John Delaney107a4002021-01-14 01:13:371476 TestNavigationObserver observer(url);
1477 observer.StartWatchingNewWebContents();
John Delaneyf43556d2020-05-04 23:19:061478 EXPECT_EQ(true, EvalJs(shell(), R"(
1479 target = document.getElementById('cross_site_link');
1480 var evt = new MouseEvent("click", {"button": 1 /* middle_button */});
1481 target.dispatchEvent(evt);)"));
1482
John Delaneyf43556d2020-05-04 23:19:061483 observer.Wait();
1484 EXPECT_EQ(url, observer.last_navigation_url());
1485 EXPECT_TRUE(observer.last_navigation_succeeded());
Antonio Sartori9a82f6f32020-12-14 09:22:451486 EXPECT_TRUE(observer.last_initiator_frame_token().has_value());
Dave Tapuskae9b7c0f72023-11-06 16:38:011487 EXPECT_EQ(initiator_global_token.frame_token,
Antonio Sartori9a82f6f32020-12-14 09:22:451488 observer.last_initiator_frame_token().value());
Dave Tapuskae9b7c0f72023-11-06 16:38:011489 EXPECT_EQ(initiator_global_token.child_id,
1490 observer.last_initiator_process_id());
John Delaneyf43556d2020-05-04 23:19:061491 }
1492}
1493
arthursonzogni313d530f2017-12-13 13:05:291494// Data URLs can have a reference fragment like any other URLs. This test makes
1495// sure it is taken into account.
Minggang Wang83f7f3712019-11-16 11:28:161496IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, DataURLWithReferenceFragment) {
arthursonzogni313d530f2017-12-13 13:05:291497 GURL url("data:text/html,body#foo");
1498 EXPECT_TRUE(NavigateToURL(shell(), url));
1499
Avi Drissmanc91bd8e2021-04-19 23:58:441500 EXPECT_EQ("body", EvalJs(shell(), "document.body.textContent;"));
arthursonzogni313d530f2017-12-13 13:05:291501
Avi Drissmanc91bd8e2021-04-19 23:58:441502 EXPECT_EQ("#foo", EvalJs(shell(), "location.hash;"));
arthursonzogni313d530f2017-12-13 13:05:291503}
1504
arthursonzogni841b1d42018-01-31 08:42:291505// Regression test for https://crbug.com/796561.
1506// 1) Start on a document with history.length == 1.
1507// 2) Create an iframe and call history.pushState at the same time.
1508// 3) history.back() must work.
Minggang Wang83f7f3712019-11-16 11:28:161509IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
arthursonzogni841b1d42018-01-31 08:42:291510 IframeAndPushStateSimultaneously) {
1511 GURL main_url = embedded_test_server()->GetURL("/simple_page.html");
1512 GURL iframe_url = embedded_test_server()->GetURL("/hello.html");
1513
1514 // 1) Start on a new document such that history.length == 1.
1515 {
1516 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1517
Avi Drissmanc91bd8e2021-04-19 23:58:441518 EXPECT_EQ(1, EvalJs(shell(), "history.length"));
arthursonzogni841b1d42018-01-31 08:42:291519 }
1520
1521 // 2) Create an iframe and call history.pushState at the same time.
1522 {
arthursonzognia26fa9a2020-11-03 10:16:471523 TestNavigationManager iframe_navigation(web_contents(), iframe_url);
arthursonzogni841b1d42018-01-31 08:42:291524 ExecuteScriptAsync(shell(),
1525 "let iframe = document.createElement('iframe');"
1526 "iframe.src = '/hello.html';"
1527 "document.body.appendChild(iframe);");
1528 EXPECT_TRUE(iframe_navigation.WaitForRequestStart());
1529
1530 // The iframe navigation is paused. In the meantime, a pushState navigation
1531 // begins and ends.
arthursonzognia26fa9a2020-11-03 10:16:471532 TestNavigationManager push_state_navigation(web_contents(), main_url);
arthursonzogni841b1d42018-01-31 08:42:291533 ExecuteScriptAsync(shell(), "window.history.pushState({}, null);");
Fergal Daly83bc3cd2023-01-18 00:22:541534 ASSERT_TRUE(push_state_navigation.WaitForNavigationFinished());
arthursonzogni841b1d42018-01-31 08:42:291535
1536 // The iframe navigation is resumed.
Fergal Daly83bc3cd2023-01-18 00:22:541537 ASSERT_TRUE(iframe_navigation.WaitForNavigationFinished());
arthursonzogni841b1d42018-01-31 08:42:291538 }
1539
1540 // 3) history.back() must work.
1541 {
arthursonzognia26fa9a2020-11-03 10:16:471542 TestNavigationObserver navigation_observer(web_contents());
Avi Drissmanc91bd8e2021-04-19 23:58:441543 EXPECT_TRUE(ExecJs(web_contents(), "history.back();"));
arthursonzogni841b1d42018-01-31 08:42:291544 navigation_observer.Wait();
1545 }
1546}
1547
Arthur Sonzogni7d1c02f2018-06-14 13:27:001548// Regression test for https://crbug.com/260144
1549// Back/Forward navigation in an iframe must not stop ongoing XHR.
Minggang Wang83f7f3712019-11-16 11:28:161550IN_PROC_BROWSER_TEST_F(NavigationBaseBrowserTest,
Arthur Sonzogni7d1c02f2018-06-14 13:27:001551 IframeNavigationsDoNotStopXHR) {
1552 // A response for the XHR request. It will be delayed until the end of all the
1553 // navigations.
1554 net::test_server::ControllableHttpResponse xhr_response(
1555 embedded_test_server(), "/xhr");
1556 EXPECT_TRUE(embedded_test_server()->Start());
1557
1558 GURL url(embedded_test_server()->GetURL("/title1.html"));
Alex Moshchukaeb20fe32019-09-25 17:40:011559 EXPECT_TRUE(NavigateToURL(shell(), url));
Arthur Sonzogni7d1c02f2018-06-14 13:27:001560
arthursonzognia26fa9a2020-11-03 10:16:471561 DOMMessageQueue dom_message_queue(web_contents());
Arthur Sonzogni7d1c02f2018-06-14 13:27:001562 std::string message;
1563
1564 // 1) Send an XHR.
1565 ExecuteScriptAsync(
1566 shell(),
1567 "let xhr = new XMLHttpRequest();"
1568 "xhr.open('GET', './xhr', true);"
1569 "xhr.onabort = () => window.domAutomationController.send('xhr.onabort');"
1570 "xhr.onerror = () => window.domAutomationController.send('xhr.onerror');"
1571 "xhr.onload = () => window.domAutomationController.send('xhr.onload');"
1572 "xhr.send();");
1573
1574 // 2) Create an iframe and wait for the initial load.
1575 {
1576 ExecuteScriptAsync(
1577 shell(),
1578 "var iframe = document.createElement('iframe');"
1579 "iframe.src = './title1.html';"
1580 "iframe.onload = function() {"
1581 " window.domAutomationController.send('iframe.onload');"
1582 "};"
1583 "document.body.appendChild(iframe);");
1584
1585 EXPECT_TRUE(dom_message_queue.WaitForMessage(&message));
1586 EXPECT_EQ("\"iframe.onload\"", message);
1587 }
1588
1589 // 3) Navigate the iframe elsewhere.
1590 {
1591 ExecuteScriptAsync(shell(),
1592 "var iframe = document.querySelector('iframe');"
1593 "iframe.src = './title2.html';");
1594
1595 EXPECT_TRUE(dom_message_queue.WaitForMessage(&message));
1596 EXPECT_EQ("\"iframe.onload\"", message);
1597 }
1598
1599 // 4) history.back() in the iframe.
1600 {
1601 ExecuteScriptAsync(shell(),
1602 "var iframe = document.querySelector('iframe');"
1603 "iframe.contentWindow.history.back()");
1604
1605 EXPECT_TRUE(dom_message_queue.WaitForMessage(&message));
1606 EXPECT_EQ("\"iframe.onload\"", message);
1607 }
1608
1609 // 5) history.forward() in the iframe.
1610 {
1611 ExecuteScriptAsync(shell(),
1612 "var iframe = document.querySelector('iframe');"
1613 "iframe.contentWindow.history.forward()");
1614
1615 EXPECT_TRUE(dom_message_queue.WaitForMessage(&message));
1616 EXPECT_EQ("\"iframe.onload\"", message);
1617 }
1618
1619 // 6) Wait for the XHR.
1620 {
1621 xhr_response.WaitForRequest();
1622 xhr_response.Send(
1623 "HTTP/1.1 200 OK\r\n"
1624 "Connection: close\r\n"
1625 "Content-Length: 2\r\n"
1626 "Content-Type: text/plain; charset=utf-8\r\n"
1627 "\r\n"
1628 "OK");
1629 xhr_response.Done();
1630 EXPECT_TRUE(dom_message_queue.WaitForMessage(&message));
1631 EXPECT_EQ("\"xhr.onload\"", message);
1632 }
1633
1634 EXPECT_FALSE(dom_message_queue.PopMessage(&message));
1635}
1636
Arthur Sonzogni4c7cbb62018-06-27 14:12:251637// Regression test for https://crbug.com/856396.
Dmitry Gozmand5d45be2019-03-12 17:26:011638// Note that original issue for the bug is not applicable anymore, because there
1639// is no provisional document loader which has not committed yet. We keep the
1640// modified version of this test to check removing iframe from the load event
1641// handler.
Minggang Wang83f7f3712019-11-16 11:28:161642IN_PROC_BROWSER_TEST_F(NavigationBaseBrowserTest,
Arthur Sonzogni4c7cbb62018-06-27 14:12:251643 ReplacingDocumentLoaderFiresLoadEvent) {
1644 net::test_server::ControllableHttpResponse main_document_response(
1645 embedded_test_server(), "/main_document");
1646 net::test_server::ControllableHttpResponse iframe_response(
1647 embedded_test_server(), "/iframe");
1648
1649 ASSERT_TRUE(embedded_test_server()->Start());
1650
1651 // 1) Load the main document.
1652 shell()->LoadURL(embedded_test_server()->GetURL("/main_document"));
1653 main_document_response.WaitForRequest();
1654 main_document_response.Send(
1655 "HTTP/1.1 200 OK\r\n"
1656 "Content-Type: text/html; charset=utf-8\r\n"
1657 "\r\n"
1658 "<script>"
1659 " var detach_iframe = function() {"
1660 " var iframe = document.querySelector('iframe');"
1661 " iframe.parentNode.removeChild(iframe);"
1662 " }"
1663 "</script>"
1664 "<body onload='detach_iframe()'>"
1665 " <iframe src='/iframe'></iframe>"
1666 "</body>");
1667 main_document_response.Done();
1668
1669 // 2) The iframe starts to load, but the server only have time to send the
Dmitry Gozmand5d45be2019-03-12 17:26:011670 // response's headers, not the response's body. This should commit the
1671 // iframe's load.
Arthur Sonzogni4c7cbb62018-06-27 14:12:251672 iframe_response.WaitForRequest();
1673 iframe_response.Send(
1674 "HTTP/1.1 200 OK\r\n"
1675 "Content-Type: text/html; charset=utf-8\r\n"
1676 "\r\n");
1677
1678 // 3) In the meantime the iframe navigates elsewhere. It causes the previous
Dmitry Gozmand5d45be2019-03-12 17:26:011679 // DocumentLoader to be replaced by the new one. Removing it may
Arthur Sonzogni4c7cbb62018-06-27 14:12:251680 // trigger the 'load' event and delete the iframe.
Avi Drissmanc91bd8e2021-04-19 23:58:441681 EXPECT_TRUE(
1682 ExecJs(shell(), "document.querySelector('iframe').src = '/title1.html'"));
Arthur Sonzogni4c7cbb62018-06-27 14:12:251683
Dmitry Gozmand5d45be2019-03-12 17:26:011684 // 4) Finish the original request.
1685 iframe_response.Done();
1686
Arthur Sonzogni4c7cbb62018-06-27 14:12:251687 // Wait for the iframe to be deleted and check the renderer process is still
1688 // alive.
1689 int iframe_count = 1;
1690 while (iframe_count != 0) {
Avi Drissmanc91bd8e2021-04-19 23:58:441691 iframe_count =
1692 EvalJs(
1693 shell(),
1694 "var iframe_count = document.getElementsByTagName('iframe').length;"
1695 "iframe_count;")
1696 .ExtractInt();
Arthur Sonzogni4c7cbb62018-06-27 14:12:251697 }
1698}
1699
Arthur Sonzogni57d111142018-09-19 15:40:131700class NavigationDownloadBrowserTest : public NavigationBaseBrowserTest {
Arthur Sonzogniece30c62018-06-28 09:57:291701 protected:
1702 void SetUpOnMainThread() override {
Arthur Sonzogni57d111142018-09-19 15:40:131703 NavigationBaseBrowserTest::SetUpOnMainThread();
Arthur Sonzogniece30c62018-06-28 09:57:291704
1705 // Set up a test download directory, in order to prevent prompting for
1706 // handling downloads.
1707 ASSERT_TRUE(downloads_directory_.CreateUniqueTempDir());
1708 ShellDownloadManagerDelegate* delegate =
1709 static_cast<ShellDownloadManagerDelegate*>(
arthursonzognia26fa9a2020-11-03 10:16:471710 web_contents()->GetBrowserContext()->GetDownloadManagerDelegate());
Arthur Sonzogniece30c62018-06-28 09:57:291711 delegate->SetDownloadBehaviorForTesting(downloads_directory_.GetPath());
1712 }
1713
1714 private:
1715 base::ScopedTempDir downloads_directory_;
1716};
1717
1718// Regression test for https://crbug.com/855033
1719// 1) A page contains many scripts and DOM elements. It forces the parser to
1720// yield CPU to other tasks. That way the response body's data are not fully
1721// read when URLLoaderClient::OnComplete(..) is received.
1722// 2) A script makes the document navigates elsewhere while it is still loading.
1723// It cancels the parser of the current document. Due to a bug, the document
1724// loader was not marked to be 'loaded' at this step.
1725// 3) The request for the new navigation starts and it turns out it is a
1726// download. The navigation is dropped.
1727// 4) There are no more possibilities for DidStopLoading() to be sent.
Minggang Wang83f7f3712019-11-16 11:28:161728IN_PROC_BROWSER_TEST_F(NavigationDownloadBrowserTest,
Arthur Sonzogniece30c62018-06-28 09:57:291729 StopLoadingAfterDroppedNavigation) {
1730 net::test_server::ControllableHttpResponse main_response(
1731 embedded_test_server(), "/main");
1732 ASSERT_TRUE(embedded_test_server()->Start());
1733
1734 GURL main_url(embedded_test_server()->GetURL("/main"));
1735 GURL download_url(embedded_test_server()->GetURL("/download-test1.lib"));
1736
1737 shell()->LoadURL(main_url);
1738 main_response.WaitForRequest();
1739 std::string headers =
1740 "HTTP/1.1 200 OK\r\n"
1741 "Content-Type: text/html; charset=utf-8\r\n"
1742 "\r\n";
1743
1744 // Craft special HTML to make the blink::DocumentParser yield CPU to other
1745 // tasks. The goal is to ensure the response body datapipe is not fully read
1746 // when URLLoaderClient::OnComplete() is called.
1747 // This relies on the HTMLParserScheduler::ShouldYield() heuristics.
1748 std::string mix_of_script_and_div = "<script></script><div></div>";
1749 for (size_t i = 0; i < 10; ++i) {
1750 mix_of_script_and_div += mix_of_script_and_div; // Exponential growth.
1751 }
1752
1753 std::string navigate_to_download =
1754 "<script>location.href='" + download_url.spec() + "'</script>";
1755
1756 main_response.Send(headers + navigate_to_download + mix_of_script_and_div);
1757 main_response.Done();
1758
arthursonzognia26fa9a2020-11-03 10:16:471759 EXPECT_TRUE(WaitForLoadStop(web_contents()));
Arthur Sonzogniece30c62018-06-28 09:57:291760}
1761
Arthur Sonzognia7d715a2018-09-20 16:11:131762// Renderer initiated back/forward navigation in beforeunload should not prevent
1763// the user to navigate away from a website.
Minggang Wang83f7f3712019-11-16 11:28:161764IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, HistoryBackInBeforeUnload) {
Arthur Sonzognia7d715a2018-09-20 16:11:131765 GURL url_1(embedded_test_server()->GetURL("/title1.html"));
1766 GURL url_2(embedded_test_server()->GetURL("/title2.html"));
1767
1768 EXPECT_TRUE(NavigateToURL(shell(), url_1));
Avi Drissmanc91bd8e2021-04-19 23:58:441769 EXPECT_TRUE(ExecJs(web_contents(),
1770 "onbeforeunload = function() {"
1771 " history.pushState({}, null, '/');"
1772 " history.back();"
1773 "};",
1774 EXECUTE_SCRIPT_NO_USER_GESTURE));
Arthur Sonzognia7d715a2018-09-20 16:11:131775 EXPECT_TRUE(NavigateToURL(shell(), url_2));
1776}
1777
1778// Same as 'HistoryBackInBeforeUnload', but wraps history.back() inside
1779// window.setTimeout(). Thus it is executed "outside" of its beforeunload
1780// handler and thus avoid basic navigation circumventions.
1781// Regression test for: https://crbug.com/879965.
Julie Jeongeun Kim144f8192020-01-18 06:59:031782IN_PROC_BROWSER_TEST_F(NavigationGoToEntryAtOffsetBrowserTest,
Arthur Sonzognia7d715a2018-09-20 16:11:131783 HistoryBackInBeforeUnloadAfterSetTimeout) {
1784 GURL url_1(embedded_test_server()->GetURL("/title1.html"));
1785 GURL url_2(embedded_test_server()->GetURL("/title2.html"));
1786
1787 EXPECT_TRUE(NavigateToURL(shell(), url_1));
Avi Drissmanc91bd8e2021-04-19 23:58:441788 EXPECT_TRUE(ExecJs(web_contents(),
1789 "onbeforeunload = function() {"
1790 " history.pushState({}, null, '/');"
1791 " setTimeout(()=>history.back());"
1792 "};",
1793 EXECUTE_SCRIPT_NO_USER_GESTURE));
arthursonzognia26fa9a2020-11-03 10:16:471794 TestNavigationManager navigation(web_contents(), url_2);
Arthur Sonzognia7d715a2018-09-20 16:11:131795
Julie Jeongeun Kim144f8192020-01-18 06:59:031796 base::RunLoop run_loop;
1797 SetQuitHandlerForGoToEntryAtOffset(run_loop.QuitClosure());
Arthur Sonzognia7d715a2018-09-20 16:11:131798 shell()->LoadURL(url_2);
Julie Jeongeun Kim144f8192020-01-18 06:59:031799 run_loop.Run();
1800
Fergal Daly83bc3cd2023-01-18 00:22:541801 ASSERT_TRUE(navigation.WaitForNavigationFinished());
Arthur Sonzognia7d715a2018-09-20 16:11:131802
1803 EXPECT_TRUE(navigation.was_successful());
1804}
1805
1806// Renderer initiated back/forward navigation can't cancel an ongoing browser
1807// initiated navigation if it is not user initiated.
Minggang Wang83f7f3712019-11-16 11:28:161808IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
Arthur Sonzognia7d715a2018-09-20 16:11:131809 HistoryBackCancelPendingNavigationNoUserGesture) {
1810 GURL url_1(embedded_test_server()->GetURL("/title1.html"));
1811 GURL url_2(embedded_test_server()->GetURL("/title2.html"));
1812 EXPECT_TRUE(NavigateToURL(shell(), url_1));
1813
1814 // 1) A pending browser initiated navigation (omnibox, ...) starts.
arthursonzognia26fa9a2020-11-03 10:16:471815 TestNavigationManager navigation(web_contents(), url_2);
Arthur Sonzognia7d715a2018-09-20 16:11:131816 shell()->LoadURL(url_2);
1817 EXPECT_TRUE(navigation.WaitForRequestStart());
1818
1819 // 2) history.back() is sent but is not user initiated.
Avi Drissmanc91bd8e2021-04-19 23:58:441820 EXPECT_TRUE(ExecJs(web_contents(),
1821 "history.pushState({}, null, '/');"
1822 "history.back();",
1823 EXECUTE_SCRIPT_NO_USER_GESTURE));
Arthur Sonzognia7d715a2018-09-20 16:11:131824
1825 // 3) The first pending navigation is not canceled and can continue.
Fergal Daly83bc3cd2023-01-18 00:22:541826 ASSERT_TRUE(navigation.WaitForNavigationFinished()); // Resume navigation.
Arthur Sonzognia7d715a2018-09-20 16:11:131827 EXPECT_TRUE(navigation.was_successful());
1828}
1829
1830// Renderer initiated back/forward navigation can cancel an ongoing browser
1831// initiated navigation if it is user initiated.
Minggang Wang83f7f3712019-11-16 11:28:161832IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
Arthur Sonzognia7d715a2018-09-20 16:11:131833 HistoryBackCancelPendingNavigationUserGesture) {
1834 GURL url_1(embedded_test_server()->GetURL("/title1.html"));
1835 GURL url_2(embedded_test_server()->GetURL("/title2.html"));
1836 EXPECT_TRUE(NavigateToURL(shell(), url_1));
1837
1838 // 1) A pending browser initiated navigation (omnibox, ...) starts.
arthursonzognia26fa9a2020-11-03 10:16:471839 TestNavigationManager navigation(web_contents(), url_2);
Arthur Sonzognia7d715a2018-09-20 16:11:131840 shell()->LoadURL(url_2);
1841 EXPECT_TRUE(navigation.WaitForRequestStart());
1842
1843 // 2) history.back() is sent and is user initiated.
Avi Drissmanc91bd8e2021-04-19 23:58:441844 EXPECT_TRUE(ExecJs(web_contents(),
1845 "history.pushState({}, null, '/');"
1846 "history.back();"));
Arthur Sonzognia7d715a2018-09-20 16:11:131847
1848 // 3) Check the first pending navigation has been canceled.
Fergal Daly83bc3cd2023-01-18 00:22:541849 ASSERT_TRUE(navigation.WaitForNavigationFinished()); // Resume navigation.
Arthur Sonzognia7d715a2018-09-20 16:11:131850 EXPECT_FALSE(navigation.was_successful());
1851}
1852
Arthur Sonzogni721f48e2018-10-31 17:10:021853// Ensure the renderer process doesn't send too many IPC to the browser process
1854// when history.pushState() and history.back() are called in a loop.
1855// Failing to do so causes the browser to become unresponsive.
1856// See https://crbug.com/882238
Zonghan Xu90de608a2025-06-16 17:04:101857// TODO(crbug.com/379844650): Disabled on Linux sanitizer bots due to flakiness.
Karol Sygietaa3780d2025-07-22 13:11:461858// TODO(crbug.com/346960510): Disabled on ChromeOS sanitizer bots due to
1859// flakiness.
1860#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && \
1861 defined(ADDRESS_SANITIZER)
Zonghan Xu90de608a2025-06-16 17:04:101862#define MAYBE_IPCFlood_GoToEntryAtOffset DISABLED_IPCFlood_GoToEntryAtOffset
1863#else
1864#define MAYBE_IPCFlood_GoToEntryAtOffset IPCFlood_GoToEntryAtOffset
1865#endif
1866IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
1867 MAYBE_IPCFlood_GoToEntryAtOffset) {
Arthur Sonzogni721f48e2018-10-31 17:10:021868 GURL url(embedded_test_server()->GetURL("/title1.html"));
1869 EXPECT_TRUE(NavigateToURL(shell(), url));
1870
arthursonzognia26fa9a2020-11-03 10:16:471871 WebContentsConsoleObserver console_observer(web_contents());
Devlin Cronin90e6b3b2020-01-08 23:43:491872 console_observer.SetPattern(
1873 "Throttling navigation to prevent the browser from hanging. See "
Eric Lawrence [MSFT]4bc72e702021-07-19 17:56:531874 "https://crbug.com/1038223. Command line switch "
Devlin Cronin90e6b3b2020-01-08 23:43:491875 "--disable-ipc-flooding-protection can be used to bypass the "
1876 "protection");
Arthur Sonzogni721f48e2018-10-31 17:10:021877
Avi Drissmanc91bd8e2021-04-19 23:58:441878 EXPECT_TRUE(ExecJs(shell(), R"(
Arthur Sonzogni721f48e2018-10-31 17:10:021879 for(let i = 0; i<1000; ++i) {
1880 history.pushState({},"page 2", "bar.html");
1881 history.back();
1882 }
1883 )"));
1884
Fergal Daly7723f9d2022-10-29 07:03:131885 ASSERT_TRUE(console_observer.Wait());
Arthur Sonzogni721f48e2018-10-31 17:10:021886}
1887
1888// Ensure the renderer process doesn't send too many IPC to the browser process
1889// when doing a same-document navigation is requested in a loop.
1890// Failing to do so causes the browser to become unresponsive.
1891// TODO(arthursonzogni): Make the same test, but when the navigation is
1892// requested from a remote frame.
1893// See https://crbug.com/882238
Minggang Wang83f7f3712019-11-16 11:28:161894IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, IPCFlood_Navigation) {
Arthur Sonzogni721f48e2018-10-31 17:10:021895 GURL url(embedded_test_server()->GetURL("/title1.html"));
1896 EXPECT_TRUE(NavigateToURL(shell(), url));
1897
arthursonzognia26fa9a2020-11-03 10:16:471898 WebContentsConsoleObserver console_observer(web_contents());
Devlin Cronin90e6b3b2020-01-08 23:43:491899 console_observer.SetPattern(
1900 "Throttling navigation to prevent the browser from hanging. See "
Eric Lawrence [MSFT]4bc72e702021-07-19 17:56:531901 "https://crbug.com/1038223. Command line switch "
Devlin Cronin90e6b3b2020-01-08 23:43:491902 "--disable-ipc-flooding-protection can be used to bypass the "
1903 "protection");
Arthur Sonzogni721f48e2018-10-31 17:10:021904
Avi Drissmanc91bd8e2021-04-19 23:58:441905 EXPECT_TRUE(ExecJs(shell(), R"(
Arthur Sonzogni721f48e2018-10-31 17:10:021906 for(let i = 0; i<1000; ++i) {
1907 location.href = "#" + i;
1908 ++i;
1909 }
1910 )"));
1911
Fergal Daly7723f9d2022-10-29 07:03:131912 ASSERT_TRUE(console_observer.Wait());
Arthur Sonzogni721f48e2018-10-31 17:10:021913}
1914
Charlie Harrisone3457f52018-11-07 19:19:331915// TODO(http://crbug.com/632514): This test currently expects opener downloads
Yao Xiao3f1657372023-12-06 21:29:051916// go through, but when the linked bug is resolved the download should be
1917// disallowed.
Minggang Wang83f7f3712019-11-16 11:28:161918IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, OpenerNavigation_DownloadPolicy) {
Charlie Harrisone3457f52018-11-07 19:19:331919 base::ScopedAllowBlockingForTesting allow_blocking;
1920 base::ScopedTempDir download_dir;
1921 ASSERT_TRUE(download_dir.CreateUniqueTempDir());
1922 ShellDownloadManagerDelegate* delegate =
1923 static_cast<ShellDownloadManagerDelegate*>(
arthursonzognia26fa9a2020-11-03 10:16:471924 web_contents()->GetBrowserContext()->GetDownloadManagerDelegate());
Charlie Harrisone3457f52018-11-07 19:19:331925 delegate->SetDownloadBehaviorForTesting(download_dir.GetPath());
Alex Moshchukaeb20fe32019-09-25 17:40:011926 EXPECT_TRUE(
1927 NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
Charlie Harrisone3457f52018-11-07 19:19:331928
1929 // Open a popup.
Avi Drissmanc91bd8e2021-04-19 23:58:441930 EXPECT_EQ(true, EvalJs(web_contents(), "!!window.open();"));
Charlie Harrisone3457f52018-11-07 19:19:331931 EXPECT_EQ(2u, Shell::windows().size());
1932
1933 // Using the popup, navigate its opener to a download.
1934 base::HistogramTester histograms;
1935 WebContents* popup = Shell::windows()[1]->web_contents();
arthursonzognia26fa9a2020-11-03 10:16:471936 EXPECT_NE(popup, web_contents());
Charlie Harrisone3457f52018-11-07 19:19:331937 DownloadTestObserverInProgress observer(
Lukasz Anforowicz48d83452021-05-12 02:58:201938 web_contents()->GetBrowserContext()->GetDownloadManager(),
Charlie Harrisone3457f52018-11-07 19:19:331939 1 /* wait_count */);
Avi Drissmanc91bd8e2021-04-19 23:58:441940 EXPECT_TRUE(ExecJs(
Charlie Harrisone3457f52018-11-07 19:19:331941 popup,
Avi Drissmanc91bd8e2021-04-19 23:58:441942 "window.opener.location ='data:html/text;base64,'+btoa('payload');",
1943 EXECUTE_SCRIPT_NO_USER_GESTURE));
Charlie Harrisone3457f52018-11-07 19:19:331944 observer.WaitForFinished();
Charlie Harrisone3457f52018-11-07 19:19:331945}
1946
Nasko Oskov6e845752018-12-15 00:24:551947// A variation of the OpenerNavigation_DownloadPolicy test above, but uses a
1948// cross-origin URL for the popup window.
Minggang Wang83f7f3712019-11-16 11:28:161949IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
Nasko Oskov6e845752018-12-15 00:24:551950 CrossOriginOpenerNavigation_DownloadPolicy) {
1951 base::ScopedAllowBlockingForTesting allow_blocking;
1952 base::ScopedTempDir download_dir;
1953 ASSERT_TRUE(download_dir.CreateUniqueTempDir());
1954 ShellDownloadManagerDelegate* delegate =
1955 static_cast<ShellDownloadManagerDelegate*>(
arthursonzognia26fa9a2020-11-03 10:16:471956 web_contents()->GetBrowserContext()->GetDownloadManagerDelegate());
Nasko Oskov6e845752018-12-15 00:24:551957 delegate->SetDownloadBehaviorForTesting(download_dir.GetPath());
Alex Moshchukaeb20fe32019-09-25 17:40:011958 EXPECT_TRUE(NavigateToURL(
1959 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
Nasko Oskov6e845752018-12-15 00:24:551960
1961 // Open a popup.
1962 ShellAddedObserver shell_observer;
arthursonzognia26fa9a2020-11-03 10:16:471963 EXPECT_TRUE(EvalJs(web_contents(), JsReplace("!!window.open($1);",
1964 embedded_test_server()->GetURL(
1965 "bar.com", "/title1.html")))
Nasko Oskov6e845752018-12-15 00:24:551966 .ExtractBool());
1967 Shell* new_shell = shell_observer.GetShell();
1968 EXPECT_EQ(2u, Shell::windows().size());
1969
1970 // Wait for the navigation in the popup to complete, so the origin of the
1971 // document will be correct.
1972 WebContents* popup = new_shell->web_contents();
arthursonzognia26fa9a2020-11-03 10:16:471973 EXPECT_NE(popup, web_contents());
Nasko Oskov6e845752018-12-15 00:24:551974 EXPECT_TRUE(WaitForLoadStop(popup));
1975
1976 // Using the popup, navigate its opener to a download.
1977 base::HistogramTester histograms;
Charlie Harrison8065ffe22019-02-21 14:50:001978 const GURL data_url("data:html/text;base64,cGF5bG9hZA==");
arthursonzognia26fa9a2020-11-03 10:16:471979 TestNavigationManager manager(web_contents(), data_url);
Avi Drissmanc91bd8e2021-04-19 23:58:441980 EXPECT_TRUE(ExecJs(popup, base::StringPrintf("window.opener.location ='%s'",
1981 data_url.spec().c_str())));
Fergal Daly83bc3cd2023-01-18 00:22:541982 ASSERT_TRUE(manager.WaitForNavigationFinished());
Charlie Harrison8065ffe22019-02-21 14:50:001983
1984 EXPECT_FALSE(manager.was_successful());
Nasko Oskov6e845752018-12-15 00:24:551985}
1986
Arthur Sonzognid8143332018-11-29 13:38:001987// Regression test for https://crbug.com/872284.
1988// A NavigationThrottle cancels a download in WillProcessResponse.
1989// The navigation request must be canceled and it must also cancel the network
1990// request. Failing to do so resulted in the network socket being leaked.
Minggang Wang83f7f3712019-11-16 11:28:161991IN_PROC_BROWSER_TEST_F(NavigationDownloadBrowserTest,
Arthur Sonzognid8143332018-11-29 13:38:001992 CancelDownloadOnResponseStarted) {
1993 ASSERT_TRUE(embedded_test_server()->Start());
1994
1995 GURL url(embedded_test_server()->GetURL("/title1.html"));
Alex Moshchukaeb20fe32019-09-25 17:40:011996 EXPECT_TRUE(NavigateToURL(shell(), url));
Arthur Sonzognid8143332018-11-29 13:38:001997
1998 // Block every iframe in WillProcessResponse.
1999 content::TestNavigationThrottleInserter throttle_inserter(
arthursonzognia26fa9a2020-11-03 10:16:472000 web_contents(),
Arthur Sonzognid8143332018-11-29 13:38:002001 base::BindLambdaForTesting(
Takashi Toyoshimae9c959ce92025-05-20 19:18:382002 [&](NavigationThrottleRegistry& registry) -> void {
2003 auto throttle = std::make_unique<TestNavigationThrottle>(registry);
Arthur Sonzognid8143332018-11-29 13:38:002004 throttle->SetResponse(TestNavigationThrottle::WILL_PROCESS_RESPONSE,
2005 TestNavigationThrottle::SYNCHRONOUS,
2006 NavigationThrottle::CANCEL_AND_IGNORE);
Takashi Toyoshimae9c959ce92025-05-20 19:18:382007 registry.AddThrottle(std::move(throttle));
Arthur Sonzognid8143332018-11-29 13:38:002008 }));
2009
2010 // Insert enough iframes so that if sockets are not properly released: there
2011 // will not be enough of them to complete all navigations. As of today, only 6
2012 // sockets can be used simultaneously. So using 7 iframes is enough. This test
2013 // uses 33 as a margin.
2014 EXPECT_TRUE(ExecJs(shell(), R"(
2015 for(let i = 0; i<33; ++i) {
2016 let iframe = document.createElement('iframe');
2017 iframe.src = './download-test1.lib'
2018 document.body.appendChild(iframe);
2019 }
2020 )"));
2021
arthursonzognia26fa9a2020-11-03 10:16:472022 EXPECT_TRUE(WaitForLoadStop(web_contents()));
Arthur Sonzognid8143332018-11-29 13:38:002023}
2024
Arthur Sonzognif8a7e522019-01-11 14:30:142025// Add header on redirect.
Minggang Wang83f7f3712019-11-16 11:28:162026IN_PROC_BROWSER_TEST_F(NavigationBaseBrowserTest, AddRequestHeaderOnRedirect) {
Arthur Sonzognif8a7e522019-01-11 14:30:142027 net::test_server::ControllableHttpResponse response_1(embedded_test_server(),
2028 "", true);
2029 net::test_server::ControllableHttpResponse response_2(embedded_test_server(),
2030 "", true);
2031 ASSERT_TRUE(embedded_test_server()->Start());
2032
2033 content::TestNavigationThrottleInserter throttle_inserter(
arthursonzognia26fa9a2020-11-03 10:16:472034 web_contents(),
Arthur Sonzognif8a7e522019-01-11 14:30:142035 base::BindLambdaForTesting(
Takashi Toyoshimae9c959ce92025-05-20 19:18:382036 [](NavigationThrottleRegistry& registry) -> void {
2037 auto throttle = std::make_unique<TestNavigationThrottle>(registry);
2038 NavigationRequest* request =
2039 NavigationRequest::From(&registry.GetNavigationHandle());
Arthur Sonzognif8a7e522019-01-11 14:30:142040 throttle->SetCallback(TestNavigationThrottle::WILL_REDIRECT_REQUEST,
Mohamed Abdelhalimf03d4a22019-10-01 13:34:312041 base::BindLambdaForTesting([request]() {
2042 request->SetRequestHeader("header_name",
2043 "header_value");
Arthur Sonzognif8a7e522019-01-11 14:30:142044 }));
Takashi Toyoshimae9c959ce92025-05-20 19:18:382045 registry.AddThrottle(std::move(throttle));
Arthur Sonzognif8a7e522019-01-11 14:30:142046 }));
2047
2048 // 1) There is no "header_name" header in the initial request.
2049 shell()->LoadURL(embedded_test_server()->GetURL("/doc"));
2050 response_1.WaitForRequest();
2051 EXPECT_FALSE(
Jan Wilken Dörrie531be7ca2019-06-07 10:05:572052 base::Contains(response_1.http_request()->headers, "header_name"));
Arthur Sonzognif8a7e522019-01-11 14:30:142053 response_1.Send(
2054 "HTTP/1.1 302 Moved Temporarily\r\nLocation: /new_doc\r\n\r\n");
2055 response_1.Done();
2056
2057 // 2) The header is added to the second request after the redirect.
2058 response_2.WaitForRequest();
2059 EXPECT_EQ("header_value",
2060 response_2.http_request()->headers.at("header_name"));
Ali Beyadb21baf5f2022-03-31 00:58:062061
2062 // Redirect should not record a ReceivedResponse event.
2063 EXPECT_EQ(0u, test_ukm_recorder()
2064 .GetEntriesByName("Navigation.ReceivedResponse")
2065 .size());
Arthur Sonzognif8a7e522019-01-11 14:30:142066}
2067
2068// Add header on request start, modify it on redirect.
Minggang Wang83f7f3712019-11-16 11:28:162069IN_PROC_BROWSER_TEST_F(NavigationBaseBrowserTest,
Arthur Sonzognif8a7e522019-01-11 14:30:142070 AddRequestHeaderModifyOnRedirect) {
2071 net::test_server::ControllableHttpResponse response_1(embedded_test_server(),
2072 "", true);
2073 net::test_server::ControllableHttpResponse response_2(embedded_test_server(),
2074 "", true);
2075 ASSERT_TRUE(embedded_test_server()->Start());
2076
2077 content::TestNavigationThrottleInserter throttle_inserter(
arthursonzognia26fa9a2020-11-03 10:16:472078 web_contents(),
Arthur Sonzognif8a7e522019-01-11 14:30:142079 base::BindLambdaForTesting(
Takashi Toyoshimae9c959ce92025-05-20 19:18:382080 [](NavigationThrottleRegistry& registry) -> void {
2081 auto throttle = std::make_unique<TestNavigationThrottle>(registry);
2082 NavigationRequest* request =
2083 NavigationRequest::From(&registry.GetNavigationHandle());
Arthur Sonzognif8a7e522019-01-11 14:30:142084 throttle->SetCallback(TestNavigationThrottle::WILL_START_REQUEST,
Mohamed Abdelhalimf03d4a22019-10-01 13:34:312085 base::BindLambdaForTesting([request]() {
2086 request->SetRequestHeader("header_name",
2087 "header_value");
Arthur Sonzognif8a7e522019-01-11 14:30:142088 }));
2089 throttle->SetCallback(TestNavigationThrottle::WILL_REDIRECT_REQUEST,
Mohamed Abdelhalimf03d4a22019-10-01 13:34:312090 base::BindLambdaForTesting([request]() {
2091 request->SetRequestHeader("header_name",
2092 "other_value");
Arthur Sonzognif8a7e522019-01-11 14:30:142093 }));
Takashi Toyoshimae9c959ce92025-05-20 19:18:382094 registry.AddThrottle(std::move(throttle));
Arthur Sonzognif8a7e522019-01-11 14:30:142095 }));
2096
2097 // 1) The header is added to the initial request.
2098 shell()->LoadURL(embedded_test_server()->GetURL("/doc"));
2099 response_1.WaitForRequest();
2100 EXPECT_EQ("header_value",
2101 response_1.http_request()->headers.at("header_name"));
2102 response_1.Send(
2103 "HTTP/1.1 302 Moved Temporarily\r\nLocation: /new_doc\r\n\r\n");
2104 response_1.Done();
2105
2106 // 2) The header is modified in the second request after the redirect.
2107 response_2.WaitForRequest();
2108 EXPECT_EQ("other_value",
2109 response_2.http_request()->headers.at("header_name"));
2110}
2111
2112// Add header on request start, remove it on redirect.
Minggang Wang83f7f3712019-11-16 11:28:162113IN_PROC_BROWSER_TEST_F(NavigationBaseBrowserTest,
Arthur Sonzognif8a7e522019-01-11 14:30:142114 AddRequestHeaderRemoveOnRedirect) {
2115 net::test_server::ControllableHttpResponse response_1(embedded_test_server(),
2116 "", true);
2117 net::test_server::ControllableHttpResponse response_2(embedded_test_server(),
2118 "", true);
2119 ASSERT_TRUE(embedded_test_server()->Start());
2120
2121 content::TestNavigationThrottleInserter throttle_inserter(
arthursonzognia26fa9a2020-11-03 10:16:472122 web_contents(),
Arthur Sonzognif8a7e522019-01-11 14:30:142123 base::BindLambdaForTesting(
Takashi Toyoshimae9c959ce92025-05-20 19:18:382124 [](NavigationThrottleRegistry& registry) -> void {
2125 NavigationRequest* request =
2126 NavigationRequest::From(&registry.GetNavigationHandle());
2127 auto throttle = std::make_unique<TestNavigationThrottle>(registry);
Arthur Sonzognif8a7e522019-01-11 14:30:142128 throttle->SetCallback(TestNavigationThrottle::WILL_START_REQUEST,
Mohamed Abdelhalimf03d4a22019-10-01 13:34:312129 base::BindLambdaForTesting([request]() {
2130 request->SetRequestHeader("header_name",
2131 "header_value");
Arthur Sonzognif8a7e522019-01-11 14:30:142132 }));
Mohamed Abdelhalimf03d4a22019-10-01 13:34:312133 throttle->SetCallback(TestNavigationThrottle::WILL_REDIRECT_REQUEST,
2134 base::BindLambdaForTesting([request]() {
2135 request->RemoveRequestHeader("header_name");
2136 }));
Takashi Toyoshimae9c959ce92025-05-20 19:18:382137 registry.AddThrottle(std::move(throttle));
Arthur Sonzognif8a7e522019-01-11 14:30:142138 }));
2139
2140 // 1) The header is added to the initial request.
2141 shell()->LoadURL(embedded_test_server()->GetURL("/doc"));
2142 response_1.WaitForRequest();
2143 EXPECT_EQ("header_value",
2144 response_1.http_request()->headers.at("header_name"));
2145 response_1.Send(
2146 "HTTP/1.1 302 Moved Temporarily\r\nLocation: /new_doc\r\n\r\n");
2147 response_1.Done();
2148
2149 // 2) The header is removed from the second request after the redirect.
2150 response_2.WaitForRequest();
2151 EXPECT_FALSE(
Jan Wilken Dörrie531be7ca2019-06-07 10:05:572152 base::Contains(response_2.http_request()->headers, "header_name"));
Arthur Sonzognif8a7e522019-01-11 14:30:142153}
2154
Scott Violeta0a72ce2020-04-22 21:46:412155// Name of header used by CorsInjectingUrlLoader.
2156const std::string kCorsHeaderName = "test-header";
2157
2158// URLLoaderThrottle that stores the last value of |kCorsHeaderName|.
2159class CorsInjectingUrlLoader : public blink::URLLoaderThrottle {
2160 public:
2161 explicit CorsInjectingUrlLoader(std::string* last_cors_header_value)
2162 : last_cors_header_value_(last_cors_header_value) {}
2163
2164 // blink::URLLoaderThrottle:
2165 void WillStartRequest(network::ResourceRequest* request,
2166 bool* defer) override {
Chris Fredricksoncf24f1b2024-07-30 17:49:342167 if (std::optional<std::string> header =
2168 request->cors_exempt_headers.GetHeader(kCorsHeaderName);
2169 header) {
2170 last_cors_header_value_->swap(*header);
2171 } else {
Scott Violeta0a72ce2020-04-22 21:46:412172 last_cors_header_value_->clear();
2173 }
2174 }
2175
2176 private:
2177 // See |NavigationCorsExemptBrowserTest::last_cors_header_value_| for details.
Keishi Hattori0e45c022021-11-27 09:25:522178 raw_ptr<std::string> last_cors_header_value_;
Scott Violeta0a72ce2020-04-22 21:46:412179};
2180
2181// ContentBrowserClient responsible for creating CorsInjectingUrlLoader.
Scott Violet99861992023-02-08 01:20:122182class CorsContentBrowserClient : public ContentBrowserTestContentBrowserClient {
Scott Violeta0a72ce2020-04-22 21:46:412183 public:
2184 explicit CorsContentBrowserClient(std::string* last_cors_header_value)
2185 : last_cors_header_value_(last_cors_header_value) {}
2186
2187 // ContentBrowserClient overrides:
2188 std::vector<std::unique_ptr<blink::URLLoaderThrottle>>
2189 CreateURLLoaderThrottles(
2190 const network::ResourceRequest& request,
2191 BrowserContext* browser_context,
2192 const base::RepeatingCallback<WebContents*()>& wc_getter,
2193 NavigationUIData* navigation_ui_data,
Avi Drissman580a3da62024-09-04 16:16:562194 FrameTreeNodeId frame_tree_node_id,
Vikram Pasupathyb53137e52024-02-01 01:46:532195 std::optional<int64_t> navigation_id) override {
Scott Violeta0a72ce2020-04-22 21:46:412196 std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles;
2197 throttles.push_back(
2198 std::make_unique<CorsInjectingUrlLoader>(last_cors_header_value_));
2199 return throttles;
2200 }
2201
2202 private:
2203 // See |NavigationCorsExemptBrowserTest::last_cors_header_value_| for details.
Keishi Hattori0e45c022021-11-27 09:25:522204 raw_ptr<std::string> last_cors_header_value_;
Scott Violeta0a72ce2020-04-22 21:46:412205};
2206
2207class NavigationCorsExemptBrowserTest : public NavigationBaseBrowserTest {
2208 public:
2209 NavigationCorsExemptBrowserTest() = default;
2210
2211 protected:
2212 const std::string& last_cors_header_value() const {
2213 return last_cors_header_value_;
2214 }
2215
2216 void SetUpCommandLine(base::CommandLine* command_line) override {
2217 ShellContentBrowserClient::set_allow_any_cors_exempt_header_for_browser(
2218 true);
2219 NavigationBaseBrowserTest::SetUpCommandLine(command_line);
2220 }
2221 void SetUpOnMainThread() override {
Scott Violet99861992023-02-08 01:20:122222 cors_content_browser_client_ =
2223 std::make_unique<CorsContentBrowserClient>(&last_cors_header_value_);
Scott Violeta0a72ce2020-04-22 21:46:412224 host_resolver()->AddRule("*", "127.0.0.1");
2225 }
2226 void TearDownOnMainThread() override {
Scott Violet99861992023-02-08 01:20:122227 cors_content_browser_client_.reset();
Scott Violeta0a72ce2020-04-22 21:46:412228 ShellContentBrowserClient::set_allow_any_cors_exempt_header_for_browser(
2229 false);
2230 }
2231
2232 private:
2233 // Last value of kCorsHeaderName. Set by CorsInjectingUrlLoader.
2234 std::string last_cors_header_value_;
Scott Violet99861992023-02-08 01:20:122235 std::unique_ptr<CorsContentBrowserClient> cors_content_browser_client_;
Scott Violeta0a72ce2020-04-22 21:46:412236};
2237
2238// Verifies a header added by way of SetRequestHeader() makes it into
2239// |cors_exempt_headers|.
2240IN_PROC_BROWSER_TEST_F(NavigationCorsExemptBrowserTest,
2241 SetCorsExemptRequestHeader) {
2242 net::test_server::ControllableHttpResponse response(embedded_test_server(),
2243 "", true);
2244 ASSERT_TRUE(embedded_test_server()->Start());
2245
2246 const std::string header_value = "value";
2247 content::TestNavigationThrottleInserter throttle_inserter(
arthursonzognia26fa9a2020-11-03 10:16:472248 web_contents(),
Takashi Toyoshimae9c959ce92025-05-20 19:18:382249 base::BindLambdaForTesting(
2250 [header_value](NavigationThrottleRegistry& registry) -> void {
2251 NavigationRequest* request =
2252 NavigationRequest::From(&registry.GetNavigationHandle());
2253 auto throttle = std::make_unique<TestNavigationThrottle>(registry);
2254 throttle->SetCallback(
2255 TestNavigationThrottle::WILL_START_REQUEST,
2256 base::BindLambdaForTesting([request, header_value]() {
2257 request->SetCorsExemptRequestHeader(kCorsHeaderName,
2258 header_value);
2259 }));
2260 registry.AddThrottle(std::move(throttle));
2261 }));
Scott Violeta0a72ce2020-04-22 21:46:412262 shell()->LoadURL(embedded_test_server()->GetURL("/doc"));
2263 response.WaitForRequest();
2264 EXPECT_EQ(header_value, response.http_request()->headers.at(kCorsHeaderName));
2265 EXPECT_EQ(header_value, last_cors_header_value());
2266}
2267
arthursonzognia17619c2019-06-27 17:38:252268// Test NavigationRequest::CheckAboutSrcDoc()
Minggang Wang83f7f3712019-11-16 11:28:162269IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, BlockedSrcDocBrowserInitiated) {
arthursonzognie4b1d2d2019-08-28 19:09:052270 const char* about_srcdoc_urls[] = {"about:srcdoc", "about:srcdoc?foo",
2271 "about:srcdoc#foo"};
arthursonzognia17619c2019-06-27 17:38:252272 // 1. Main frame navigations to about:srcdoc and its variations are blocked.
arthursonzognie4b1d2d2019-08-28 19:09:052273 for (const char* url : about_srcdoc_urls) {
arthursonzognia26fa9a2020-11-03 10:16:472274 NavigationHandleObserver handle_observer(web_contents(), GURL(url));
arthursonzognia17619c2019-06-27 17:38:252275 EXPECT_FALSE(NavigateToURL(shell(), GURL(url)));
2276 EXPECT_TRUE(handle_observer.has_committed());
2277 EXPECT_TRUE(handle_observer.is_error());
2278 EXPECT_EQ(net::ERR_INVALID_URL, handle_observer.net_error_code());
2279 }
2280
arthursonzognie4b1d2d2019-08-28 19:09:052281 // 2. Subframe navigations to variations of about:srcdoc are not blocked.
2282 for (const char* url : about_srcdoc_urls) {
arthursonzognia17619c2019-06-27 17:38:252283 GURL main_url =
2284 embedded_test_server()->GetURL("/frame_tree/page_with_one_frame.html");
2285 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2286
arthursonzognia26fa9a2020-11-03 10:16:472287 NavigationHandleObserver handle_observer(web_contents(), GURL(url));
arthursonzognia17619c2019-06-27 17:38:252288 shell()->LoadURLForFrame(GURL(url), "child-name-0",
2289 ui::PAGE_TRANSITION_FORWARD_BACK);
arthursonzognia26fa9a2020-11-03 10:16:472290 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzognia17619c2019-06-27 17:38:252291 EXPECT_TRUE(handle_observer.has_committed());
arthursonzognia17619c2019-06-27 17:38:252292 EXPECT_FALSE(handle_observer.is_error());
2293 EXPECT_EQ(net::OK, handle_observer.net_error_code());
2294 }
2295}
2296
2297// Test NavigationRequest::CheckAboutSrcDoc().
Minggang Wang83f7f3712019-11-16 11:28:162298IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, BlockedSrcDocRendererInitiated) {
arthursonzognia17619c2019-06-27 17:38:252299 EXPECT_TRUE(
2300 NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
arthursonzognie4b1d2d2019-08-28 19:09:052301 const char* about_srcdoc_urls[] = {"about:srcdoc", "about:srcdoc?foo",
2302 "about:srcdoc#foo"};
arthursonzognia17619c2019-06-27 17:38:252303
2304 // 1. Main frame navigations to about:srcdoc and its variations are blocked.
arthursonzognie4b1d2d2019-08-28 19:09:052305 for (const char* url : about_srcdoc_urls) {
arthursonzognia26fa9a2020-11-03 10:16:472306 DidStartNavigationObserver start_observer(web_contents());
2307 NavigationHandleObserver handle_observer(web_contents(), GURL(url));
arthursonzognia26fa9a2020-11-03 10:16:472308 EXPECT_TRUE(ExecJs(main_frame(), JsReplace("location.href = $1", url)));
arthursonzognia17619c2019-06-27 17:38:252309 start_observer.Wait();
François Dorayc1afdeb2021-04-27 15:56:252310 WaitForLoadStop(web_contents());
arthursonzognia17619c2019-06-27 17:38:252311 EXPECT_TRUE(handle_observer.has_committed());
arthursonzognibe9935c2019-07-10 16:28:532312 EXPECT_TRUE(handle_observer.is_error());
2313 EXPECT_EQ(net::ERR_INVALID_URL, handle_observer.net_error_code());
arthursonzognia17619c2019-06-27 17:38:252314 }
2315
W. James MacLean810d2b2e2024-02-22 16:49:442316 // 2. Subframe navigations to variations of about:srcdoc are blocked unless
2317 // they are same-document or the initiator is same origin to the srcdoc's
2318 // parent. In this test suite, the subframe is self-navigating, but attempting
2319 // a cross-origin navigation from a non-about:srcdoc page to about:srcdoc,
2320 // which isn't allowed.
arthursonzognie4b1d2d2019-08-28 19:09:052321 for (const char* url : about_srcdoc_urls) {
arthursonzognia17619c2019-06-27 17:38:252322 GURL main_url =
2323 embedded_test_server()->GetURL("/frame_tree/page_with_one_frame.html");
2324 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2325
arthursonzognia26fa9a2020-11-03 10:16:472326 DidStartNavigationObserver start_observer(web_contents());
2327 NavigationHandleObserver handle_observer(web_contents(), GURL(url));
2328 FrameTreeNode* subframe = main_frame()->child_at(0);
W. James MacLean810d2b2e2024-02-22 16:49:442329 // Executing location.href = "about:srcdoc" fails.
2330 // Note: if the subframe had already been navigated to about:srcdoc, then
2331 // executing location.href = 'about:srcdoc#foo' would be considered a same-
2332 // document navigation, and would be allowed. This behavior is tested in
2333 // web platform tests, e.g.
2334 // grandparent_location_aboutsrcdoc.sub.window.js, added in the same CL that
2335 // added this comment.
arthursonzognia17619c2019-06-27 17:38:252336 EXPECT_TRUE(ExecJs(subframe, JsReplace("location.href = $1", url)));
arthursonzognia17619c2019-06-27 17:38:252337 start_observer.Wait();
arthursonzognia26fa9a2020-11-03 10:16:472338 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzognia17619c2019-06-27 17:38:252339
2340 EXPECT_TRUE(handle_observer.has_committed());
W. James MacLean810d2b2e2024-02-22 16:49:442341 EXPECT_TRUE(handle_observer.is_error());
2342 EXPECT_EQ(net::ERR_INVALID_URL, handle_observer.net_error_code());
2343 }
2344
2345 // 3. Navigate the sub-frame to be same-origin to the mainframe, then do the
2346 // about:srcdoc navigations with with the main frame as the initiator. The
2347 // test should succeed since the initiator and the parent are the same.
2348 {
2349 DidStartNavigationObserver start_observer(web_contents());
2350 EXPECT_TRUE(ExecJs(
2351 main_frame(), "document.querySelector('iframe').src = 'title1.html';"));
2352 start_observer.Wait();
2353 EXPECT_TRUE(WaitForLoadStop(web_contents()));
2354 }
2355 for (const char* url : about_srcdoc_urls) {
2356 DidStartNavigationObserver start_observer(web_contents());
2357 NavigationHandleObserver handle_observer(web_contents(), GURL(url));
2358 EXPECT_TRUE(
2359 ExecJs(main_frame(), JsReplace("frames[0].location.href = $1", url)));
2360 start_observer.Wait();
2361 EXPECT_TRUE(WaitForLoadStop(web_contents()));
2362
2363 EXPECT_TRUE(handle_observer.has_committed());
2364 EXPECT_FALSE(handle_observer.is_error());
2365 EXPECT_EQ(net::OK, handle_observer.net_error_code());
2366 }
2367
2368 // 4. The subframe is now on about:srcdoc ... verify it can reload itself.
2369 {
2370 FrameTreeNode* subframe = main_frame()->child_at(0);
2371 GURL url("about:srcdoc");
2372 {
2373 // First, navigate the subframe to about:srcdoc without a fragment, so
2374 // that the subsequent reload will have a commit (i.e. it won't be
2375 // reloading a same-document navigation).
2376 DidStartNavigationObserver start_observer(web_contents());
2377 NavigationHandleObserver handle_observer(web_contents(), GURL(url));
2378 EXPECT_TRUE(
2379 ExecJs(main_frame(), JsReplace("frames[0].location.href = $1", url)));
2380 start_observer.Wait();
2381 EXPECT_TRUE(WaitForLoadStop(web_contents()));
2382
2383 EXPECT_TRUE(handle_observer.has_committed());
2384 EXPECT_FALSE(handle_observer.is_error());
2385 EXPECT_EQ(net::OK, handle_observer.net_error_code());
2386 }
2387 DidStartNavigationObserver start_observer(web_contents());
2388 NavigationHandleObserver handle_observer(web_contents(), url);
2389 EXPECT_TRUE(ExecJs(subframe, "location.reload()"));
2390 start_observer.Wait();
2391 EXPECT_TRUE(WaitForLoadStop(web_contents()));
2392
2393 EXPECT_TRUE(handle_observer.has_committed());
arthursonzognia17619c2019-06-27 17:38:252394 EXPECT_FALSE(handle_observer.is_error());
2395 EXPECT_EQ(net::OK, handle_observer.net_error_code());
2396 }
2397}
2398
Charlie Reis56cedcb2024-01-18 20:41:312399// Ensure that about:srcdoc navigations get their origin and base URL from their
2400// parent frame (since that's where the content comes from) and not from the
2401// initiator of the navigation (like about:blank cases). See also the
2402// NavigateGrandchildToAboutBlank test. See https://crbug.com/1515381.
W. James MacLean810d2b2e2024-02-22 16:49:442403IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
2404 GrandchildToAboutSrcdoc_BaseUrl_CrossOrigin) {
2405 GURL parent_url = embedded_test_server()->GetURL(
Charlie Reis56cedcb2024-01-18 20:41:312406 "a.com", "/frame_tree/page_with_one_frame.html");
W. James MacLean810d2b2e2024-02-22 16:49:442407 EXPECT_TRUE(NavigateToURL(shell(), parent_url));
Charlie Reis56cedcb2024-01-18 20:41:312408 FrameTreeNode* subframe = main_frame()->child_at(0);
2409
2410 // Navigate the subframe to a cross-site URL with a srcdoc subframe.
W. James MacLean810d2b2e2024-02-22 16:49:442411 GURL child_url = embedded_test_server()->GetURL(
Charlie Reis56cedcb2024-01-18 20:41:312412 "b.com", "/frame_tree/page_with_srcdoc_frame.html");
2413 TestNavigationObserver observer(web_contents());
W. James MacLean810d2b2e2024-02-22 16:49:442414 EXPECT_TRUE(ExecJs(subframe, JsReplace("location.href = $1", child_url)));
Charlie Reis56cedcb2024-01-18 20:41:312415 observer.Wait();
W. James MacLean810d2b2e2024-02-22 16:49:442416 EXPECT_EQ(child_url, subframe->current_frame_host()->GetLastCommittedURL());
Charlie Reis56cedcb2024-01-18 20:41:312417 FrameTreeNode* grandchild = subframe->child_at(0);
2418 EXPECT_EQ("hello",
2419 EvalJs(grandchild, "document.body.innerHTML").ExtractString());
2420
W. James MacLean810d2b2e2024-02-22 16:49:442421 // From the main frame, attempt to navigate the grandchild frame to
2422 // about:srcdoc. This should fail.
Charlie Reis56cedcb2024-01-18 20:41:312423 TestNavigationObserver srcdoc_observer(web_contents());
Charlie Reis56cedcb2024-01-18 20:41:312424 EXPECT_TRUE(
2425 ExecJs(main_frame(), "frames[0][0].location.href = 'about:srcdoc';"));
2426 srcdoc_observer.Wait();
2427
W. James MacLean810d2b2e2024-02-22 16:49:442428 EXPECT_EQ(
2429 "Could not load the requested resource.<br>Error code: -300 "
2430 "(net::ERR_INVALID_URL)",
2431 EvalJs(grandchild, "document.body.innerHTML").ExtractString());
Charlie Reis56cedcb2024-01-18 20:41:312432
W. James MacLean810d2b2e2024-02-22 16:49:442433 // Since the navigation attempt failed, the origin and base URI are inherited
2434 // from the error page.
Charlie Reis56cedcb2024-01-18 20:41:312435 EXPECT_EQ(GURL(url::kAboutSrcdocURL),
2436 grandchild->current_frame_host()->GetLastCommittedURL());
W. James MacLean810d2b2e2024-02-22 16:49:442437 EXPECT_TRUE(
2438 grandchild->current_frame_host()->GetLastCommittedOrigin().opaque());
2439 EXPECT_EQ("chrome-error://chromewebdata/",
2440 EvalJs(grandchild, "document.baseURI").ExtractString());
2441}
2442
2443IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
2444 GrandchildToAboutSrcdoc_BaseUrl_SameOrigin) {
2445 GURL mainframe_url = embedded_test_server()->GetURL(
2446 "a.com", "/frame_tree/page_with_one_frame.html");
2447 EXPECT_TRUE(NavigateToURL(shell(), mainframe_url));
2448 FrameTreeNode* sub_frame = main_frame()->child_at(0);
2449
2450 // Navigate `sub_frame` to a same-origin URL with a srcdoc subframe.
2451 GURL subframe_url = embedded_test_server()->GetURL(
2452 "a.com", "/frame_tree/page_with_srcdoc_frame.html");
2453 TestNavigationObserver observer(web_contents());
2454 EXPECT_TRUE(ExecJs(sub_frame, JsReplace("location.href = $1", subframe_url)));
2455 observer.Wait();
2456 EXPECT_EQ(subframe_url,
2457 sub_frame->current_frame_host()->GetLastCommittedURL());
2458 FrameTreeNode* srcdoc_frame = sub_frame->child_at(0);
2459 EXPECT_EQ("hello",
2460 EvalJs(srcdoc_frame, "document.body.innerHTML").ExtractString());
2461 EXPECT_EQ(subframe_url,
2462 EvalJs(srcdoc_frame, "document.baseURI").ExtractString());
2463
2464 // From the mainframe, attempt to navigate `srcdoc_frame` to about:srcdoc.
2465 // This should succeed since the mainframe and `sub_frame` are same origin.
2466 TestNavigationObserver srcdoc_observer(web_contents());
2467 EXPECT_TRUE(
2468 ExecJs(main_frame(), "frames[0][0].location.href = 'about:srcdoc';"));
2469 srcdoc_observer.Wait();
2470
2471 EXPECT_EQ("hello",
2472 EvalJs(srcdoc_frame, "document.body.innerHTML").ExtractString());
2473
2474 // The origin and base URI should be inherited from the initiator, since it's
2475 // same-origin to the srcdoc's parent frame `sub_frame`.
2476 EXPECT_EQ(GURL(url::kAboutSrcdocURL),
2477 srcdoc_frame->current_frame_host()->GetLastCommittedURL());
2478 EXPECT_EQ(url::Origin::Create(subframe_url),
2479 srcdoc_frame->current_frame_host()->GetLastCommittedOrigin());
2480 // This picks up the mainframe's url since it's same-origin to `sub_frame`,
2481 // which is `srcdoc_frame`'s parent.
2482 EXPECT_EQ(mainframe_url,
2483 EvalJs(srcdoc_frame, "document.baseURI").ExtractString());
Charlie Reis56cedcb2024-01-18 20:41:312484}
2485
2486// Ensure that about:blank navigations get their origin and base URL from the
2487// initiator of the navigation, and not from their parent frame (like
2488// about:srcdoc cases). See also the NavigateGrandchildToAboutSrcdoc test.
2489IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, GrandchildToAboutBlank_BaseUrl) {
2490 GURL url_a = embedded_test_server()->GetURL(
2491 "a.com", "/frame_tree/page_with_one_frame.html");
2492 EXPECT_TRUE(NavigateToURL(shell(), url_a));
2493 FrameTreeNode* subframe = main_frame()->child_at(0);
2494
2495 // Navigate the subframe to a cross-site URL with a srcdoc subframe.
2496 GURL url_b = embedded_test_server()->GetURL(
2497 "b.com", "/frame_tree/page_with_srcdoc_frame.html");
2498 TestNavigationObserver observer(web_contents());
2499 EXPECT_TRUE(ExecJs(subframe, JsReplace("location.href = $1", url_b)));
2500 observer.Wait();
2501 EXPECT_EQ(url_b, subframe->current_frame_host()->GetLastCommittedURL());
2502 FrameTreeNode* grandchild = subframe->child_at(0);
2503 EXPECT_EQ("hello",
2504 EvalJs(grandchild, "document.body.innerHTML").ExtractString());
2505
2506 // From the main frame, navigate the grandchild frame to about:blank.
2507 TestNavigationObserver srcdoc_observer(web_contents());
2508 EXPECT_TRUE(
2509 ExecJs(main_frame(), "frames[0][0].location.href = 'about:blank';"));
2510 srcdoc_observer.Wait();
2511
2512 // There is no content when navigating to about:blank.
2513 EXPECT_EQ("", EvalJs(grandchild, "document.body.innerHTML").ExtractString());
2514
2515 // The origin and base URI should be inherited from the initiator of the
2516 // navigation and not the parent frame, unlike navigations to about:srcdoc.
2517 EXPECT_EQ(GURL(url::kAboutBlankURL),
2518 grandchild->current_frame_host()->GetLastCommittedURL());
2519 EXPECT_EQ(url::Origin::Create(url_a),
2520 grandchild->current_frame_host()->GetLastCommittedOrigin());
2521 EXPECT_EQ(url_a, EvalJs(grandchild, "document.baseURI").ExtractString());
2522}
2523
arthursonzognibe9935c2019-07-10 16:28:532524// Test renderer initiated navigations to about:srcdoc are routed through the
2525// browser process. It means RenderFrameHostImpl::BeginNavigation() is called.
Minggang Wang83f7f3712019-11-16 11:28:162526IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, AboutSrcDocUsesBeginNavigation) {
arthursonzognibe9935c2019-07-10 16:28:532527 GURL url(embedded_test_server()->GetURL("/title1.html"));
Alex Moshchukaeb20fe32019-09-25 17:40:012528 EXPECT_TRUE(NavigateToURL(shell(), url));
arthursonzognibe9935c2019-07-10 16:28:532529
2530 // If DidStartNavigation is called before DidCommitProvisionalLoad, then it
2531 // means the navigation was driven by the browser process, otherwise by the
2532 // renderer process. This tests it was driven by the browser process:
arthursonzognia26fa9a2020-11-03 10:16:472533 InterceptAndCancelDidCommitProvisionalLoad interceptor(web_contents());
2534 DidStartNavigationObserver observer(web_contents());
arthursonzognibe9935c2019-07-10 16:28:532535
2536 EXPECT_TRUE(ExecJs(shell(), R"(
2537 let iframe = document.createElement("iframe");
2538 iframe.srcdoc = "foo"
2539 document.body.appendChild(iframe);
2540 )"));
2541
2542 observer.Wait(); // BeginNavigation is called.
2543 interceptor.Wait(1); // DidCommitNavigation is called.
2544}
2545
arthursonzognie4b1d2d2019-08-28 19:09:052546// Regression test for https://crbug.com/996044
2547// 1) Navigate an iframe to srcdoc (about:srcdoc);
2548// 2) Same-document navigation to about:srcdoc#1.
2549// 3) Same-document navigation to about:srcdoc#2.
2550// 4) history.back() to about:srcdoc#1.
Minggang Wang83f7f3712019-11-16 11:28:162551IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
arthursonzognie4b1d2d2019-08-28 19:09:052552 SrcDocWithFragmentHistoryNavigation) {
2553 GURL url(embedded_test_server()->GetURL("/title1.html"));
2554 EXPECT_TRUE(NavigateToURL(shell(), url));
2555
2556 // 1) Navigate an iframe to srcdoc (about:srcdoc)
2557 EXPECT_TRUE(ExecJs(shell(), R"(
2558 new Promise(async resolve => {
2559 let iframe = document.createElement('iframe');
2560 iframe.srcdoc = "test";
2561 iframe.onload = resolve;
2562 document.body.appendChild(iframe);
2563 });
2564 )"));
2565
2566 // 2) Same-document navigation to about:srcdoc#1.
2567 // 3) Same-document navigation to about:srcdoc#2.
2568 EXPECT_TRUE(ExecJs(shell(), R"(
2569 let subwindow = document.querySelector('iframe').contentWindow;
2570 subwindow.location.hash = "1";
2571 subwindow.location.hash = "2";
2572 )"));
2573
2574 // Inspect the session history.
arthursonzognia26fa9a2020-11-03 10:16:472575 NavigationControllerImpl& controller = web_contents()->GetController();
arthursonzognie4b1d2d2019-08-28 19:09:052576 ASSERT_EQ(3, controller.GetEntryCount());
2577 ASSERT_EQ(2, controller.GetCurrentEntryIndex());
2578
Arthur Sonzognid5ce01f72024-12-13 13:35:282579 std::array<FrameNavigationEntry*, 3> entry;
arthursonzognie4b1d2d2019-08-28 19:09:052580 for (int i = 0; i < 3; ++i) {
2581 entry[i] = controller.GetEntryAtIndex(i)
2582 ->root_node()
2583 ->children[0]
2584 ->frame_entry.get();
2585 }
2586
2587 EXPECT_EQ(entry[0]->url(), "about:srcdoc");
2588 EXPECT_EQ(entry[1]->url(), "about:srcdoc#1");
2589 EXPECT_EQ(entry[2]->url(), "about:srcdoc#2");
2590
2591 // 4) history.back() to about:srcdoc#1.
2592 EXPECT_TRUE(ExecJs(shell(), "history.back()"));
2593
2594 ASSERT_EQ(3, controller.GetEntryCount());
2595 ASSERT_EQ(1, controller.GetCurrentEntryIndex());
2596}
2597
2598// Regression test for https://crbug.com/996044.
2599// 1) Navigate an iframe to srcdoc (about:srcdoc).
2600// 2) Cross-document navigation to about:srcdoc?1.
2601// 3) Cross-document navigation to about:srcdoc?2.
2602// 4) history.back() to about:srcdoc?1.
Minggang Wang83f7f3712019-11-16 11:28:162603IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
arthursonzognie4b1d2d2019-08-28 19:09:052604 SrcDocWithQueryHistoryNavigation) {
2605 GURL url(embedded_test_server()->GetURL("/title1.html"));
2606 EXPECT_TRUE(NavigateToURL(shell(), url));
2607
2608 // 1) Navigate an iframe to srcdoc (about:srcdoc).
2609 EXPECT_TRUE(ExecJs(shell(), R"(
2610 new Promise(async resolve => {
2611 let iframe = document.createElement('iframe');
2612 iframe.srcdoc = "test";
2613 iframe.onload = resolve;
2614 document.body.appendChild(iframe);
2615 });
2616 )"));
2617
2618 // 2) Cross-document navigation to about:srcdoc?1.
2619 {
arthursonzognia26fa9a2020-11-03 10:16:472620 TestNavigationManager commit_waiter(web_contents(), GURL("about:srcdoc?1"));
arthursonzognie4b1d2d2019-08-28 19:09:052621 EXPECT_TRUE(ExecJs(shell(), R"(
2622 let subwindow = document.querySelector('iframe').contentWindow;
2623 subwindow.location.search = "1";
2624 )"));
Fergal Daly83bc3cd2023-01-18 00:22:542625 ASSERT_TRUE(commit_waiter.WaitForNavigationFinished());
arthursonzognie4b1d2d2019-08-28 19:09:052626 }
2627
2628 // 3) Cross-document navigation to about:srcdoc?2.
2629 {
arthursonzognia26fa9a2020-11-03 10:16:472630 TestNavigationManager commit_waiter(web_contents(), GURL("about:srcdoc?2"));
arthursonzognie4b1d2d2019-08-28 19:09:052631 EXPECT_TRUE(ExecJs(shell(), R"(
2632 let subwindow = document.querySelector('iframe').contentWindow;
2633 subwindow.location.search = "2";
2634 )"));
Fergal Daly83bc3cd2023-01-18 00:22:542635 ASSERT_TRUE(commit_waiter.WaitForNavigationFinished());
arthursonzognie4b1d2d2019-08-28 19:09:052636 }
2637
2638 // Inspect the session history.
arthursonzognia26fa9a2020-11-03 10:16:472639 NavigationControllerImpl& controller = web_contents()->GetController();
arthursonzognie4b1d2d2019-08-28 19:09:052640 ASSERT_EQ(3, controller.GetEntryCount());
2641 ASSERT_EQ(2, controller.GetCurrentEntryIndex());
2642
Arthur Sonzognid5ce01f72024-12-13 13:35:282643 std::array<FrameNavigationEntry*, 3> entry;
arthursonzognie4b1d2d2019-08-28 19:09:052644 for (int i = 0; i < 3; ++i) {
2645 entry[i] = controller.GetEntryAtIndex(i)
2646 ->root_node()
2647 ->children[0]
2648 ->frame_entry.get();
2649 }
2650
2651 EXPECT_EQ(entry[0]->url(), "about:srcdoc");
2652 EXPECT_EQ(entry[1]->url(), "about:srcdoc?1");
2653 EXPECT_EQ(entry[2]->url(), "about:srcdoc?2");
2654
2655 // 4) history.back() to about:srcdoc#1.
2656 EXPECT_TRUE(ExecJs(shell(), "history.back()"));
2657
2658 ASSERT_EQ(3, controller.GetEntryCount());
2659 ASSERT_EQ(1, controller.GetCurrentEntryIndex());
2660}
Nick Burrisc367efe2019-07-24 18:45:432661
arthursonzogni55d9c722019-09-11 09:43:162662// Make sure embedders are notified about visible URL changes in this scenario:
2663// 1. Navigate to A.
2664// 2. Navigate to B.
2665// 3. Add a forward entry in the history for later (same-document).
2666// 4. Start navigation to C.
2667// 5. Start history cross-document navigation, cancelling 4.
2668// 6. Start history same-document navigation, cancelling 5.
2669//
2670// Regression test for https://crbug.com/998284.
Minggang Wang83f7f3712019-11-16 11:28:162671IN_PROC_BROWSER_TEST_F(NavigationBaseBrowserTest,
arthursonzogni55d9c722019-09-11 09:43:162672 BackForwardInOldDocumentCancelPendingNavigation) {
Alexander Timin84fcc8e2019-10-16 17:31:552673 // This test expects a new request to be made when navigating back, which is
2674 // not happening with back-forward cache enabled.
2675 // See BackForwardCacheBrowserTest.RestoreWhilePendingCommit which covers the
2676 // same scenario for back-forward cache.
arthursonzognia26fa9a2020-11-03 10:16:472677 web_contents()->GetController().GetBackForwardCache().DisableForTesting(
Rakina Zata Amni30af7062022-01-19 23:46:362678 BackForwardCacheImpl::TEST_REQUIRES_NO_CACHING);
Alexander Timin84fcc8e2019-10-16 17:31:552679
arthursonzogni55d9c722019-09-11 09:43:162680 using Response = net::test_server::ControllableHttpResponse;
2681 Response response_A1(embedded_test_server(), "/A");
2682 Response response_A2(embedded_test_server(), "/A");
2683 Response response_B1(embedded_test_server(), "/B");
2684 Response response_C1(embedded_test_server(), "/C");
2685
2686 ASSERT_TRUE(embedded_test_server()->Start());
2687
2688 GURL url_a = embedded_test_server()->GetURL("a.com", "/A");
2689 GURL url_b = embedded_test_server()->GetURL("b.com", "/B");
2690 GURL url_c = embedded_test_server()->GetURL("c.com", "/C");
2691
2692 EmbedderVisibleUrlTracker embedder_url_tracker;
arthursonzognia26fa9a2020-11-03 10:16:472693 web_contents()->SetDelegate(&embedder_url_tracker);
arthursonzogni55d9c722019-09-11 09:43:162694
2695 // 1. Navigate to A.
2696 shell()->LoadURL(url_a);
2697 response_A1.WaitForRequest();
2698 response_A1.Send(non_cacheable_html_response);
2699 response_A1.Done();
arthursonzognia26fa9a2020-11-03 10:16:472700 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzogni55d9c722019-09-11 09:43:162701
2702 // 2. Navigate to B.
2703 shell()->LoadURL(url_b);
2704 response_B1.WaitForRequest();
2705 response_B1.Send(non_cacheable_html_response);
2706 response_B1.Done();
arthursonzognia26fa9a2020-11-03 10:16:472707 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzogni55d9c722019-09-11 09:43:162708
2709 // 3. Add a forward entry in the history for later (same-document).
arthursonzognia26fa9a2020-11-03 10:16:472710 EXPECT_TRUE(ExecJs(web_contents(), R"(
arthursonzogni55d9c722019-09-11 09:43:162711 history.pushState({},'');
2712 history.back();
2713 )"));
2714
2715 // 4. Start navigation to C.
2716 {
arthursonzognia26fa9a2020-11-03 10:16:472717 EXPECT_EQ(url_b, web_contents()->GetVisibleURL());
arthursonzogni55d9c722019-09-11 09:43:162718 EXPECT_EQ(url_b, embedder_url_tracker.url());
2719 }
2720 shell()->LoadURL(url_c);
2721 // TODO(arthursonzogni): The embedder_url_tracker should update to url_c at
2722 // this point, but we currently rely on FrameTreeNode::DidStopLoading for
2723 // invalidation and it does not occur when a prior navigation is already in
2724 // progress. The browser is still waiting on the same-document
2725 // "history.back()" to complete.
2726 {
arthursonzognia26fa9a2020-11-03 10:16:472727 EXPECT_EQ(url_c, web_contents()->GetVisibleURL());
arthursonzogni55d9c722019-09-11 09:43:162728 EXPECT_EQ(url_b, embedder_url_tracker.url());
2729 }
2730 embedder_url_tracker.WaitUntilUrlInvalidated();
2731 {
arthursonzognia26fa9a2020-11-03 10:16:472732 EXPECT_EQ(url_c, web_contents()->GetVisibleURL());
arthursonzogni55d9c722019-09-11 09:43:162733 EXPECT_EQ(url_c, embedder_url_tracker.url());
2734 }
2735 response_C1.WaitForRequest();
2736
2737 // 5. Start history cross-document navigation, cancelling 4.
arthursonzognia26fa9a2020-11-03 10:16:472738 EXPECT_TRUE(ExecJs(web_contents(), "history.back()"));
arthursonzogni55d9c722019-09-11 09:43:162739 {
arthursonzognia26fa9a2020-11-03 10:16:472740 EXPECT_EQ(url_b, web_contents()->GetVisibleURL());
arthursonzogni69a6a1b2019-09-17 09:23:002741 EXPECT_EQ(url_b, embedder_url_tracker.url());
arthursonzogni55d9c722019-09-11 09:43:162742 }
2743 response_A2.WaitForRequest();
2744 {
arthursonzognia26fa9a2020-11-03 10:16:472745 EXPECT_EQ(url_b, web_contents()->GetVisibleURL());
arthursonzogni69a6a1b2019-09-17 09:23:002746 EXPECT_EQ(url_b, embedder_url_tracker.url());
arthursonzogni55d9c722019-09-11 09:43:162747 }
2748
2749 // 6. Start history same-document navigation, cancelling 5.
arthursonzognia26fa9a2020-11-03 10:16:472750 EXPECT_TRUE(ExecJs(web_contents(), "history.forward()"));
arthursonzogni55d9c722019-09-11 09:43:162751 {
arthursonzognia26fa9a2020-11-03 10:16:472752 EXPECT_EQ(url_b, web_contents()->GetVisibleURL());
arthursonzogni69a6a1b2019-09-17 09:23:002753 EXPECT_EQ(url_b, embedder_url_tracker.url());
arthursonzogni55d9c722019-09-11 09:43:162754 }
arthursonzognia26fa9a2020-11-03 10:16:472755 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzogni55d9c722019-09-11 09:43:162756 {
arthursonzognia26fa9a2020-11-03 10:16:472757 EXPECT_EQ(url_b, web_contents()->GetVisibleURL());
arthursonzogni69a6a1b2019-09-17 09:23:002758 EXPECT_EQ(url_b, embedder_url_tracker.url());
arthursonzogni55d9c722019-09-11 09:43:162759 }
arthursonzogni55d9c722019-09-11 09:43:162760}
2761
arthursonzogni66f711c2019-10-08 14:40:362762// Regression test for https://crbug.com/999932.
Minggang Wang83f7f3712019-11-16 11:28:162763IN_PROC_BROWSER_TEST_F(NavigationBaseBrowserTest, CanceledNavigationBug999932) {
arthursonzogni66f711c2019-10-08 14:40:362764 using Response = net::test_server::ControllableHttpResponse;
2765 Response response_A1(embedded_test_server(), "/A");
2766 Response response_A2(embedded_test_server(), "/A");
2767 Response response_B1(embedded_test_server(), "/B");
2768
2769 ASSERT_TRUE(embedded_test_server()->Start());
2770
2771 GURL url_a = embedded_test_server()->GetURL("a.com", "/A");
2772 GURL url_b = embedded_test_server()->GetURL("b.com", "/B");
2773
2774 // 1. Navigate to A.
2775 shell()->LoadURL(url_a);
2776 response_A1.WaitForRequest();
2777 response_A1.Send(non_cacheable_html_response);
2778 response_A1.Done();
arthursonzognia26fa9a2020-11-03 10:16:472779 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzogni66f711c2019-10-08 14:40:362780
2781 // 2. Start pending navigation to B.
2782 shell()->LoadURL(url_b);
arthursonzognia26fa9a2020-11-03 10:16:472783 EXPECT_EQ(url_b, web_contents()->GetVisibleURL());
2784 EXPECT_TRUE(web_contents()->GetController().GetPendingEntry());
arthursonzogni66f711c2019-10-08 14:40:362785
2786 // 3. Cancel (2) with renderer-initiated reload with a UserGesture.
arthursonzognia26fa9a2020-11-03 10:16:472787 EXPECT_TRUE(ExecJs(web_contents(), "location.reload()"));
2788 EXPECT_EQ(url_a, web_contents()->GetVisibleURL());
2789 EXPECT_FALSE(web_contents()->GetController().GetPendingEntry());
arthursonzogni66f711c2019-10-08 14:40:362790
2791 // 4. Cancel (3) using document.open();
arthursonzognia26fa9a2020-11-03 10:16:472792 EXPECT_TRUE(ExecJs(web_contents(), "document.open()"));
2793 EXPECT_EQ(url_a, web_contents()->GetVisibleURL());
2794 EXPECT_FALSE(web_contents()->GetController().GetPendingEntry());
arthursonzogni66f711c2019-10-08 14:40:362795}
2796
arthursonzognica6496d2019-09-20 11:20:492797// Regression test for https://crbug.com/1001283
2798// 1) Load main document with CSP: script-src 'none'
2799// 2) Open an about:srcdoc iframe. It inherits the CSP.
2800// 3) The iframe navigates elsewhere.
2801// 4) The iframe navigates back to about:srcdoc.
2802// Check Javascript is never allowed.
Minggang Wang83f7f3712019-11-16 11:28:162803IN_PROC_BROWSER_TEST_F(NavigationBaseBrowserTest,
arthursonzognica6496d2019-09-20 11:20:492804 SrcDocCSPInheritedAfterSameSiteHistoryNavigation) {
2805 using Response = net::test_server::ControllableHttpResponse;
2806 Response main_document_response(embedded_test_server(), "/main_document");
2807
2808 ASSERT_TRUE(embedded_test_server()->Start());
2809
2810 GURL url_a = embedded_test_server()->GetURL("a.com", "/main_document");
2811 GURL url_b = embedded_test_server()->GetURL("a.com", "/title1.html");
2812
Devlin Cronin90e6b3b2020-01-08 23:43:492813 {
arthursonzognia26fa9a2020-11-03 10:16:472814 WebContentsConsoleObserver console_observer(web_contents());
Devlin Cronin90e6b3b2020-01-08 23:43:492815 console_observer.SetPattern("Refused to execute inline script *");
arthursonzognica6496d2019-09-20 11:20:492816
Devlin Cronin90e6b3b2020-01-08 23:43:492817 // 1) Load main document with CSP: script-src 'none'
2818 // 2) Open an about:srcdoc iframe. It inherits the CSP from its parent.
2819 shell()->LoadURL(url_a);
2820 main_document_response.WaitForRequest();
2821 main_document_response.Send(
2822 "HTTP/1.1 200 OK\n"
2823 "content-type: text/html; charset=UTF-8\n"
2824 "Content-Security-Policy: script-src 'none'\n"
2825 "\n"
2826 "<iframe name='theiframe' srcdoc='"
2827 " <script>"
2828 " console.error(\"CSP failure\");"
2829 " </script>"
2830 "'>"
2831 "</iframe>");
2832 main_document_response.Done();
arthursonzognia26fa9a2020-11-03 10:16:472833 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzognica6496d2019-09-20 11:20:492834
Devlin Cronin90e6b3b2020-01-08 23:43:492835 // Check Javascript was blocked the first time.
Fergal Daly7723f9d2022-10-29 07:03:132836 ASSERT_TRUE(console_observer.Wait());
Devlin Cronin90e6b3b2020-01-08 23:43:492837 }
arthursonzognica6496d2019-09-20 11:20:492838
2839 // 3) The iframe navigates elsewhere.
2840 shell()->LoadURLForFrame(url_b, "theiframe",
2841 ui::PAGE_TRANSITION_MANUAL_SUBFRAME);
arthursonzognia26fa9a2020-11-03 10:16:472842 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzognica6496d2019-09-20 11:20:492843
Devlin Cronin90e6b3b2020-01-08 23:43:492844 {
arthursonzognia26fa9a2020-11-03 10:16:472845 WebContentsConsoleObserver console_observer(web_contents());
Devlin Cronin90e6b3b2020-01-08 23:43:492846 console_observer.SetPattern("Refused to execute inline script *");
arthursonzognica6496d2019-09-20 11:20:492847
Devlin Cronin90e6b3b2020-01-08 23:43:492848 // 4) The iframe navigates back to about:srcdoc.
arthursonzognia26fa9a2020-11-03 10:16:472849 web_contents()->GetController().GoBack();
2850 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzognica6496d2019-09-20 11:20:492851
Devlin Cronin90e6b3b2020-01-08 23:43:492852 // Check Javascript was blocked the second time.
Fergal Daly7723f9d2022-10-29 07:03:132853 ASSERT_TRUE(console_observer.Wait());
Devlin Cronin90e6b3b2020-01-08 23:43:492854 }
arthursonzognica6496d2019-09-20 11:20:492855}
2856
Minggang Wang83f7f3712019-11-16 11:28:162857IN_PROC_BROWSER_TEST_F(NavigationBaseBrowserTest,
arthursonzognica6496d2019-09-20 11:20:492858 SrcDocCSPInheritedAfterCrossSiteHistoryNavigation) {
2859 using Response = net::test_server::ControllableHttpResponse;
2860 Response main_document_response(embedded_test_server(), "/main_document");
2861
2862 ASSERT_TRUE(embedded_test_server()->Start());
2863
2864 GURL url_a = embedded_test_server()->GetURL("a.com", "/main_document");
2865 GURL url_b = embedded_test_server()->GetURL("b.com", "/title1.html");
2866
Devlin Cronin90e6b3b2020-01-08 23:43:492867 {
arthursonzognia26fa9a2020-11-03 10:16:472868 WebContentsConsoleObserver console_observer(web_contents());
Devlin Cronin90e6b3b2020-01-08 23:43:492869 console_observer.SetPattern("Refused to execute inline script *");
arthursonzognica6496d2019-09-20 11:20:492870
Devlin Cronin90e6b3b2020-01-08 23:43:492871 // 1) Load main document with CSP: script-src 'none'
2872 // 2) Open an about:srcdoc iframe. It inherits the CSP from its parent.
2873 shell()->LoadURL(url_a);
2874 main_document_response.WaitForRequest();
2875 main_document_response.Send(
2876 "HTTP/1.1 200 OK\n"
2877 "content-type: text/html; charset=UTF-8\n"
2878 "Content-Security-Policy: script-src 'none'\n"
2879 "\n"
2880 "<iframe name='theiframe' srcdoc='"
2881 " <script>"
2882 " console.error(\"CSP failure\");"
2883 " </script>"
2884 "'>"
2885 "</iframe>");
2886 main_document_response.Done();
arthursonzognia26fa9a2020-11-03 10:16:472887 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzognica6496d2019-09-20 11:20:492888
Devlin Cronin90e6b3b2020-01-08 23:43:492889 // Check Javascript was blocked the first time.
Fergal Daly7723f9d2022-10-29 07:03:132890 ASSERT_TRUE(console_observer.Wait());
Devlin Cronin90e6b3b2020-01-08 23:43:492891 }
arthursonzognica6496d2019-09-20 11:20:492892
2893 // 3) The iframe navigates elsewhere.
2894 shell()->LoadURLForFrame(url_b, "theiframe",
2895 ui::PAGE_TRANSITION_MANUAL_SUBFRAME);
arthursonzognia26fa9a2020-11-03 10:16:472896 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzognica6496d2019-09-20 11:20:492897
Devlin Cronin90e6b3b2020-01-08 23:43:492898 {
arthursonzognia26fa9a2020-11-03 10:16:472899 WebContentsConsoleObserver console_observer(web_contents());
Devlin Cronin90e6b3b2020-01-08 23:43:492900 console_observer.SetPattern("Refused to execute inline script *");
arthursonzognica6496d2019-09-20 11:20:492901
Devlin Cronin90e6b3b2020-01-08 23:43:492902 // 4) The iframe navigates back to about:srcdoc.
arthursonzognia26fa9a2020-11-03 10:16:472903 web_contents()->GetController().GoBack();
2904 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzognica6496d2019-09-20 11:20:492905
Devlin Cronin90e6b3b2020-01-08 23:43:492906 // Check Javascript was blocked the second time.
Fergal Daly7723f9d2022-10-29 07:03:132907 ASSERT_TRUE(console_observer.Wait());
Devlin Cronin90e6b3b2020-01-08 23:43:492908 }
arthursonzognica6496d2019-09-20 11:20:492909}
2910
Maks Orlovich48d758b2020-09-01 16:29:042911// Test that NavigationRequest::GetNextPageUkmSourceId returns the eventual
2912// value of RenderFrameHost::GetPageUkmSourceId() --- unremarkable top-level
2913// navigation case.
2914IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
2915 NavigationRequest_GetNextPageUkmSourceId_Basic) {
2916 const GURL kUrl(embedded_test_server()->GetURL("/title1.html"));
arthursonzognia26fa9a2020-11-03 10:16:472917 TestNavigationManager manager(web_contents(), kUrl);
Maks Orlovich48d758b2020-09-01 16:29:042918 shell()->LoadURL(kUrl);
2919
2920 EXPECT_TRUE(manager.WaitForRequestStart());
arthursonzognia26fa9a2020-11-03 10:16:472921 ASSERT_TRUE(main_frame()->navigation_request());
Maks Orlovich48d758b2020-09-01 16:29:042922
2923 ukm::SourceId nav_request_id =
arthursonzognia26fa9a2020-11-03 10:16:472924 main_frame()->navigation_request()->GetNextPageUkmSourceId();
Maks Orlovich48d758b2020-09-01 16:29:042925
2926 EXPECT_TRUE(manager.WaitForResponse());
Fergal Daly83bc3cd2023-01-18 00:22:542927 ASSERT_TRUE(manager.WaitForNavigationFinished());
arthursonzognia26fa9a2020-11-03 10:16:472928 EXPECT_EQ(current_frame_host()->GetPageUkmSourceId(), nav_request_id);
Maks Orlovich48d758b2020-09-01 16:29:042929}
2930
2931// Test that NavigationRequest::GetNextPageUkmSourceId returns the eventual
2932// value of RenderFrameHost::GetPageUkmSourceId() --- child frame case.
2933IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
2934 NavigationRequest_GetNextPageUkmSourceId_ChildFrame) {
2935 const GURL kUrl(
2936 embedded_test_server()->GetURL("/frame_tree/page_with_one_frame.html"));
2937 const GURL kDestUrl(embedded_test_server()->GetURL("/title1.html"));
2938 EXPECT_TRUE(NavigateToURL(shell(), kUrl));
arthursonzognia26fa9a2020-11-03 10:16:472939 FrameTreeNode* subframe = main_frame()->child_at(0);
Maks Orlovich48d758b2020-09-01 16:29:042940 ASSERT_TRUE(subframe);
2941
arthursonzognia26fa9a2020-11-03 10:16:472942 TestNavigationManager manager(web_contents(), kDestUrl);
Maks Orlovich48d758b2020-09-01 16:29:042943 EXPECT_TRUE(
2944 ExecJs(subframe, JsReplace("location.href = $1", kDestUrl.spec())));
2945 EXPECT_TRUE(manager.WaitForRequestStart());
2946 ASSERT_TRUE(subframe->navigation_request());
2947
2948 ukm::SourceId nav_request_id =
2949 subframe->navigation_request()->GetNextPageUkmSourceId();
2950
2951 EXPECT_TRUE(manager.WaitForResponse());
Fergal Daly83bc3cd2023-01-18 00:22:542952 ASSERT_TRUE(manager.WaitForNavigationFinished());
Maks Orlovich48d758b2020-09-01 16:29:042953
2954 // Should have the same page UKM ID in navigation as page post commit, and as
2955 // the top-level frame.
arthursonzognia26fa9a2020-11-03 10:16:472956 EXPECT_EQ(current_frame_host()->GetPageUkmSourceId(), nav_request_id);
Maks Orlovich48d758b2020-09-01 16:29:042957 EXPECT_EQ(subframe->current_frame_host()->GetPageUkmSourceId(),
2958 nav_request_id);
2959}
2960
2961// Test that NavigationRequest::GetNextPageUkmSourceId returns the eventual
2962// value of RenderFrameHost::GetPageUkmSourceId() --- same document navigation.
2963IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
2964 NavigationRequest_GetNextPageUkmSourceId_SameDocument) {
2965 const GURL kUrl(embedded_test_server()->GetURL("/title1.html"));
2966 const GURL kFragment(kUrl.Resolve("#here"));
2967 EXPECT_TRUE(NavigateToURL(shell(), kUrl));
Maks Orlovich48d758b2020-09-01 16:29:042968
arthursonzognia26fa9a2020-11-03 10:16:472969 NavigationHandleObserver handle_observer(web_contents(), kFragment);
2970 EXPECT_TRUE(
2971 ExecJs(main_frame(), JsReplace("location.href = $1", kFragment.spec())));
2972 EXPECT_TRUE(WaitForLoadStop(web_contents()));
Maks Orlovich48d758b2020-09-01 16:29:042973
2974 EXPECT_TRUE(handle_observer.is_same_document());
arthursonzognia26fa9a2020-11-03 10:16:472975 EXPECT_EQ(current_frame_host()->GetPageUkmSourceId(),
Maks Orlovich48d758b2020-09-01 16:29:042976 handle_observer.next_page_ukm_source_id());
2977}
2978
2979// Test that NavigationRequest::GetNextPageUkmSourceId returns the eventual
2980// value of RenderFrameHost::GetPageUkmSourceId() --- back navigation;
2981// this case matters because of back-forward cache.
2982IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
2983 NavigationRequest_GetNextPageUkmSourceId_Back) {
2984 const GURL kUrl1(embedded_test_server()->GetURL("a.com", "/title1.html"));
2985 const GURL kUrl2(embedded_test_server()->GetURL("b.com", "/title2.html"));
2986 EXPECT_TRUE(NavigateToURL(shell(), kUrl1));
2987 EXPECT_TRUE(NavigateToURL(shell(), kUrl2));
2988
arthursonzognia26fa9a2020-11-03 10:16:472989 NavigationHandleObserver handle_observer(web_contents(), kUrl1);
2990 web_contents()->GetController().GoBack();
2991 EXPECT_TRUE(WaitForLoadStop(web_contents()));
Maks Orlovich48d758b2020-09-01 16:29:042992
arthursonzognia26fa9a2020-11-03 10:16:472993 EXPECT_EQ(current_frame_host()->GetPageUkmSourceId(),
Maks Orlovich48d758b2020-09-01 16:29:042994 handle_observer.next_page_ukm_source_id());
2995}
2996
Maksim Orlovich09c4f972019-10-21 19:51:412997// Tests for cookies. Provides an HTTPS server.
2998class NavigationCookiesBrowserTest : public NavigationBaseBrowserTest {
2999 protected:
Arthur Sonzognib9ab5102021-08-09 17:52:413000 NavigationCookiesBrowserTest() = default;
Maksim Orlovich09c4f972019-10-21 19:51:413001 net::EmbeddedTestServer* https_server() { return &https_server_; }
3002
3003 private:
Arthur Sonzognib9ab5102021-08-09 17:52:413004 void SetUpOnMainThread() override {
3005 NavigationBaseBrowserTest::SetUpOnMainThread();
3006 mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK);
3007 https_server()->AddDefaultHandlers(GetTestDataFilePath());
3008 }
3009
3010 void SetUpCommandLine(base::CommandLine* command_line) override {
3011 NavigationBaseBrowserTest::SetUpCommandLine(command_line);
3012 mock_cert_verifier_.SetUpCommandLine(command_line);
3013 }
3014
3015 void SetUpInProcessBrowserTestFixture() override {
3016 NavigationBaseBrowserTest::SetUpInProcessBrowserTestFixture();
3017 mock_cert_verifier_.SetUpInProcessBrowserTestFixture();
3018 }
3019
3020 void TearDownInProcessBrowserTestFixture() override {
3021 NavigationBaseBrowserTest::TearDownInProcessBrowserTestFixture();
3022 mock_cert_verifier_.TearDownInProcessBrowserTestFixture();
3023 }
3024
3025 content::ContentMockCertVerifier mock_cert_verifier_;
3026 net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS};
Maksim Orlovich09c4f972019-10-21 19:51:413027};
3028
arthursonzognicd49c292019-10-01 17:06:573029// Test how cookies are inherited in about:srcdoc iframes.
3030//
3031// Regression test: https://crbug.com/1003167.
Michael Thiessen7f85eed52024-06-28 17:38:253032// Test is flaky on all platforms: https://crbug.com/339033006
3033IN_PROC_BROWSER_TEST_F(NavigationCookiesBrowserTest,
3034 DISABLED_CookiesInheritedSrcDoc) {
arthursonzognicd49c292019-10-01 17:06:573035 using Response = net::test_server::ControllableHttpResponse;
Maksim Orlovich09c4f972019-10-21 19:51:413036 Response response_1(https_server(), "/response_1");
3037 Response response_2(https_server(), "/response_2");
3038 Response response_3(https_server(), "/response_3");
arthursonzognicd49c292019-10-01 17:06:573039
Maksim Orlovich09c4f972019-10-21 19:51:413040 ASSERT_TRUE(https_server()->Start());
arthursonzognicd49c292019-10-01 17:06:573041
Maksim Orlovich09c4f972019-10-21 19:51:413042 GURL url_a(https_server()->GetURL("a.com", "/title1.html"));
3043 GURL url_b(https_server()->GetURL("b.com", "/title1.html"));
arthursonzognicd49c292019-10-01 17:06:573044 EXPECT_TRUE(NavigateToURL(shell(), url_a));
3045
3046 EXPECT_TRUE(ExecJs(shell(), R"(
3047 let iframe = document.createElement("iframe");
3048 iframe.srcdoc = "foo";
3049 document.body.appendChild(iframe);
3050 )"));
arthursonzognia26fa9a2020-11-03 10:16:473051 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzognicd49c292019-10-01 17:06:573052
arthursonzognia26fa9a2020-11-03 10:16:473053 RenderFrameHostImpl* main_document = current_frame_host();
arthursonzognicd49c292019-10-01 17:06:573054 RenderFrameHostImpl* sub_document_1 =
3055 main_document->child_at(0)->current_frame_host();
3056 EXPECT_EQ(url::kAboutSrcdocURL, sub_document_1->GetLastCommittedURL());
3057 EXPECT_EQ(url::Origin::Create(url_a),
3058 sub_document_1->GetLastCommittedOrigin());
3059 EXPECT_EQ(main_document->GetSiteInstance(),
3060 sub_document_1->GetSiteInstance());
3061
3062 // 0. The default state doesn't contain any cookies.
3063 EXPECT_EQ("", EvalJs(main_document, "document.cookie"));
3064 EXPECT_EQ("", EvalJs(sub_document_1, "document.cookie"));
3065
3066 // 1. Set a cookie in the main document, it affects its child too.
3067 EXPECT_TRUE(ExecJs(main_document, "document.cookie = 'a=0';"));
3068
3069 EXPECT_EQ("a=0", EvalJs(main_document, "document.cookie"));
3070 EXPECT_EQ("a=0", EvalJs(sub_document_1, "document.cookie"));
3071
3072 // 2. Set a cookie in the child, it affects its parent too.
3073 EXPECT_TRUE(ExecJs(sub_document_1, "document.cookie = 'b=0';"));
3074
3075 EXPECT_EQ("a=0; b=0", EvalJs(main_document, "document.cookie"));
3076 EXPECT_EQ("a=0; b=0", EvalJs(sub_document_1, "document.cookie"));
3077
3078 // 3. Checks cookies are sent while requesting resources.
arthursonzogni3d3ec9e2021-03-04 11:00:503079 ExecuteScriptAsync(sub_document_1, "fetch('/response_1');");
arthursonzognicd49c292019-10-01 17:06:573080 response_1.WaitForRequest();
3081 EXPECT_EQ("a=0; b=0", response_1.http_request()->headers.at("Cookie"));
3082
3083 // 4. Navigate the iframe elsewhere.
3084 EXPECT_TRUE(ExecJs(sub_document_1, JsReplace("location.href = $1", url_b)));
arthursonzognia26fa9a2020-11-03 10:16:473085 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzognicd49c292019-10-01 17:06:573086 RenderFrameHostImpl* sub_document_2 =
3087 main_document->child_at(0)->current_frame_host();
3088
3089 EXPECT_EQ("a=0; b=0", EvalJs(main_document, "document.cookie"));
3090 EXPECT_EQ("", EvalJs(sub_document_2, "document.cookie"));
3091
3092 // 5. Set a cookie in the main document. It doesn't affect its child.
3093 EXPECT_TRUE(ExecJs(main_document, "document.cookie = 'c=0';"));
3094
3095 EXPECT_EQ("a=0; b=0; c=0", EvalJs(main_document, "document.cookie"));
3096 EXPECT_EQ("", EvalJs(sub_document_2, "document.cookie"));
3097
3098 // 6. Set a cookie in the child. It doesn't affect its parent.
Maksim Orlovich09c4f972019-10-21 19:51:413099 EXPECT_TRUE(ExecJs(sub_document_2,
3100 "document.cookie = 'd=0; SameSite=none; Secure';"));
arthursonzognicd49c292019-10-01 17:06:573101
3102 EXPECT_EQ("a=0; b=0; c=0", EvalJs(main_document, "document.cookie"));
3103 EXPECT_EQ("d=0", EvalJs(sub_document_2, "document.cookie"));
3104
3105 // 7. Checks cookies are sent while requesting resources.
arthursonzogni3d3ec9e2021-03-04 11:00:503106 ExecuteScriptAsync(sub_document_2, "fetch('/response_2');");
arthursonzognicd49c292019-10-01 17:06:573107 response_2.WaitForRequest();
3108 EXPECT_EQ("d=0", response_2.http_request()->headers.at("Cookie"));
3109
3110 // 8. Navigate the iframe back to about:srcdoc.
arthursonzognia26fa9a2020-11-03 10:16:473111 web_contents()->GetController().GoBack();
3112 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzognicd49c292019-10-01 17:06:573113 RenderFrameHostImpl* sub_document_3 =
3114 main_document->child_at(0)->current_frame_host();
3115 EXPECT_EQ(url_a, main_document->GetLastCommittedURL());
3116 EXPECT_EQ(url::kAboutSrcdocURL, sub_document_3->GetLastCommittedURL());
3117 EXPECT_EQ(url::Origin::Create(url_a),
3118 sub_document_3->GetLastCommittedOrigin());
3119 EXPECT_EQ(main_document->GetSiteInstance(),
3120 sub_document_3->GetSiteInstance());
3121
arthursonzognicd49c292019-10-01 17:06:573122 EXPECT_EQ("a=0; b=0; c=0", EvalJs(main_document, "document.cookie"));
arthursonzogni4f83af072019-10-04 10:19:243123 EXPECT_EQ("a=0; b=0; c=0", EvalJs(sub_document_3, "document.cookie"));
arthursonzognicd49c292019-10-01 17:06:573124
3125 // 9. Set cookie in the main document. It should be inherited by the child.
3126 EXPECT_TRUE(ExecJs(main_document, "document.cookie = 'e=0';"));
3127
arthursonzognicd49c292019-10-01 17:06:573128 EXPECT_EQ("a=0; b=0; c=0; e=0", EvalJs(main_document, "document.cookie"));
arthursonzogni4f83af072019-10-04 10:19:243129 EXPECT_EQ("a=0; b=0; c=0; e=0", EvalJs(sub_document_3, "document.cookie"));
arthursonzognicd49c292019-10-01 17:06:573130
3131 // 11. Set cookie in the child document. It should be reflected on its parent.
3132 EXPECT_TRUE(ExecJs(sub_document_3, "document.cookie = 'f=0';"));
3133
arthursonzogni4f83af072019-10-04 10:19:243134 EXPECT_EQ("a=0; b=0; c=0; e=0; f=0",
3135 EvalJs(main_document, "document.cookie"));
3136 EXPECT_EQ("a=0; b=0; c=0; e=0; f=0",
3137 EvalJs(sub_document_3, "document.cookie"));
arthursonzognicd49c292019-10-01 17:06:573138
3139 // 12. Checks cookies are sent while requesting resources.
arthursonzogni3d3ec9e2021-03-04 11:00:503140 ExecuteScriptAsync(sub_document_3, "fetch('/response_3');");
arthursonzognicd49c292019-10-01 17:06:573141 response_3.WaitForRequest();
arthursonzogni4f83af072019-10-04 10:19:243142 EXPECT_EQ("a=0; b=0; c=0; e=0; f=0",
arthursonzognicd49c292019-10-01 17:06:573143 response_3.http_request()->headers.at("Cookie"));
3144}
3145
3146// Test how cookies are inherited in about:blank iframes.
Minggang Wang83f7f3712019-11-16 11:28:163147IN_PROC_BROWSER_TEST_F(NavigationCookiesBrowserTest,
Maksim Orlovich09c4f972019-10-21 19:51:413148 CookiesInheritedAboutBlank) {
arthursonzognicd49c292019-10-01 17:06:573149 // This test expects several cross-site navigation to happen.
Takashi Toyoshimae9c959ce92025-05-20 19:18:383150 if (!AreAllSitesIsolatedForTesting()) {
arthursonzognicd49c292019-10-01 17:06:573151 return;
Takashi Toyoshimae9c959ce92025-05-20 19:18:383152 }
arthursonzognicd49c292019-10-01 17:06:573153
3154 using Response = net::test_server::ControllableHttpResponse;
Maksim Orlovich09c4f972019-10-21 19:51:413155 Response response_1(https_server(), "/response_1");
3156 Response response_2(https_server(), "/response_2");
3157 Response response_3(https_server(), "/response_3");
arthursonzognicd49c292019-10-01 17:06:573158
Maksim Orlovich09c4f972019-10-21 19:51:413159 ASSERT_TRUE(https_server()->Start());
arthursonzognicd49c292019-10-01 17:06:573160
Maksim Orlovich09c4f972019-10-21 19:51:413161 GURL url_a(https_server()->GetURL("a.com", "/title1.html"));
3162 GURL url_b(https_server()->GetURL("b.com", "/title1.html"));
arthursonzognicd49c292019-10-01 17:06:573163 EXPECT_TRUE(NavigateToURL(shell(), url_a));
3164
3165 EXPECT_TRUE(
3166 ExecJs(shell(), JsReplace("let iframe = document.createElement('iframe');"
3167 "iframe.src = $1;"
3168 "document.body.appendChild(iframe);",
3169 url_b)));
arthursonzognia26fa9a2020-11-03 10:16:473170 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzognicd49c292019-10-01 17:06:573171 EXPECT_TRUE(ExecJs(shell(), R"(
3172 document.querySelector('iframe').src = "about:blank"
3173 )"));
arthursonzognia26fa9a2020-11-03 10:16:473174 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzognicd49c292019-10-01 17:06:573175
arthursonzognia26fa9a2020-11-03 10:16:473176 RenderFrameHostImpl* main_document = current_frame_host();
arthursonzognicd49c292019-10-01 17:06:573177 RenderFrameHostImpl* sub_document_1 =
3178 main_document->child_at(0)->current_frame_host();
3179
3180 EXPECT_EQ(url::kAboutBlankURL, sub_document_1->GetLastCommittedURL());
3181 EXPECT_EQ(url::Origin::Create(url_a),
3182 sub_document_1->GetLastCommittedOrigin());
3183 EXPECT_EQ(main_document->GetSiteInstance(),
3184 sub_document_1->GetSiteInstance());
3185
3186 // 0. The default state doesn't contain any cookies.
3187 EXPECT_EQ("", EvalJs(main_document, "document.cookie"));
3188 EXPECT_EQ("", EvalJs(sub_document_1, "document.cookie"));
3189
3190 // 1. Set a cookie in the main document, it affects its child too.
3191 EXPECT_TRUE(ExecJs(main_document, "document.cookie = 'a=0';"));
3192
3193 EXPECT_EQ("a=0", EvalJs(main_document, "document.cookie"));
3194 EXPECT_EQ("a=0", EvalJs(sub_document_1, "document.cookie"));
3195
3196 // 2. Set a cookie in the child, it affects its parent too.
3197 EXPECT_TRUE(ExecJs(sub_document_1, "document.cookie = 'b=0';"));
3198
3199 EXPECT_EQ("a=0; b=0", EvalJs(main_document, "document.cookie"));
3200 EXPECT_EQ("a=0; b=0", EvalJs(sub_document_1, "document.cookie"));
3201
3202 // 3. Checks cookies are sent while requesting resources.
Maksim Orlovich09c4f972019-10-21 19:51:413203 GURL url_response_1 = https_server()->GetURL("a.com", "/response_1");
arthursonzogni3d3ec9e2021-03-04 11:00:503204 ExecuteScriptAsync(sub_document_1, JsReplace("fetch($1)", url_response_1));
arthursonzognicd49c292019-10-01 17:06:573205 response_1.WaitForRequest();
3206 EXPECT_EQ("a=0; b=0", response_1.http_request()->headers.at("Cookie"));
3207
3208 // 4. Navigate the iframe elsewhere.
3209 EXPECT_TRUE(ExecJs(sub_document_1, JsReplace("location.href = $1", url_b)));
arthursonzognia26fa9a2020-11-03 10:16:473210 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzognicd49c292019-10-01 17:06:573211 RenderFrameHostImpl* sub_document_2 =
3212 main_document->child_at(0)->current_frame_host();
3213
3214 EXPECT_EQ("a=0; b=0", EvalJs(main_document, "document.cookie"));
3215 EXPECT_EQ("", EvalJs(sub_document_2, "document.cookie"));
3216
3217 // 5. Set a cookie in the main document. It doesn't affect its child.
3218 EXPECT_TRUE(ExecJs(main_document, "document.cookie = 'c=0';"));
3219
3220 EXPECT_EQ("a=0; b=0; c=0", EvalJs(main_document, "document.cookie"));
3221 EXPECT_EQ("", EvalJs(sub_document_2, "document.cookie"));
3222
3223 // 6. Set a cookie in the child. It doesn't affect its parent.
Maksim Orlovich09c4f972019-10-21 19:51:413224 EXPECT_TRUE(ExecJs(sub_document_2,
3225 "document.cookie = 'd=0; SameSite=none; Secure';"));
arthursonzognicd49c292019-10-01 17:06:573226
3227 EXPECT_EQ("a=0; b=0; c=0", EvalJs(main_document, "document.cookie"));
3228 EXPECT_EQ("d=0", EvalJs(sub_document_2, "document.cookie"));
3229
3230 // 7. Checks cookies are sent while requesting resources.
arthursonzogni3d3ec9e2021-03-04 11:00:503231 ExecuteScriptAsync(sub_document_2, "fetch('/response_2');");
arthursonzognicd49c292019-10-01 17:06:573232 response_2.WaitForRequest();
3233 EXPECT_EQ("d=0", response_2.http_request()->headers.at("Cookie"));
3234
3235 // 8. Navigate the iframe back to about:blank.
arthursonzognia26fa9a2020-11-03 10:16:473236 web_contents()->GetController().GoBack();
3237 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzognicd49c292019-10-01 17:06:573238 RenderFrameHostImpl* sub_document_3 =
3239 main_document->child_at(0)->current_frame_host();
3240 EXPECT_EQ(url_a, main_document->GetLastCommittedURL());
3241 EXPECT_EQ(url::kAboutBlankURL, sub_document_3->GetLastCommittedURL());
3242 EXPECT_EQ(url::Origin::Create(url_a),
3243 sub_document_3->GetLastCommittedOrigin());
3244 EXPECT_EQ(main_document->GetSiteInstance(),
3245 sub_document_3->GetSiteInstance());
3246
3247 EXPECT_EQ("a=0; b=0; c=0", EvalJs(main_document, "document.cookie"));
arthursonzogni4f83af072019-10-04 10:19:243248 EXPECT_EQ("a=0; b=0; c=0", EvalJs(sub_document_3, "document.cookie"));
arthursonzognicd49c292019-10-01 17:06:573249
arthursonzogni4f83af072019-10-04 10:19:243250 // 9. Set cookie in the main document. It affects the iframe.
arthursonzognicd49c292019-10-01 17:06:573251 EXPECT_TRUE(ExecJs(main_document, "document.cookie = 'e=0';"));
3252
3253 EXPECT_EQ("a=0; b=0; c=0; e=0", EvalJs(main_document, "document.cookie"));
arthursonzogni4f83af072019-10-04 10:19:243254 EXPECT_EQ("a=0; b=0; c=0; e=0", EvalJs(sub_document_3, "document.cookie"));
arthursonzognicd49c292019-10-01 17:06:573255
arthursonzogni4f83af072019-10-04 10:19:243256 // 10. Set cookie in the iframe. It affects the main frame.
arthursonzognicd49c292019-10-01 17:06:573257 EXPECT_TRUE(ExecJs(sub_document_3, "document.cookie = 'f=0';"));
arthursonzogni4f83af072019-10-04 10:19:243258 EXPECT_EQ("a=0; b=0; c=0; e=0; f=0",
3259 EvalJs(main_document, "document.cookie"));
3260 EXPECT_EQ("a=0; b=0; c=0; e=0; f=0",
3261 EvalJs(sub_document_3, "document.cookie"));
arthursonzognicd49c292019-10-01 17:06:573262
3263 // 11. Even if document.cookie is empty, cookies are sent.
arthursonzogni3d3ec9e2021-03-04 11:00:503264 ExecuteScriptAsync(sub_document_3, "fetch('/response_3');");
arthursonzognicd49c292019-10-01 17:06:573265 response_3.WaitForRequest();
arthursonzogni4f83af072019-10-04 10:19:243266 EXPECT_EQ("a=0; b=0; c=0; e=0; f=0",
arthursonzognicd49c292019-10-01 17:06:573267 response_3.http_request()->headers.at("Cookie"));
3268}
3269
3270// Test how cookies are inherited in about:blank iframes.
3271//
Maksim Orlovich09c4f972019-10-21 19:51:413272// This is a variation of
3273// NavigationCookiesBrowserTest.CookiesInheritedAboutBlank. Instead of
3274// requesting an history navigation, a new navigation is requested from the main
3275// frame. The navigation is cross-site instead of being same-site.
Minggang Wang83f7f3712019-11-16 11:28:163276IN_PROC_BROWSER_TEST_F(NavigationCookiesBrowserTest,
Maksim Orlovich09c4f972019-10-21 19:51:413277 CookiesInheritedAboutBlank2) {
arthursonzognicd49c292019-10-01 17:06:573278 // This test expects several cross-site navigation to happen.
Takashi Toyoshimae9c959ce92025-05-20 19:18:383279 if (!AreAllSitesIsolatedForTesting()) {
arthursonzognicd49c292019-10-01 17:06:573280 return;
Takashi Toyoshimae9c959ce92025-05-20 19:18:383281 }
arthursonzognicd49c292019-10-01 17:06:573282
3283 using Response = net::test_server::ControllableHttpResponse;
Maksim Orlovich09c4f972019-10-21 19:51:413284 Response response_1(https_server(), "/response_1");
3285 Response response_2(https_server(), "/response_2");
3286 Response response_3(https_server(), "/response_3");
arthursonzognicd49c292019-10-01 17:06:573287
Maksim Orlovich09c4f972019-10-21 19:51:413288 ASSERT_TRUE(https_server()->Start());
arthursonzognicd49c292019-10-01 17:06:573289
Maksim Orlovich09c4f972019-10-21 19:51:413290 GURL url_a(https_server()->GetURL("a.com", "/title1.html"));
3291 GURL url_b(https_server()->GetURL("b.com", "/title1.html"));
arthursonzognicd49c292019-10-01 17:06:573292 EXPECT_TRUE(NavigateToURL(shell(), url_a));
3293
3294 EXPECT_TRUE(
3295 ExecJs(shell(), JsReplace("let iframe = document.createElement('iframe');"
3296 "iframe.src = $1;"
3297 "document.body.appendChild(iframe);",
3298 url_b)));
arthursonzognia26fa9a2020-11-03 10:16:473299 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzognicd49c292019-10-01 17:06:573300 EXPECT_TRUE(ExecJs(shell(), R"(
3301 document.querySelector('iframe').src = "about:blank"
3302 )"));
arthursonzognia26fa9a2020-11-03 10:16:473303 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzognicd49c292019-10-01 17:06:573304
arthursonzognia26fa9a2020-11-03 10:16:473305 RenderFrameHostImpl* main_document = current_frame_host();
arthursonzognicd49c292019-10-01 17:06:573306 RenderFrameHostImpl* sub_document_1 =
3307 main_document->child_at(0)->current_frame_host();
3308 EXPECT_EQ(url::kAboutBlankURL, sub_document_1->GetLastCommittedURL());
3309 EXPECT_EQ(url::Origin::Create(url_a),
3310 sub_document_1->GetLastCommittedOrigin());
3311 EXPECT_EQ(main_document->GetSiteInstance(),
3312 sub_document_1->GetSiteInstance());
3313
3314 // 0. The default state doesn't contain any cookies.
3315 EXPECT_EQ("", EvalJs(main_document, "document.cookie"));
3316 EXPECT_EQ("", EvalJs(sub_document_1, "document.cookie"));
3317
3318 // 1. Set a cookie in the main document, it affects its child too.
3319 EXPECT_TRUE(ExecJs(main_document, "document.cookie = 'a=0';"));
3320
3321 EXPECT_EQ("a=0", EvalJs(main_document, "document.cookie"));
3322 EXPECT_EQ("a=0", EvalJs(sub_document_1, "document.cookie"));
3323
3324 // 2. Set a cookie in the child, it affects its parent too.
3325 EXPECT_TRUE(ExecJs(sub_document_1, "document.cookie = 'b=0';"));
3326
3327 EXPECT_EQ("a=0; b=0", EvalJs(main_document, "document.cookie"));
3328 EXPECT_EQ("a=0; b=0", EvalJs(sub_document_1, "document.cookie"));
3329
3330 // 3. Checks cookies are sent while requesting resources.
arthursonzogni3d3ec9e2021-03-04 11:00:503331 ExecuteScriptAsync(sub_document_1, "fetch('/response_1');");
arthursonzognicd49c292019-10-01 17:06:573332 response_1.WaitForRequest();
3333 EXPECT_EQ("a=0; b=0", response_1.http_request()->headers.at("Cookie"));
3334
3335 // 4. Navigate the iframe elsewhere.
3336 EXPECT_TRUE(ExecJs(sub_document_1, JsReplace("location.href = $1", url_b)));
arthursonzognia26fa9a2020-11-03 10:16:473337 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzognicd49c292019-10-01 17:06:573338 RenderFrameHostImpl* sub_document_2 =
3339 main_document->child_at(0)->current_frame_host();
3340
3341 EXPECT_EQ("a=0; b=0", EvalJs(main_document, "document.cookie"));
3342 EXPECT_EQ("", EvalJs(sub_document_2, "document.cookie"));
3343
3344 // 5. Set a cookie in the main document. It doesn't affect its child.
3345 EXPECT_TRUE(ExecJs(main_document, "document.cookie = 'c=0';"));
3346
3347 EXPECT_EQ("a=0; b=0; c=0", EvalJs(main_document, "document.cookie"));
3348 EXPECT_EQ("", EvalJs(sub_document_2, "document.cookie"));
3349
3350 // 6. Set a cookie in the child. It doesn't affect its parent.
Maksim Orlovich09c4f972019-10-21 19:51:413351 EXPECT_TRUE(ExecJs(sub_document_2,
3352 "document.cookie = 'd=0; SameSite=none; Secure';"));
arthursonzognicd49c292019-10-01 17:06:573353
3354 EXPECT_EQ("a=0; b=0; c=0", EvalJs(main_document, "document.cookie"));
3355 EXPECT_EQ("d=0", EvalJs(sub_document_2, "document.cookie"));
3356
3357 // 7. Checks cookies are sent while requesting resources.
arthursonzogni3d3ec9e2021-03-04 11:00:503358 ExecuteScriptAsync(sub_document_2, "fetch('/response_2');");
arthursonzognicd49c292019-10-01 17:06:573359 response_2.WaitForRequest();
3360 EXPECT_EQ("d=0", response_2.http_request()->headers.at("Cookie"));
3361
3362 // 8. Ask the top-level, a.com frame to navigate the subframe to about:blank.
3363 EXPECT_TRUE(ExecJs(shell(), R"(
3364 document.querySelector('iframe').src = "about:blank";
3365 )"));
arthursonzognia26fa9a2020-11-03 10:16:473366 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzognicd49c292019-10-01 17:06:573367 RenderFrameHostImpl* sub_document_3 =
3368 main_document->child_at(0)->current_frame_host();
3369 EXPECT_EQ(url::kAboutBlankURL, sub_document_3->GetLastCommittedURL());
3370 EXPECT_EQ(url::Origin::Create(url_a),
3371 sub_document_3->GetLastCommittedOrigin());
3372 EXPECT_EQ(main_document->GetSiteInstance(),
3373 sub_document_3->GetSiteInstance());
3374
3375 EXPECT_EQ("a=0; b=0; c=0", EvalJs(main_document, "document.cookie"));
3376 EXPECT_EQ("a=0; b=0; c=0", EvalJs(sub_document_3, "document.cookie"));
3377
3378 // 9. Set cookie in the main document.
3379 EXPECT_TRUE(ExecJs(main_document, "document.cookie = 'e=0';"));
3380
3381 EXPECT_EQ("a=0; b=0; c=0; e=0", EvalJs(main_document, "document.cookie"));
3382 EXPECT_EQ("a=0; b=0; c=0; e=0", EvalJs(sub_document_3, "document.cookie"));
3383
3384 // 10. Set cookie in the child document.
3385 EXPECT_TRUE(ExecJs(sub_document_3, "document.cookie = 'f=0';"));
3386
3387 EXPECT_EQ("a=0; b=0; c=0; e=0; f=0",
3388 EvalJs(main_document, "document.cookie"));
3389 EXPECT_EQ("a=0; b=0; c=0; e=0; f=0",
3390 EvalJs(sub_document_3, "document.cookie"));
3391
3392 // 11. Checks cookies are sent while requesting resources.
arthursonzogni3d3ec9e2021-03-04 11:00:503393 ExecuteScriptAsync(sub_document_3, "fetch('/response_3');");
arthursonzognicd49c292019-10-01 17:06:573394 response_3.WaitForRequest();
3395 EXPECT_EQ("a=0; b=0; c=0; e=0; f=0",
3396 response_3.http_request()->headers.at("Cookie"));
3397}
3398
3399// Test how cookies are inherited in data-URL iframes.
Minggang Wang83f7f3712019-11-16 11:28:163400IN_PROC_BROWSER_TEST_F(NavigationCookiesBrowserTest, CookiesInheritedDataUrl) {
arthursonzognicd49c292019-10-01 17:06:573401 using Response = net::test_server::ControllableHttpResponse;
Maksim Orlovich09c4f972019-10-21 19:51:413402 Response response_1(https_server(), "/response_1");
3403 Response response_2(https_server(), "/response_2");
3404 Response response_3(https_server(), "/response_3");
arthursonzognicd49c292019-10-01 17:06:573405
Maksim Orlovich09c4f972019-10-21 19:51:413406 ASSERT_TRUE(https_server()->Start());
arthursonzognicd49c292019-10-01 17:06:573407
Maksim Orlovich09c4f972019-10-21 19:51:413408 GURL url_a(https_server()->GetURL("a.com", "/title1.html"));
3409 GURL url_b(https_server()->GetURL("b.com", "/title1.html"));
arthursonzognicd49c292019-10-01 17:06:573410 EXPECT_TRUE(NavigateToURL(shell(), url_a));
3411
3412 EXPECT_TRUE(ExecJs(shell(), R"(
3413 let iframe = document.createElement("iframe");
3414 iframe.src = "data:text/html,";
3415 document.body.appendChild(iframe);
3416 )"));
arthursonzognia26fa9a2020-11-03 10:16:473417 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzognicd49c292019-10-01 17:06:573418
arthursonzognia26fa9a2020-11-03 10:16:473419 RenderFrameHostImpl* main_document = current_frame_host();
arthursonzognicd49c292019-10-01 17:06:573420 RenderFrameHostImpl* sub_document_1 =
3421 main_document->child_at(0)->current_frame_host();
3422 EXPECT_EQ("data:text/html,", sub_document_1->GetLastCommittedURL());
3423 EXPECT_TRUE(sub_document_1->GetLastCommittedOrigin().opaque());
Sharon Yang85d8ee72024-11-13 00:52:223424 if (ShouldCreateSiteInstanceForDataUrls()) {
3425 EXPECT_NE(main_document->GetSiteInstance(),
3426 sub_document_1->GetSiteInstance());
3427 EXPECT_EQ(main_document->GetSiteInstance()->group(),
3428 sub_document_1->GetSiteInstance()->group());
3429 } else {
3430 EXPECT_EQ(main_document->GetSiteInstance(),
3431 sub_document_1->GetSiteInstance());
3432 }
arthursonzognicd49c292019-10-01 17:06:573433
3434 // 1. Writing a cookie inside a data-URL document is forbidden.
Devlin Cronin90e6b3b2020-01-08 23:43:493435 {
arthursonzognia26fa9a2020-11-03 10:16:473436 WebContentsConsoleObserver console_observer(web_contents());
Devlin Cronin90e6b3b2020-01-08 23:43:493437 console_observer.SetPattern(
3438 "*Failed to set the 'cookie' property on 'Document': Cookies are "
3439 "disabled inside 'data:' URLs.*");
3440 ExecuteScriptAsync(sub_document_1, "document.cookie = 'a=0';");
Fergal Daly7723f9d2022-10-29 07:03:133441 ASSERT_TRUE(console_observer.Wait());
Devlin Cronin90e6b3b2020-01-08 23:43:493442 }
arthursonzognicd49c292019-10-01 17:06:573443
3444 // 2. Reading a cookie inside a data-URL document is forbidden.
Devlin Cronin90e6b3b2020-01-08 23:43:493445 {
arthursonzognia26fa9a2020-11-03 10:16:473446 WebContentsConsoleObserver console_observer(web_contents());
Devlin Cronin90e6b3b2020-01-08 23:43:493447 console_observer.SetPattern(
3448 "*Failed to read the 'cookie' property from 'Document': Cookies are "
3449 "disabled inside 'data:' URLs.*");
3450 ExecuteScriptAsync(sub_document_1, "document.cookie");
Fergal Daly7723f9d2022-10-29 07:03:133451 ASSERT_TRUE(console_observer.Wait());
Devlin Cronin90e6b3b2020-01-08 23:43:493452 }
arthursonzognicd49c292019-10-01 17:06:573453
3454 // 3. Set cookie in the main document. No cookies are sent when requested from
3455 // the data-URL.
3456 EXPECT_TRUE(ExecJs(main_document, "document.cookie = 'a=0;SameSite=Lax'"));
3457 EXPECT_TRUE(ExecJs(main_document, "document.cookie = 'b=0;SameSite=Strict'"));
Maksim Orlovich09c4f972019-10-21 19:51:413458 GURL url_response_1 = https_server()->GetURL("a.com", "/response_1");
arthursonzogni3d3ec9e2021-03-04 11:00:503459 ExecuteScriptAsync(sub_document_1, JsReplace("fetch($1)", url_response_1));
arthursonzognicd49c292019-10-01 17:06:573460 response_1.WaitForRequest();
3461 EXPECT_EQ(0u, response_1.http_request()->headers.count("Cookie"));
3462
3463 // 4. Navigate the iframe elsewhere and back using history navigation.
3464 EXPECT_TRUE(ExecJs(sub_document_1, JsReplace("location.href = $1", url_b)));
arthursonzognia26fa9a2020-11-03 10:16:473465 EXPECT_TRUE(WaitForLoadStop(web_contents()));
3466 web_contents()->GetController().GoBack();
3467 EXPECT_TRUE(WaitForLoadStop(web_contents()));
arthursonzognicd49c292019-10-01 17:06:573468 RenderFrameHostImpl* sub_document_2 =
3469 main_document->child_at(0)->current_frame_host();
3470 EXPECT_EQ(url_a, main_document->GetLastCommittedURL());
3471 EXPECT_EQ("data:text/html,", sub_document_2->GetLastCommittedURL());
3472 EXPECT_TRUE(sub_document_2->GetLastCommittedOrigin().opaque());
Sharon Yang85d8ee72024-11-13 00:52:223473 if (ShouldCreateSiteInstanceForDataUrls()) {
3474 EXPECT_NE(main_document->GetSiteInstance(),
3475 sub_document_2->GetSiteInstance());
3476 EXPECT_EQ(main_document->GetSiteInstance()->GetSiteInstanceGroupId(),
3477 sub_document_2->GetSiteInstance()->GetSiteInstanceGroupId());
3478 } else {
3479 EXPECT_EQ(main_document->GetSiteInstance(),
3480 sub_document_2->GetSiteInstance());
3481 }
arthursonzognicd49c292019-10-01 17:06:573482
3483 // 5. Writing a cookie inside a data-URL document is still forbidden.
Devlin Cronin90e6b3b2020-01-08 23:43:493484 {
arthursonzognia26fa9a2020-11-03 10:16:473485 WebContentsConsoleObserver console_observer(web_contents());
Devlin Cronin90e6b3b2020-01-08 23:43:493486 console_observer.SetPattern(
3487 "*Failed to set the 'cookie' property on 'Document': Cookies are "
3488 "disabled inside 'data:' URLs.*");
3489 ExecuteScriptAsync(sub_document_2, "document.cookie = 'c=0';");
Fergal Daly7723f9d2022-10-29 07:03:133490 ASSERT_TRUE(console_observer.Wait());
Devlin Cronin90e6b3b2020-01-08 23:43:493491 }
arthursonzognicd49c292019-10-01 17:06:573492
3493 // 6. Reading a cookie inside a data-URL document is still forbidden.
Devlin Cronin90e6b3b2020-01-08 23:43:493494 {
arthursonzognia26fa9a2020-11-03 10:16:473495 WebContentsConsoleObserver console_observer(web_contents());
Devlin Cronin90e6b3b2020-01-08 23:43:493496 console_observer.SetPattern(
3497 "*Failed to read the 'cookie' property from 'Document': Cookies are "
3498 "disabled inside 'data:' URLs.*");
3499 ExecuteScriptAsync(sub_document_2, "document.cookie");
Fergal Daly7723f9d2022-10-29 07:03:133500 ASSERT_TRUE(console_observer.Wait());
Devlin Cronin90e6b3b2020-01-08 23:43:493501 }
arthursonzognicd49c292019-10-01 17:06:573502
3503 // 7. No cookies are sent when requested from the data-URL.
Maksim Orlovich09c4f972019-10-21 19:51:413504 GURL url_response_2 = https_server()->GetURL("a.com", "/response_2");
arthursonzogni3d3ec9e2021-03-04 11:00:503505 ExecuteScriptAsync(sub_document_2, JsReplace("fetch($1)", url_response_2));
arthursonzognicd49c292019-10-01 17:06:573506 response_2.WaitForRequest();
3507 EXPECT_EQ(0u, response_2.http_request()->headers.count("Cookie"));
3508}
3509
dpapadfeeae4272023-06-16 01:03:103510// Tests for validating URL rewriting behavior like chrome://newtab to
3511// chrome-native://newtab.
Aaron Colwell4696a7b2019-11-07 22:28:563512class NavigationUrlRewriteBrowserTest : public NavigationBaseBrowserTest {
3513 protected:
3514 static constexpr const char* kRewriteURL = "http://a.com/rewrite";
3515 static constexpr const char* kNoAccessScheme = "no-access";
3516 static constexpr const char* kNoAccessURL = "no-access://testing/";
3517
Scott Violet99861992023-02-08 01:20:123518 class BrowserClient : public ContentBrowserTestContentBrowserClient {
Aaron Colwell4696a7b2019-11-07 22:28:563519 public:
3520 void BrowserURLHandlerCreated(BrowserURLHandler* handler) override {
3521 handler->AddHandlerPair(RewriteUrl,
3522 BrowserURLHandlerImpl::null_handler());
Lukasz Anforowicze2a822d2020-10-05 20:58:263523 fake_url_loader_factory_ = std::make_unique<FakeNetworkURLLoaderFactory>(
3524 "HTTP/1.1 200 OK\nContent-Type: text/html\n\n", "This is a test",
3525 /* network_accessed */ true, net::OK);
Aaron Colwell4696a7b2019-11-07 22:28:563526 }
3527
Hiroshige Hayashizaki26bbd5862024-02-21 16:56:433528 mojo::PendingRemote<network::mojom::URLLoaderFactory>
3529 CreateNonNetworkNavigationURLLoaderFactory(
3530 const std::string& scheme,
Avi Drissmanbd153642024-09-03 18:58:053531 FrameTreeNodeId frame_tree_node_id) override {
Hiroshige Hayashizaki26bbd5862024-02-21 16:56:433532 if (scheme == kNoAccessScheme) {
3533 mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote;
3534 fake_url_loader_factory_->Clone(
3535 pending_remote.InitWithNewPipeAndPassReceiver());
3536 return pending_remote;
3537 }
3538 return {};
Aaron Colwell4696a7b2019-11-07 22:28:563539 }
3540
Aaron Colwell4696a7b2019-11-07 22:28:563541 static bool RewriteUrl(GURL* url, BrowserContext* browser_context) {
3542 if (*url == GURL(kRewriteURL)) {
3543 *url = GURL(kNoAccessURL);
3544 return true;
3545 }
3546 return false;
3547 }
Lukasz Anforowicze2a822d2020-10-05 20:58:263548
3549 private:
3550 std::unique_ptr<FakeNetworkURLLoaderFactory> fake_url_loader_factory_;
Aaron Colwell4696a7b2019-11-07 22:28:563551 };
3552
Michael Thiessen2add7d442020-02-05 13:49:383553 NavigationUrlRewriteBrowserTest() {
Aaron Colwell4696a7b2019-11-07 22:28:563554 url::AddStandardScheme(kNoAccessScheme, url::SCHEME_WITH_HOST);
3555 url::AddNoAccessScheme(kNoAccessScheme);
Alex Moshchukd8e016d2023-03-20 17:18:123556
3557 // This test needs to use an unassigned SiteInstance for kNoAccessScheme,
3558 // which requires adding it as an empty document scheme.
3559 url::AddEmptyDocumentScheme(kNoAccessScheme);
Aaron Colwell4696a7b2019-11-07 22:28:563560 }
3561
3562 void SetUpOnMainThread() override {
3563 NavigationBaseBrowserTest::SetUpOnMainThread();
3564 ASSERT_TRUE(embedded_test_server()->Start());
3565
3566 browser_client_ = std::make_unique<BrowserClient>();
Aaron Colwell4696a7b2019-11-07 22:28:563567 }
3568
3569 void TearDownOnMainThread() override {
Aaron Colwell4696a7b2019-11-07 22:28:563570 browser_client_.reset();
3571
3572 NavigationBaseBrowserTest::TearDownOnMainThread();
3573 }
3574
3575 GURL GetRewriteToNoAccessURL() const { return GURL(kRewriteURL); }
3576
3577 private:
3578 std::unique_ptr<BrowserClient> browser_client_;
Michael Thiessen2add7d442020-02-05 13:49:383579 url::ScopedSchemeRegistryForTests scoped_registry_;
Aaron Colwell4696a7b2019-11-07 22:28:563580};
3581
Aaron Colwell4696a7b2019-11-07 22:28:563582// Tests navigating to a URL that gets rewritten to a "no access" URL. This
3583// mimics the behavior of navigating to special URLs like chrome://newtab and
3584// chrome://history which get rewritten to "no access" chrome-native:// URLs.
Lukasz Anforowicz6a6a05d2021-01-12 01:40:073585IN_PROC_BROWSER_TEST_F(NavigationUrlRewriteBrowserTest, RewriteToNoAccess) {
Aaron Colwell4696a7b2019-11-07 22:28:563586 // Perform an initial navigation.
3587 {
arthursonzognia26fa9a2020-11-03 10:16:473588 TestNavigationObserver observer(web_contents());
Aaron Colwell4696a7b2019-11-07 22:28:563589 GURL url = embedded_test_server()->GetURL("a.com", "/title1.html");
3590 EXPECT_TRUE(NavigateToURL(shell(), url));
3591 EXPECT_EQ(url, observer.last_navigation_url());
3592 EXPECT_TRUE(observer.last_navigation_succeeded());
3593 EXPECT_FALSE(observer.last_initiator_origin().has_value());
3594 }
3595
3596 // Navigate to the URL that will get rewritten to a "no access" URL.
3597 {
arthursonzognia26fa9a2020-11-03 10:16:473598 TestNavigationObserver observer(web_contents());
Aaron Colwell4696a7b2019-11-07 22:28:563599
3600 // Note: We are using LoadURLParams here because we need to have the
3601 // initiator_origin set and NavigateToURL() does not do that.
3602 NavigationController::LoadURLParams params(GetRewriteToNoAccessURL());
arthursonzognia26fa9a2020-11-03 10:16:473603 params.initiator_origin = current_frame_host()->GetLastCommittedOrigin();
3604 web_contents()->GetController().LoadURLWithParams(params);
3605 web_contents()->Focus();
Aaron Colwell4696a7b2019-11-07 22:28:563606 observer.Wait();
3607
3608 EXPECT_EQ(GURL(kNoAccessURL), observer.last_navigation_url());
3609 EXPECT_TRUE(observer.last_navigation_succeeded());
3610 EXPECT_TRUE(observer.last_initiator_origin().has_value());
3611 }
3612}
3613
danakjc9e8e132020-12-03 00:48:183614IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, SameDocumentNavigation) {
3615 WebContents* wc = shell()->web_contents();
3616 GURL url1 = embedded_test_server()->GetURL("a.com", "/title1.html#frag1");
3617 GURL url2 = embedded_test_server()->GetURL("a.com", "/title1.html#frag2");
3618 NavigationHandleCommitObserver navigation_0(wc, url1);
3619 NavigationHandleCommitObserver navigation_1(wc, url2);
3620
3621 EXPECT_TRUE(NavigateToURL(shell(), url1));
3622 NavigationEntry* entry =
3623 web_contents()->GetController().GetLastCommittedEntry();
3624 EXPECT_TRUE(NavigateToURL(shell(), url2));
3625 // The NavigationEntry changes on a same-document navigation.
3626 EXPECT_NE(web_contents()->GetController().GetLastCommittedEntry(), entry);
3627
3628 EXPECT_TRUE(navigation_0.has_committed());
3629 EXPECT_TRUE(navigation_1.has_committed());
3630 EXPECT_FALSE(navigation_0.was_same_document());
3631 EXPECT_TRUE(navigation_1.was_same_document());
3632}
3633
3634// Some navigations are not allowed, such as when they fail the content security
3635// policy, or for trying to load about:srcdoc in the main frame. These result in
3636// us redirecting the navigation to an error page via
3637// RenderFrameHostImpl::FailedNavigation().
3638// Repeating the request with a different URL fragment results in attempting a
3639// same-document navigation, but error pages do not support such navigations. In
3640// this case treat each failed navigation request as a separate load, with the
3641// resulting navigation being performed as a cross-document navigation. This is
3642// regression test for https://crbug.com/1018385.
Minggang Wang83f7f3712019-11-16 11:28:163643IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
danakjc9e8e132020-12-03 00:48:183644 SameDocumentNavigationOnBlockedPage) {
3645 GURL url1("about:srcdoc#0");
3646 GURL url2("about:srcdoc#1");
3647 NavigationHandleCommitObserver navigation_0(web_contents(), url1);
3648 NavigationHandleCommitObserver navigation_1(web_contents(), url2);
arthursonzogni1b0a9912019-11-05 12:51:493649
3650 // Big warning: about:srcdoc is not supposed to be valid browser-initiated
3651 // main-frame navigation, it is currently blocked by the NavigationRequest.
3652 // It is used here to reproduce bug https://crbug.com/1018385. Please avoid
3653 // copying this kind of navigation in your own tests.
danakjc9e8e132020-12-03 00:48:183654 EXPECT_FALSE(NavigateToURL(shell(), url1));
3655 EXPECT_FALSE(NavigateToURL(shell(), url2));
arthursonzogni1b0a9912019-11-05 12:51:493656
3657 EXPECT_TRUE(navigation_0.has_committed());
3658 EXPECT_TRUE(navigation_1.has_committed());
3659 EXPECT_FALSE(navigation_0.was_same_document());
3660 EXPECT_FALSE(navigation_1.was_same_document());
3661}
3662
danakjc9e8e132020-12-03 00:48:183663// This navigation is allowed by the browser, but the network will not be able
3664// to connect to the site, so the NavigationRequest fails on the browser side
3665// and is redirected to an error page. Performing another navigation should
3666// make the full attempt again, in case the network request succeeds this time.
3667IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
3668 SameDocumentNavigationOnBadServerErrorPage) {
3669 GURL url1("http://badserver.com:9/");
3670 GURL url2("http://badserver.com:9/#1");
3671 NavigationHandleCommitObserver navigation_0(web_contents(), url1);
3672 NavigationHandleCommitObserver navigation_1(web_contents(), url2);
3673
3674 // The navigation is okay from the browser's perspective, so NavigateToURL()
3675 // will return true. But the network request ultimately fails, so the request
3676 // is redirected to an error page.
3677 EXPECT_FALSE(NavigateToURL(shell(), url1));
3678 EXPECT_TRUE(navigation_0.has_committed());
3679 EXPECT_FALSE(navigation_0.was_same_document());
3680
3681 // The 2nd request shares a URL but it should be another cross-document
3682 // navigation, rather than trying to navigate inside the error page.
3683 EXPECT_FALSE(NavigateToURL(shell(), url2));
3684 EXPECT_TRUE(navigation_1.has_committed());
3685 EXPECT_FALSE(navigation_1.was_same_document());
3686}
3687
3688// This navigation is allowed by the browser, and the request to the server is
3689// successful, but it returns 404 error headers, and (optionally) an error page.
3690// When another request is made for the same page but with a different fragment,
3691// the browser will attempt to perform a same-document navigation but that
3692// navigation is intended for the actual document not the error page that has
3693// been loaded instead. A same-document navigation in the renderer-loaded error
3694// page should be performed as a cross-document navigation in order to attempt
3695// to reload the page.
3696IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
3697 SameDocumentNavigationOn404ErrorPage) {
3698 // This case is a non-empty 404 page. It makes different choices about where
3699 // to load the page on a same-document navigation.
3700 {
3701 GURL url1 = embedded_test_server()->GetURL("a.com", "/page404.html");
3702 GURL url2 = embedded_test_server()->GetURL("a.com", "/page404.html#1");
3703 NavigationHandleCommitObserver navigation_0(web_contents(), url1);
3704 NavigationHandleCommitObserver navigation_1(web_contents(), url2);
3705
3706 EXPECT_TRUE(NavigateToURL(shell(), url1));
3707 EXPECT_TRUE(navigation_0.has_committed());
3708 EXPECT_FALSE(navigation_0.was_same_document());
3709
3710 // This is another navigation to the non-existent URL, but with a different
3711 // fragment. We have successfully loaded content from a.com. The fact that
3712 // it is 404 response does not mean it is an error page, since the term
3713 // "error page" is used for cases where the browser encounters an error
3714 // loading a document from the origin. HTTP responses with >400 status codes
3715 // are just like regular documents from the origin and we render their
3716 // response body just like we would a 200 response. This is why it can make
3717 // sense for a same document navigation to be performed from a 404 page.
3718 EXPECT_TRUE(NavigateToURL(shell(), url2));
3719 EXPECT_TRUE(navigation_1.has_committed());
3720 EXPECT_TRUE(navigation_1.was_same_document());
3721 }
3722 // This case is an empty 404 page. It makes different choices about where
3723 // to load the page on a same-document navigation. Since the server has only
3724 // replied with an error, the browser will display its own error page and
3725 // therefore it is not one coming from the server's origin.
3726 {
3727 GURL url1 = embedded_test_server()->GetURL("a.com", "/empty404.html");
3728 GURL url2 = embedded_test_server()->GetURL("a.com", "/empty404.html#1");
3729 NavigationHandleCommitObserver navigation_0(web_contents(), url1);
3730 NavigationHandleCommitObserver navigation_1(web_contents(), url2);
3731
3732 EXPECT_FALSE(NavigateToURL(shell(), url1));
3733 EXPECT_TRUE(navigation_0.has_committed());
3734 EXPECT_FALSE(navigation_0.was_same_document());
3735
3736 // This is another navigation to the non-existent URL, but with a different
3737 // fragment. Since we did not load a document from the server (we got
3738 // `false` from `NavigateToURL()`) there is no server-provided document to
3739 // navigate within. The result should be a cross-document navigation in
3740 // order to attempt to load the document at the given path from the server
3741 // again.
3742 EXPECT_FALSE(NavigateToURL(shell(), url2));
3743 EXPECT_TRUE(navigation_1.has_committed());
3744 EXPECT_FALSE(navigation_1.was_same_document());
3745 }
Rakina Zata Amni94932ec2021-03-30 00:24:383746
3747 // This case is also an empty 404 page, but we do replaceState and pushState
3748 // afterwards, creating successful same-document navigations.
3749 {
3750 // Navigate to empty 404, committing an error page.
3751 GURL url1 = embedded_test_server()->GetURL("a.com", "/empty404.html");
3752 NavigationHandleCommitObserver navigation(web_contents(), url1);
3753 EXPECT_FALSE(NavigateToURL(shell(), url1));
3754 EXPECT_TRUE(navigation.has_committed());
3755 EXPECT_FALSE(navigation.was_same_document());
3756
3757 // replaceState on an error page, without changing the URL.
3758 {
3759 FrameNavigateParamsCapturer capturer(main_frame());
3760 capturer.set_wait_for_load(false);
3761 EXPECT_TRUE(ExecJs(shell(), "history.replaceState('foo', '')"));
3762 capturer.Wait();
3763 EXPECT_TRUE(capturer.is_same_document());
3764 }
3765
3766 // pushState on an error page, without changing the URL.
3767 {
3768 FrameNavigateParamsCapturer capturer(main_frame());
3769 capturer.set_wait_for_load(false);
3770 EXPECT_TRUE(ExecJs(shell(), "history.pushState('foo', '')"));
3771 capturer.Wait();
3772 EXPECT_TRUE(capturer.is_same_document());
3773 }
3774 }
danakjc9e8e132020-12-03 00:48:183775}
3776
3777IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
3778 SameDocumentNavigationFromCrossDocumentRedirect) {
3779 WebContents* wc = shell()->web_contents();
3780 GURL url0 = embedded_test_server()->GetURL("/title1.html#frag1");
3781 GURL url1 =
3782 embedded_test_server()->GetURL("/server-redirect?title1.html#frag2");
3783 GURL url2 = embedded_test_server()->GetURL("/title1.html#frag2");
3784 NavigationHandleCommitObserver navigation_0(wc, url0);
3785 NavigationHandleCommitObserver navigation_1(wc, url1);
3786 NavigationHandleCommitObserver navigation_2(wc, url2);
3787
3788 EXPECT_TRUE(NavigateToURL(shell(), url0));
3789 // Since the redirect does not land at the URL we passed in, we get a false
3790 // return here.
3791 EXPECT_FALSE(NavigateToURL(shell(), url1));
3792
3793 // The navigation to |url1| is redirected and so |url1| does not commit. Then
3794 // the resulting navigation to |url2| lands at the same document URL as |url0|
3795 // which would be a same-document navigation if there wasn't a redirect
3796 // involved. But since it started as a cross-document navigation it results in
3797 // loading a new document instead of doing a same-document navigation.
3798 EXPECT_TRUE(navigation_0.has_committed());
3799 EXPECT_FALSE(navigation_1.has_committed());
3800 EXPECT_TRUE(navigation_2.has_committed());
3801 EXPECT_FALSE(navigation_0.was_same_document());
3802 EXPECT_FALSE(navigation_1.was_same_document());
3803 EXPECT_FALSE(navigation_2.was_same_document());
3804
Dave Tapuska327c06c92022-06-13 20:31:513805 EXPECT_EQ(wc->GetPrimaryMainFrame()->GetLastCommittedURL(), url2);
Ali Beyadb21baf5f2022-03-31 00:58:063806
3807 // Redirect should not record a ReceivedResponse event.
3808 EXPECT_EQ(1u, test_ukm_recorder()
3809 .GetEntriesByName("Navigation.ReceivedResponse")
3810 .size());
danakjc9e8e132020-12-03 00:48:183811}
3812
danakj5cd7dd32021-06-09 13:56:483813// 1. The browser navigates to a.html.
3814// 2. The renderer uses history.pushState() to change the URL of the current
3815// document from a.html to b.html.
3816// 3. The browser tries to perform a same-document navigation to a.html#foo,
3817// since it did not hear about the document's URL changing yet. When it gets
3818// to the renderer, we discover a race has happened.
3819// 4. Meanwhile, the browser hears about the URL change to b.html and applies
3820// it.
3821// Now - how do we resolve the race?
3822// 5. We will reorder the a.html#foo navigation to start over in the browser
3823// after the b.html navigation.
3824// Technically, this is still a same-document navigation! The URL changed but
3825// the document did not. Currently, however, the browser only considers the URL
3826// when performing a non-history navigation to decide if it's a same-document
3827// navigation, so..
3828// 6. The browser will perform a cross-document navigation to a.html#foo.
Hiroki Nakagawa6efbc8562021-10-21 07:36:423829//
Alison Gale81f4f2c72024-04-22 19:33:313830// TODO(crbug.com/40799231): Test is flaky on various platforms.
danakj5cd7dd32021-06-09 13:56:483831IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
Hiroki Nakagawa6efbc8562021-10-21 07:36:423832 DISABLED_SameDocumentNavigationRacesPushStateURLChange) {
danakj5cd7dd32021-06-09 13:56:483833 WebContents* wc = shell()->web_contents();
3834 GURL url0 = embedded_test_server()->GetURL("/title1.html");
3835 GURL url1 = embedded_test_server()->GetURL("/title2.html");
3836 GURL url2 = embedded_test_server()->GetURL("/title1.html#frag2");
3837 NavigationHandleCommitObserver navigation_0(wc, url0);
3838 NavigationHandleCommitObserver navigation_1(wc, url1);
3839 NavigationHandleCommitObserver navigation_2(wc, url2);
3840
3841 // Start at `url0`.
3842 EXPECT_TRUE(NavigateToURL(shell(), url0));
3843
3844 // Have the renderer `history.pushState()` to `url1`, which leaves it on the
3845 // `url0` document, but with a different URL now.
3846 ExecuteScriptAsync(shell(), JsReplace("history.pushState('', '', $1);"
3847 "window.location.href == $1;",
3848 url1));
3849
3850 // The browser didn't hear about the change yet.
Dave Tapuska327c06c92022-06-13 20:31:513851 EXPECT_EQ(wc->GetPrimaryMainFrame()->GetLastCommittedURL(), url0);
danakj5cd7dd32021-06-09 13:56:483852
3853 {
3854 // We will wait for 2 navigations: one will be the pushState() and the other
3855 // will be the navigation to `url2` started below.
3856 TestNavigationObserver nav_observer(wc, 2);
3857
Lukasz Anforowicz051314d2021-09-29 20:07:393858 // Start a same-document navigation to url2 that is racing with the
3859 // renderer's history.pushState().
3860 shell()->LoadURL(url2);
danakj5cd7dd32021-06-09 13:56:483861
3862 nav_observer.Wait();
3863 }
3864
3865 // The last navigation to resolve is the one to `url2` as it's reordered to
3866 // come after the race with the already-completed history.pushState().
Dave Tapuska327c06c92022-06-13 20:31:513867 EXPECT_EQ(wc->GetPrimaryMainFrame()->GetLastCommittedURL(), url2);
danakj5cd7dd32021-06-09 13:56:483868
3869 // Navigation 0 was a cross-document navigation, to initially load the
3870 // document.
3871 EXPECT_TRUE(navigation_0.has_committed());
3872 EXPECT_FALSE(navigation_0.was_same_document());
3873
3874 // Navigation 1 was a same-document navigation, from the renderer's
3875 // history.pushState() call.
3876 EXPECT_TRUE(navigation_1.has_committed());
3877 EXPECT_TRUE(navigation_1.was_same_document());
3878
3879 // Navigation 2 was restarted and came after. When it restarted, it saw the
3880 // URL did not match and did a cross-document navigation. Technically the same
3881 // document was still loaded from `url0`, but the browser makes its choice
3882 // on the document's current URL.
3883 EXPECT_TRUE(navigation_2.has_committed());
3884 EXPECT_FALSE(navigation_2.was_same_document());
3885}
3886
Scott Violet99861992023-02-08 01:20:123887class GetEffectiveUrlClient : public ContentBrowserTestContentBrowserClient {
danakjc9e8e132020-12-03 00:48:183888 public:
3889 GURL GetEffectiveURL(content::BrowserContext* browser_context,
3890 const GURL& url) override {
Takashi Toyoshimae9c959ce92025-05-20 19:18:383891 if (effective_url_) {
danakjc9e8e132020-12-03 00:48:183892 return *effective_url_;
Takashi Toyoshimae9c959ce92025-05-20 19:18:383893 }
danakjc9e8e132020-12-03 00:48:183894 return url;
3895 }
3896
3897 bool IsSuitableHost(RenderProcessHost* process_host,
3898 const GURL& site_url) override {
Takashi Toyoshimae9c959ce92025-05-20 19:18:383899 if (!disallowed_process_id_) {
danakjc9e8e132020-12-03 00:48:183900 return true;
Takashi Toyoshimae9c959ce92025-05-20 19:18:383901 }
Emily Andrewsd15fd762024-12-10 20:41:543902 return process_host->GetDeprecatedID() != disallowed_process_id_;
danakjc9e8e132020-12-03 00:48:183903 }
3904
3905 void set_effective_url(const GURL& url) { effective_url_ = url; }
3906
3907 void set_disallowed_process(int id) { disallowed_process_id_ = id; }
3908
3909 private:
Arthur Sonzognic686e8f2024-01-11 08:36:373910 std::optional<GURL> effective_url_;
danakjc9e8e132020-12-03 00:48:183911 int disallowed_process_id_ = 0;
3912};
3913
3914// While a document is open, state in the browser may change such that loading
3915// the document would choose a different SiteInstance. A cross-document
3916// navigation would pick up this different SiteInstance, but a same-document
3917// navigation should not. It should just navigate inside the currently loaded
3918// document instead of reloading the document.
3919IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
3920 SameDocumentNavigationWhenSiteInstanceWouldChange) {
3921 auto* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
3922 GURL url0 = embedded_test_server()->GetURL("a.com", "/title1.html#ref1");
3923 GURL url1 = embedded_test_server()->GetURL("a.com", "/title1.html#ref2");
3924
3925 GetEffectiveUrlClient new_client;
danakjc9e8e132020-12-03 00:48:183926
3927 NavigationHandleCommitObserver navigation_0(wc, url0);
3928 EXPECT_TRUE(NavigateToURL(shell(), url0));
3929 EXPECT_TRUE(navigation_0.has_committed());
3930 EXPECT_FALSE(navigation_0.was_same_document());
3931
Dave Tapuska327c06c92022-06-13 20:31:513932 RenderFrameHost* main_frame_host = wc->GetPrimaryMainFrame();
danakjc9e8e132020-12-03 00:48:183933 RenderProcessHost* main_frame_process_host = main_frame_host->GetProcess();
3934
3935 // When we both change the effective URL and also disallow the current
3936 // renderer process, a new load of the current document would get a different
3937 // SiteInstance.
3938 GURL modified_url0 =
3939 embedded_test_server()->GetURL("c.com", "/title1.html#ref1");
3940 new_client.set_effective_url(modified_url0);
Emily Andrewsd15fd762024-12-10 20:41:543941 new_client.set_disallowed_process(main_frame_process_host->GetDeprecatedID());
danakjc9e8e132020-12-03 00:48:183942
3943 NavigationHandleCommitObserver navigation_1(wc, url1);
3944 EXPECT_TRUE(NavigateToURL(shell(), url1));
3945 EXPECT_TRUE(navigation_1.has_committed());
3946 EXPECT_TRUE(navigation_1.was_same_document());
3947
3948 // The RenderFrameHost should not have changed, we should perform the
3949 // navigation in the currently loaded document.
Dave Tapuska327c06c92022-06-13 20:31:513950 EXPECT_EQ(main_frame_host, wc->GetPrimaryMainFrame());
3951 EXPECT_EQ(main_frame_process_host, wc->GetPrimaryMainFrame()->GetProcess());
danakjc9e8e132020-12-03 00:48:183952}
3953
3954// This tests the same ideas as the above test except in this case the same-
3955// document navigation is done through a history navigation, which exercises
3956// different codepaths in the NavigationControllerImpl.
3957IN_PROC_BROWSER_TEST_F(
3958 NavigationBrowserTest,
3959 SameDocumentHistoryNavigationWhenSiteInstanceWouldChange) {
3960 auto* wc = static_cast<WebContentsImpl*>(shell()->web_contents());
3961 GURL url0 = embedded_test_server()->GetURL("a.com", "/title1.html#ref1");
3962 GURL url1 = embedded_test_server()->GetURL("a.com", "/title1.html#ref2");
3963 NavigationHandleCommitObserver navigation_0(wc, url0);
3964 NavigationHandleCommitObserver navigation_1(wc, url1);
3965
3966 GetEffectiveUrlClient new_client;
danakjc9e8e132020-12-03 00:48:183967
3968 EXPECT_TRUE(NavigateToURL(shell(), url0));
3969 EXPECT_TRUE(navigation_0.has_committed());
3970 EXPECT_FALSE(navigation_0.was_same_document());
3971
3972 EXPECT_TRUE(NavigateToURL(shell(), url1));
3973 EXPECT_TRUE(navigation_1.has_committed());
3974 EXPECT_TRUE(navigation_1.was_same_document());
3975
Dave Tapuska327c06c92022-06-13 20:31:513976 RenderFrameHost* main_frame_host = wc->GetPrimaryMainFrame();
danakjc9e8e132020-12-03 00:48:183977 RenderProcessHost* main_frame_process_host = main_frame_host->GetProcess();
3978
3979 // When we both change the effective URL and also disallow the current
3980 // renderer process, a new load of the current document would get a different
3981 // SiteInstance.
3982 GURL modified_url0 =
3983 embedded_test_server()->GetURL("c.com", "/title1.html#ref1");
3984 new_client.set_effective_url(modified_url0);
Emily Andrewsd15fd762024-12-10 20:41:543985 new_client.set_disallowed_process(main_frame_process_host->GetDeprecatedID());
danakjc9e8e132020-12-03 00:48:183986
3987 // Navigates to the same-document. Since the SiteInstance changed, we would
3988 // normally try isolate this navigation by using a different RenderProcessHost
3989 // and RenderFrameHost. But since it is same-document, we want to avoid that
3990 // and perform the navigation inside the loaded |url0| document.
3991 wc->GetController().GoBack();
3992 EXPECT_TRUE(WaitForLoadStop(wc));
3993
3994 // The RenderFrameHost should not have changed, we should perform the
3995 // navigation in the currently loaded document.
Dave Tapuska327c06c92022-06-13 20:31:513996 EXPECT_EQ(main_frame_host, wc->GetPrimaryMainFrame());
3997 EXPECT_EQ(main_frame_process_host, wc->GetPrimaryMainFrame()->GetProcess());
danakjc9e8e132020-12-03 00:48:183998}
3999
Charlie Reis45da42612024-02-07 03:41:164000// Verify that actual renderer-initiated navigations to about:blank#blocked
4001// are respected, even though both the browser and renderer rewrite some illegal
4002// navigations to that URL as well. See https://crbug.com/40066983.
4003IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
4004 SameDocumentHashNavigationToBlockedFragmentAllowed) {
4005 const GURL url(embedded_test_server()->GetURL("/empty.html"));
4006 EXPECT_TRUE(NavigateToURL(shell(), url));
4007
4008 TestNavigationObserver blank_observer(web_contents());
4009 EXPECT_TRUE(ExecJs(shell(), "location.href = 'about:blank';"));
4010 blank_observer.Wait();
4011
4012 GURL blocked_url = GURL(kBlockedURL);
4013 TestNavigationObserver blocked_observer(web_contents());
4014 EXPECT_TRUE(ExecJs(shell(), JsReplace("location.href = $1", blocked_url)));
4015 blocked_observer.Wait();
4016
4017 // If the browser process receives a request to same-document navigate to
4018 // about:blank#blocked, the URL should be used and not ignored (as in a
4019 // blocked case like the SameDocumentLongURLHashNavigation test).
4020 EXPECT_EQ(blocked_url, web_contents()->GetLastCommittedURL());
4021}
4022
Charlie Reisbf7d6072024-02-12 19:14:334023// Verify that same-document navigations from about:blank to an excessively long
4024// fragment do not crash the browser.
4025IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
4026 SameDocumentAboutBlankLongURLHashNavigation) {
4027 const GURL blank_url(url::kAboutBlankURL);
4028 EXPECT_TRUE(NavigateToURL(shell(), blank_url));
4029
4030 std::string long_url = "#";
4031 long_url.append(2 * url::kMaxURLChars, 'a');
4032 EXPECT_TRUE(ExecJs(shell(), JsReplace("location = $1", long_url)));
4033
4034 // If the renderer attempts to navigate same-document from about:blank to a
4035 // too long hash (>2 MB), the URL (and base URL) will be blocked and rewritten
4036 // in the renderer to avoid the Mojo serialization limit. Ensure that the
4037 // browser process does not crash due to an empty base URL, and that the
4038 // blocked URL is used, unlike in the non-about:blank case in the
4039 // SameDocumentLongURLHashNavigation test.
Alison Gale770f3fc2024-04-27 00:39:584040 // TODO(crbug.com/40067230): Ideally this would be blocked earlier in the
Charlie Reisbf7d6072024-02-12 19:14:334041 // renderer process, failing the navigation.
4042 EXPECT_EQ(GURL(kBlockedURL), web_contents()->GetLastCommittedURL());
4043 // The renderer process considers the same-document navigation to the long URL
4044 // to have successfully completed.
4045 EXPECT_EQ(long_url, EvalJs(web_contents(), "location.hash"));
4046}
4047
Aran Gilman249eb122019-12-02 23:32:464048IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
Daniel Chengcb17f932023-07-17 22:26:534049 SameDocumentLongURLHashNavigation) {
4050 const GURL url(embedded_test_server()->GetURL("/empty.html"));
4051 EXPECT_TRUE(NavigateToURL(shell(), url));
4052
4053 std::string long_url = "#";
4054 long_url.append(2 * url::kMaxURLChars, 'a');
4055 EXPECT_TRUE(ExecJs(shell(), JsReplace("location = $1", long_url)));
4056
4057 // If the browser process receives a request to same-document navigate to a
4058 // too long URL (>2 MB), it simply pretends that the renderer performed a
4059 // same-document navigation to the currently committed URL (previously, it was
4060 // mapped to about:blank#blocked, which could be confusing).
Alison Gale770f3fc2024-04-27 00:39:584061 // TODO(crbug.com/40067230): Ideally this would be blocked in the renderer
Daniel Chengcb17f932023-07-17 22:26:534062 // instead of having special browser-side handling.
4063 EXPECT_EQ(url, web_contents()->GetLastCommittedURL());
4064 // The renderer process enforces no such limit and should consider the
4065 // same-document navigation to have successfully completed.
4066 EXPECT_EQ(long_url, EvalJs(web_contents(), "location.hash"));
4067}
4068
4069IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, SameDocumentLongURLPushState) {
4070 const GURL url(embedded_test_server()->GetURL("/empty.html"));
4071 EXPECT_TRUE(NavigateToURL(shell(), url));
4072
4073 std::string long_url = "#";
4074 long_url.append(2 * url::kMaxURLChars, 'a');
4075 EXPECT_TRUE(ExecJs(
4076 shell(), JsReplace("history.pushState('state', '', $1)", long_url)));
4077
4078 // If the browser process receives a request to same-document navigate to a
4079 // too long URL (>2 MB), it simply pretends that the renderer performed a
4080 // same-document navigation to the currently committed URL (previously, it was
4081 // mapped to about:blank#blocked, which could be confusing).
Alison Gale770f3fc2024-04-27 00:39:584082 // TODO(crbug.com/40067230): Ideally this would be blocked in the renderer
Daniel Chengcb17f932023-07-17 22:26:534083 // instead of having special browser-side handling.
4084 EXPECT_EQ(url, web_contents()->GetLastCommittedURL());
4085 // The renderer process enforces no such limit and should consider the
4086 // same-document navigation to have successfully completed.
4087 EXPECT_EQ(long_url, EvalJs(web_contents(), "location.hash"));
4088}
4089
4090IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
4091 SameDocumentLongURL204PopupHashNavigation) {
4092 const GURL url(embedded_test_server()->GetURL("/empty.html"));
4093 EXPECT_TRUE(NavigateToURL(shell(), url));
4094
4095 // Open a popup window with a navigation that will result in a 204. This will
4096 // result in a WebContents where the last committed URL is the empty URL.
4097 const GURL nocontent_url(embedded_test_server()->GetURL("/nocontent"));
4098 ShellAddedObserver new_shell_observer;
4099 EXPECT_TRUE(ExecJs(shell(), JsReplace("window.open($1);", nocontent_url)));
4100 Shell* opened_shell = new_shell_observer.GetShell();
4101 EXPECT_TRUE(WaitForLoadStop(opened_shell->web_contents()));
4102
4103 std::string long_url = "#";
4104 long_url.append(2 * url::kMaxURLChars, 'a');
4105 EXPECT_TRUE(ExecJs(opened_shell, JsReplace("location = $1", long_url)));
4106
4107 // Hash navigations in a popup in this state (incorrectly) perform a
4108 // cross-document navigation. This is because the check for whether or not to
4109 // perform a same-document navigation uses the initial empty Document's actual
4110 // URL (which is, surprisingly enough, the empty URL) rather than URL the web
4111 // platform generally sees (which is about:blank). As a result, the check ends
4112 // up comparing the empty URL against the completed URL of about:blank#...,
4113 // which means the URLs are not equal ignoring fragments, and Blink performs a
4114 // cross-document navigation instead.
4115 //
Alison Gale770f3fc2024-04-27 00:39:584116 // TODO(crbug.com/40922971): This probably should be fixed to be treated as a
Daniel Chengcb17f932023-07-17 22:26:534117 // same-document navigation.
4118 EXPECT_TRUE(WaitForLoadStop(opened_shell->web_contents()));
4119 EXPECT_EQ(GURL(kBlockedURL),
4120 opened_shell->web_contents()->GetLastCommittedURL());
4121 EXPECT_EQ(kBlockedURL, EvalJs(opened_shell->web_contents(), "location.href"));
4122}
4123
4124IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
4125 SameDocumentLongURL204PopupPushState) {
4126 const GURL url(embedded_test_server()->GetURL("/empty.html"));
4127 EXPECT_TRUE(NavigateToURL(shell(), url));
4128
4129 // Open a popup window with a navigation that will result in a 204. This will
4130 // result in a WebContents where the last committed URL is the empty URL.
4131 const GURL nocontent_url(embedded_test_server()->GetURL("/nocontent"));
4132 ShellAddedObserver new_shell_observer;
4133 EXPECT_TRUE(ExecJs(shell(), JsReplace("window.open($1);", nocontent_url)));
4134 Shell* opened_shell = new_shell_observer.GetShell();
4135 EXPECT_TRUE(WaitForLoadStop(opened_shell->web_contents()));
4136
4137 std::string long_url = "#";
4138 long_url.append(2 * url::kMaxURLChars, 'a');
4139 // Blink incorrectly disallows pushState() because the security check is
4140 // broken, since the security check uses the initial empty Document's actual
4141 // URL (which is, surprisingly enough, the empty URL) rather than the URL the
4142 // web platform generally sees (which is about:blank).
4143 //
Alison Gale770f3fc2024-04-27 00:39:584144 // TODO(crbug.com/40922971): This pushState() should probably be allowed.
Daniel Chengcb17f932023-07-17 22:26:534145 EXPECT_EQ(
4146 "SecurityError",
4147 EvalJs(
4148 opened_shell,
4149 JsReplace(
4150 "try { history.pushState('state', '', $1) } catch (e) { e.name }",
4151 long_url)));
4152}
4153
Charlie Reis673ad732024-08-15 18:32:084154// Ensure that no crash occurs when doing a same-document navigation within a
4155// site-less SiteInstance, such as for a browser-initiated about:blank.
4156// See https://crbug.com/359807735.
4157IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, SameDocumentSitelessNavigation) {
4158 WebContents* web_contents = shell()->web_contents();
4159 GURL url1 = GURL("about:blank#1");
4160 GURL url2 = GURL("about:blank#2");
4161 NavigationHandleCommitObserver navigation_1(web_contents, url1);
4162 NavigationHandleCommitObserver navigation_2(web_contents, url2);
4163
4164 EXPECT_TRUE(NavigateToURL(shell(), url1));
4165 EXPECT_TRUE(NavigateToURL(shell(), url2));
4166
4167 EXPECT_TRUE(navigation_1.has_committed());
4168 EXPECT_TRUE(navigation_2.has_committed());
4169 EXPECT_FALSE(navigation_1.was_same_document());
4170 EXPECT_TRUE(navigation_2.was_same_document());
4171}
4172
Daniel Chengcb17f932023-07-17 22:26:534173IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
Aran Gilman249eb122019-12-02 23:32:464174 NonDeterministicUrlRewritesUseLastUrl) {
4175 // Lambda expressions cannot be assigned to function pointers if they use
4176 // captures, so track how many times the handler is called using a non-const
4177 // static variable.
4178 static int rewrite_count;
4179 rewrite_count = 0;
4180
4181 BrowserURLHandler::URLHandler handler_method =
4182 [](GURL* url, BrowserContext* browser_context) {
4183 GURL::Replacements replace_path;
4184 if (rewrite_count > 0) {
4185 replace_path.SetPathStr("title2.html");
4186 } else {
4187 replace_path.SetPathStr("title1.html");
4188 }
4189 *url = url->ReplaceComponents(replace_path);
4190 rewrite_count++;
4191 return true;
4192 };
4193 BrowserURLHandler::GetInstance()->AddHandlerPair(
4194 handler_method, BrowserURLHandler::null_handler());
4195
arthursonzognia26fa9a2020-11-03 10:16:474196 TestNavigationObserver observer(web_contents());
Lukasz Anforowicz051314d2021-09-29 20:07:394197 shell()->LoadURL(embedded_test_server()->GetURL("/virtual-url.html"));
4198 observer.Wait();
Aran Gilman249eb122019-12-02 23:32:464199 EXPECT_EQ("/title2.html", observer.last_navigation_url().path());
4200 EXPECT_EQ(2, rewrite_count);
4201}
4202
arthursonzogni5a8f5ad2020-09-28 11:55:324203// Create two windows. When the second is deleted, it initiates a navigation in
Antonio Sartori9a82f6f32020-12-14 09:22:454204// the first. This is a situation where the navigation has an initiator frame
4205// token, but no corresponding RenderFrameHost.
arthursonzogni5a8f5ad2020-09-28 11:55:324206IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
Kurumi Mutobcc783562023-12-04 00:31:224207 RendererInitiatedCrossWindowNavigationInPagehide) {
arthursonzogni5a8f5ad2020-09-28 11:55:324208 GURL url(embedded_test_server()->GetURL("/empty.html"));
Antonio Sartoridb967c52021-01-20 09:54:304209 GURL always_referrer_url(embedded_test_server()->GetURL(
4210 "/set-header?Referrer-Policy: unsafe-url"));
arthursonzogni5a8f5ad2020-09-28 11:55:324211
4212 // Setup the opener window.
4213 EXPECT_TRUE(NavigateToURL(shell(), url));
4214
4215 // Setup the openee window;
4216 ShellAddedObserver new_shell_observer;
Antonio Sartoridb967c52021-01-20 09:54:304217 EXPECT_TRUE(
4218 ExecJs(shell(), JsReplace("window.open($1);", always_referrer_url)));
arthursonzogni5a8f5ad2020-09-28 11:55:324219 Shell* openee_shell = new_shell_observer.GetShell();
Antonio Sartoridb967c52021-01-20 09:54:304220 EXPECT_TRUE(WaitForLoadStop(openee_shell->web_contents()));
arthursonzogni5a8f5ad2020-09-28 11:55:324221
4222 // When deleted, the openee will initiate a navigation in its opener.
4223 EXPECT_TRUE(ExecJs(openee_shell, R"(
Kurumi Mutobcc783562023-12-04 00:31:224224 window.addEventListener("pagehide", () => {
arthursonzogni5a8f5ad2020-09-28 11:55:324225 opener.location.href = "about:blank";
4226 })
4227 )"));
4228
4229 RenderFrameHost* openee_rfh =
4230 static_cast<WebContentsImpl*>(openee_shell->web_contents())
Dave Tapuska327c06c92022-06-13 20:31:514231 ->GetPrimaryMainFrame();
Sharon Yang417a5df2024-04-23 17:57:154232 // Issue a KeepAlive for the navigation state so that the PolicyContainerHost
4233 // will still exist after the initiator RenderFrameHost is gone.
4234 mojo::PendingRemote<blink::mojom::NavigationStateKeepAliveHandle> keep_alive;
4235 static_cast<RenderFrameHostImpl*>(openee_rfh)
4236 ->IssueKeepAliveHandle(keep_alive.InitWithNewPipeAndPassReceiver());
4237
Dave Tapuskae9b7c0f72023-11-06 16:38:014238 auto initiator_global_token = openee_rfh->GetGlobalFrameToken();
arthursonzogni5a8f5ad2020-09-28 11:55:324239 base::RunLoop loop;
4240 DidStartNavigationCallback callback(
arthursonzognia26fa9a2020-11-03 10:16:474241 web_contents(), base::BindLambdaForTesting([&](NavigationHandle* handle) {
arthursonzogni5a8f5ad2020-09-28 11:55:324242 auto* request = NavigationRequest::From(handle);
Antonio Sartori9a82f6f32020-12-14 09:22:454243
Arthur Sonzognic686e8f2024-01-11 08:36:374244 const std::optional<blink::LocalFrameToken>& frame_token =
Antonio Sartori9a82f6f32020-12-14 09:22:454245 request->GetInitiatorFrameToken();
4246 EXPECT_TRUE(frame_token.has_value());
Dave Tapuskae9b7c0f72023-11-06 16:38:014247 EXPECT_EQ(initiator_global_token.frame_token, frame_token.value());
4248 EXPECT_EQ(initiator_global_token.child_id,
4249 request->GetInitiatorProcessId());
Antonio Sartori9a82f6f32020-12-14 09:22:454250
4251 auto* initiator_rfh = RenderFrameHostImpl::FromFrameToken(
Liam Brady767b85e2023-07-05 16:59:594252 request->GetInitiatorProcessId(), *frame_token);
arthursonzogni5a8f5ad2020-09-28 11:55:324253 ASSERT_FALSE(initiator_rfh);
Antonio Sartoridb967c52021-01-20 09:54:304254
4255 // Even if the initiator RenderFrameHost is gone, its policy container
4256 // should still be around since the LocalFrame has not been destroyed
4257 // yet.
Sharon Yang417a5df2024-04-23 17:57:154258 PolicyContainerHost* initiator_policy_container =
4259 RenderFrameHostImpl::GetPolicyContainerHost(
4260 base::OptionalToPtr(frame_token),
4261 request->GetInitiatorProcessId(),
4262 web_contents()->GetPrimaryMainFrame()->GetStoragePartition());
Antonio Sartoridb967c52021-01-20 09:54:304263 ASSERT_TRUE(initiator_policy_container);
4264 ASSERT_EQ(network::mojom::ReferrerPolicy::kAlways,
4265 initiator_policy_container->referrer_policy());
4266
4267 // Even if the initiator RenderFrameHost is gone, the navigation request
4268 // (to "about:blank") should have inherited its policy container.
Titouan Rigoudy2f995bc2021-02-19 19:39:414269 auto* initiator_policies =
4270 request->GetInitiatorPolicyContainerPolicies();
4271 ASSERT_TRUE(initiator_policies);
Antonio Sartoridb967c52021-01-20 09:54:304272 ASSERT_EQ(network::mojom::ReferrerPolicy::kAlways,
Titouan Rigoudy2f995bc2021-02-19 19:39:414273 initiator_policies->referrer_policy);
4274
arthursonzogni5a8f5ad2020-09-28 11:55:324275 loop.Quit();
4276 }));
4277
4278 // Delete the openee, which trigger the navigation in the opener.
4279 openee_shell->Close();
4280 loop.Run();
4281}
4282
4283// A document initiates a form submission in another frame, then deletes itself.
Antonio Sartori9a82f6f32020-12-14 09:22:454284// Check the initiator frame token.
arthursonzogni5a8f5ad2020-09-28 11:55:324285IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, FormSubmissionThenDeleteFrame) {
4286 GURL url(embedded_test_server()->GetURL("/empty.html"));
Antonio Sartoridb967c52021-01-20 09:54:304287 GURL always_referrer_url(embedded_test_server()->GetURL(
4288 "/set-header?Referrer-Policy: unsafe-url"));
arthursonzogni5a8f5ad2020-09-28 11:55:324289
4290 // Setup the opener window.
4291 EXPECT_TRUE(NavigateToURL(shell(), url));
4292
4293 // Setup the openee window;
4294 ShellAddedObserver new_shell_observer;
4295 EXPECT_TRUE(ExecJs(shell(), JsReplace("window.open($1);", url)));
4296 Shell* openee_shell = new_shell_observer.GetShell();
4297
4298 // Create a 'named' iframe in the first window. This will be the target of the
4299 // form submission.
4300 EXPECT_TRUE(ExecJs(shell(), R"(
4301 new Promise(resolve => {
4302 let iframe = document.createElement("iframe");
4303 iframe.onload = resolve;
4304 iframe.name = 'form-submission-target';
4305 iframe.src = location.href;
arthursonzogni5a8f5ad2020-09-28 11:55:324306 document.body.appendChild(iframe);
4307 });
4308 )"));
4309
4310 // Create an iframe in the second window. It will be initiating a form
4311 // submission and removing itself before the scheduled form navigation occurs.
Antonio Sartoridb967c52021-01-20 09:54:304312 // This iframe will have referrer policy "unsafe-url".
arthursonzogni5a8f5ad2020-09-28 11:55:324313 EXPECT_TRUE(WaitForLoadStop(openee_shell->web_contents()));
Antonio Sartoridb967c52021-01-20 09:54:304314 EXPECT_TRUE(ExecJs(openee_shell, JsReplace(R"(
arthursonzogni5a8f5ad2020-09-28 11:55:324315 new Promise(resolve => {
Antonio Sartoridb967c52021-01-20 09:54:304316 let iframe = document.createElement('iframe');
arthursonzogni5a8f5ad2020-09-28 11:55:324317 iframe.onload = resolve;
Antonio Sartoridb967c52021-01-20 09:54:304318 iframe.src = $1;
arthursonzogni5a8f5ad2020-09-28 11:55:324319 document.body.appendChild(iframe);
4320 });
Antonio Sartoridb967c52021-01-20 09:54:304321 )",
4322 always_referrer_url)));
arthursonzogni5a8f5ad2020-09-28 11:55:324323 EXPECT_TRUE(WaitForLoadStop(openee_shell->web_contents()));
4324
4325 RenderFrameHost* initiator_rfh =
4326 static_cast<WebContentsImpl*>(openee_shell->web_contents())
Dave Tapuska327c06c92022-06-13 20:31:514327 ->GetPrimaryMainFrame()
arthursonzogni5a8f5ad2020-09-28 11:55:324328 ->child_at(0)
4329 ->current_frame_host();
Dave Tapuskae9b7c0f72023-11-06 16:38:014330 auto initiator_global_token = initiator_rfh->GetGlobalFrameToken();
arthursonzogni5a8f5ad2020-09-28 11:55:324331 base::RunLoop loop;
4332 DidStartNavigationCallback callback(
arthursonzognia26fa9a2020-11-03 10:16:474333 web_contents(), base::BindLambdaForTesting([&](NavigationHandle* handle) {
arthursonzogni5a8f5ad2020-09-28 11:55:324334 auto* request = NavigationRequest::From(handle);
4335 ASSERT_TRUE(request->IsPost());
4336
Arthur Sonzognic686e8f2024-01-11 08:36:374337 const std::optional<blink::LocalFrameToken>& frame_token =
Antonio Sartori9a82f6f32020-12-14 09:22:454338 request->GetInitiatorFrameToken();
4339 EXPECT_TRUE(frame_token.has_value());
Dave Tapuskae9b7c0f72023-11-06 16:38:014340 EXPECT_EQ(initiator_global_token.frame_token, frame_token.value());
4341 EXPECT_EQ(initiator_global_token.child_id,
4342 request->GetInitiatorProcessId());
arthursonzogni5a8f5ad2020-09-28 11:55:324343
Sharon Yangd7b53f92024-02-16 18:28:504344 auto* deleted_initiator_rfh = RenderFrameHostImpl::FromFrameToken(
Liam Brady767b85e2023-07-05 16:59:594345 request->GetInitiatorProcessId(), frame_token.value());
Sharon Yangd7b53f92024-02-16 18:28:504346 ASSERT_FALSE(deleted_initiator_rfh);
arthursonzogni5a8f5ad2020-09-28 11:55:324347
Antonio Sartoridb967c52021-01-20 09:54:304348 // Even if the initiator RenderFrameHost is gone, its policy container
4349 // should still be around since the LocalFrame has not been destroyed
4350 // yet.
Sharon Yang417a5df2024-04-23 17:57:154351 PolicyContainerHost* initiator_policy_container =
4352 RenderFrameHostImpl::GetPolicyContainerHost(
4353 base::OptionalToPtr(frame_token),
4354 request->GetInitiatorProcessId(),
4355 web_contents()->GetPrimaryMainFrame()->GetStoragePartition());
Antonio Sartoridb967c52021-01-20 09:54:304356 ASSERT_TRUE(initiator_policy_container);
4357 EXPECT_EQ(network::mojom::ReferrerPolicy::kAlways,
4358 initiator_policy_container->referrer_policy());
Titouan Rigoudy2f995bc2021-02-19 19:39:414359
4360 auto* initiator_policies =
4361 request->GetInitiatorPolicyContainerPolicies();
4362 ASSERT_TRUE(initiator_policies);
Antonio Sartoridb967c52021-01-20 09:54:304363 ASSERT_EQ(network::mojom::ReferrerPolicy::kAlways,
Titouan Rigoudy2f995bc2021-02-19 19:39:414364 initiator_policies->referrer_policy);
Antonio Sartoridb967c52021-01-20 09:54:304365
arthursonzogni5a8f5ad2020-09-28 11:55:324366 loop.Quit();
4367 }));
4368
4369 // Initiate a form submission into the first window and delete the initiator.
4370 EXPECT_TRUE(WaitForLoadStop(openee_shell->web_contents()));
4371 ExecuteScriptAsync(initiator_rfh, R"(
4372 let input = document.createElement("input");
4373 input.setAttribute("type", "hidden");
4374 input.setAttribute("name", "my_token");
4375 input.setAttribute("value", "my_value");
4376
4377 // Schedule a form submission navigation (which will occur in a separate
4378 // task).
4379 let form = document.createElement('form');
4380 form.appendChild(input);
4381 form.setAttribute("method", "POST");
Antonio Sartoridb967c52021-01-20 09:54:304382 form.setAttribute("action", "about:blank");
arthursonzogni5a8f5ad2020-09-28 11:55:324383 form.setAttribute("target", "form-submission-target");
4384 document.body.appendChild(form);
4385 form.submit();
4386
4387 // Delete this frame before the scheduled navigation occurs in the target
4388 // frame.
4389 parent.document.querySelector("iframe").remove();
4390 )");
4391 loop.Run();
4392}
4393
Antonio Sartoricdf3d5b42021-02-17 10:23:104394// Same as the previous test, but for a remote frame navigation:
4395// A document initiates a form submission in a cross-origin frame, then deletes
4396// itself. Check the initiator frame token.
4397IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
4398 FormSubmissionInRemoteFrameThenDeleteFrame) {
4399 GURL url(embedded_test_server()->GetURL("/empty.html"));
4400 GURL cross_origin_always_referrer_url(embedded_test_server()->GetURL(
4401 "foo.com", "/set-header?Referrer-Policy: unsafe-url"));
4402
4403 // Setup the main page.
4404 EXPECT_TRUE(NavigateToURL(shell(), url));
4405
4406 // Create a cross origin child iframe. This iframe will embed another iframe,
4407 // which will initiate the navigation. The only purpose of this iframe is to
4408 // allow its child to delete itself by issuing
4409 // parent.document.querySelector("iframe").remove();
4410 // (The main frame cannot do it because it is cross-origin.)
4411 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
4412 EXPECT_TRUE(ExecJs(shell(), JsReplace(R"(
4413 let iframe = document.createElement('iframe');
4414 iframe.src = $1;
4415 document.body.appendChild(iframe);
4416 )",
4417 cross_origin_always_referrer_url)));
4418 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
4419
4420 RenderFrameHostImpl* middle_rfh =
4421 current_frame_host()->child_at(0)->current_frame_host();
4422
4423 // Now create a grandchild iframe, which is same-origin with the parent (but
4424 // cross-origin with the grandparent). The grandchild will initiate a form
4425 // submission in the top frame and remove itself before the scheduled form
4426 // navigation occurs. This iframe will have referrer policy "unsafe-url".
4427 EXPECT_TRUE(ExecJs(middle_rfh, JsReplace(R"(
4428 let iframe = document.createElement('iframe');
4429 iframe.src = $1;
4430 document.body.appendChild(iframe);
4431 )",
4432 cross_origin_always_referrer_url)));
4433 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
4434
4435 RenderFrameHost* initiator_rfh =
4436 middle_rfh->child_at(0)->current_frame_host();
Dave Tapuskae9b7c0f72023-11-06 16:38:014437 auto initiator_global_token = initiator_rfh->GetGlobalFrameToken();
Antonio Sartoricdf3d5b42021-02-17 10:23:104438
4439 base::RunLoop loop;
4440 DidStartNavigationCallback callback(
4441 shell()->web_contents(),
4442 base::BindLambdaForTesting([&](NavigationHandle* handle) {
4443 auto* request = NavigationRequest::From(handle);
4444 ASSERT_TRUE(request->IsPost());
4445
Arthur Sonzognic686e8f2024-01-11 08:36:374446 const std::optional<blink::LocalFrameToken>& frame_token =
Antonio Sartoricdf3d5b42021-02-17 10:23:104447 request->GetInitiatorFrameToken();
4448 EXPECT_TRUE(frame_token.has_value());
Dave Tapuskae9b7c0f72023-11-06 16:38:014449 EXPECT_EQ(initiator_global_token.frame_token, frame_token.value());
4450 EXPECT_EQ(initiator_global_token.child_id,
4451 request->GetInitiatorProcessId());
Antonio Sartoricdf3d5b42021-02-17 10:23:104452
Sharon Yangd7b53f92024-02-16 18:28:504453 auto* deleted_initiator_rfh = RenderFrameHostImpl::FromFrameToken(
Liam Brady767b85e2023-07-05 16:59:594454 request->GetInitiatorProcessId(), frame_token.value());
Sharon Yangd7b53f92024-02-16 18:28:504455 ASSERT_FALSE(deleted_initiator_rfh);
Antonio Sartoricdf3d5b42021-02-17 10:23:104456
4457 // Even if the initiator RenderFrameHost is gone, its policy container
4458 // should still be around since the LocalFrame has not been destroyed
4459 // yet.
Sharon Yang417a5df2024-04-23 17:57:154460 PolicyContainerHost* initiator_policy_container =
4461 RenderFrameHostImpl::GetPolicyContainerHost(
4462 base::OptionalToPtr(frame_token),
4463 request->GetInitiatorProcessId(),
4464 web_contents()->GetPrimaryMainFrame()->GetStoragePartition());
Antonio Sartoricdf3d5b42021-02-17 10:23:104465 ASSERT_TRUE(initiator_policy_container);
4466 EXPECT_EQ(network::mojom::ReferrerPolicy::kAlways,
4467 initiator_policy_container->referrer_policy());
Titouan Rigoudy2f995bc2021-02-19 19:39:414468 EXPECT_EQ(
4469 network::mojom::ReferrerPolicy::kAlways,
4470 request->GetInitiatorPolicyContainerPolicies()->referrer_policy);
Antonio Sartoricdf3d5b42021-02-17 10:23:104471
4472 loop.Quit();
4473 }));
4474
4475 // Initiate a form submission into the main frame and delete the initiator.
4476 ExecuteScriptAsync(initiator_rfh, R"(
4477 let input = document.createElement("input");
4478 input.setAttribute("type", "hidden");
4479 input.setAttribute("name", "my_token");
4480 input.setAttribute("value", "my_value");
4481
4482 // Schedule a form submission navigation (which will occur in a separate
4483 // task).
4484 let form = document.createElement('form');
4485 form.appendChild(input);
4486 form.setAttribute("method", "POST");
4487 form.setAttribute("action", "about:blank");
4488 form.setAttribute("target", "_top");
4489 document.body.appendChild(form);
4490 form.submit();
4491
4492 // Delete this frame before the scheduled navigation occurs in the main
4493 // frame.
4494 parent.document.querySelector("iframe").remove();
4495 )");
4496 loop.Run();
4497}
4498
Sharon Yang9d7d5772024-02-16 20:08:414499// A class to intercept RemoteFrameHost IPCs, specifically OpenURL. When an
4500// OpenURL IPC is received, this interceptor closes the initiator's Shell,
4501// `shell_to_close`, and ensures the corresponding process exits before
4502// proceeding with the OpenURL call.
4503class InitiatorClosingOpenURLInterceptor
4504 : public blink::mojom::RemoteFrameHostInterceptorForTesting {
4505 public:
4506 // `this` takes ownership of `shell_to_close` and eventually deletes it.
4507 InitiatorClosingOpenURLInterceptor(content::RenderFrameProxyHost* proxy_host,
4508 std::unique_ptr<Shell> shell_to_close,
4509 RenderProcessHost* renderer_to_exit)
Daniel Chengf693d882024-05-07 16:48:374510 : shell_to_close_(std::move(shell_to_close)),
Sharon Yang9d7d5772024-02-16 20:08:414511 renderer_to_exit_(renderer_to_exit),
Daniel Cheng304b8f52024-05-07 16:48:114512 swapped_impl_(std::make_unique<mojo::test::ScopedSwapImplForTesting<
4513 blink::mojom::RemoteFrameHost>>(
Daniel Chengf693d882024-05-07 16:48:374514 proxy_host->frame_host_receiver_for_testing(),
Daniel Cheng304b8f52024-05-07 16:48:114515 this)) {}
Sharon Yang9d7d5772024-02-16 20:08:414516 ~InitiatorClosingOpenURLInterceptor() override = default;
4517
4518 blink::mojom::RemoteFrameHost* GetForwardingInterface() override {
Daniel Chengf693d882024-05-07 16:48:374519 return swapped_impl_->old_impl();
Sharon Yang9d7d5772024-02-16 20:08:414520 }
4521
4522 // This closes `shell_to_close_` and causes `renderer_to_exit_` to exit
4523 // before forwarding the call to the RenderFrameProxyHost. This mimics the
4524 // case where the frame that sent the OpenURL gets closed before the IPC
4525 // reaches its destination. Once the OpenURL IPC is sent, the proxy should
4526 // receive it, even if the sender is gone.
4527 void OpenURL(blink::mojom::OpenURLParamsPtr params) override {
4528 // `Close()` internally deletes the pointer, so it must be released so
4529 // `shell_to_close_` doesn't point to a deleted value.
4530 shell_to_close_.release()->Close();
4531 renderer_to_exit_->Shutdown(content::RESULT_CODE_KILLED);
4532
4533 GetForwardingInterface()->OpenURL(std::move(params));
Sharon Yang417a5df2024-04-23 17:57:154534
4535 // Delete the swapped impl while the real RenderFrameProxyHost still exists,
4536 // since we only need to intercept a single OpenURL call. The next task may
4537 // delete the real impl.
4538 swapped_impl_.reset();
4539
4540 // Clear the other raw_ptrs to avoid dangling pointers.
4541 renderer_to_exit_ = nullptr;
Sharon Yang9d7d5772024-02-16 20:08:414542 }
4543
4544 private:
Sharon Yang9d7d5772024-02-16 20:08:414545 std::unique_ptr<Shell> shell_to_close_;
4546 raw_ptr<RenderProcessHost> renderer_to_exit_;
Sharon Yang417a5df2024-04-23 17:57:154547
4548 // The `swapped_impl_` is a unique_ptr, so the member can be deleted before
Daniel Chengf693d882024-05-07 16:48:374549 // `this` gets destroyed. The original implementation would normally be
4550 // swapped back in when `this` is destroyed. However, in this test, the
4551 // `RenderFrameProxyHost` is deleted shortly after the OpenURL IPC is handled,
4552 // and relying on normal scoper cleanup would cause a use-after-free. To avoid
4553 // this, we early delete the `swapped_impl_` to swap back the original
4554 // implementation as soon as `OpenURL()` has been processed.
Daniel Cheng304b8f52024-05-07 16:48:114555 std::unique_ptr<
4556 mojo::test::ScopedSwapImplForTesting<blink::mojom::RemoteFrameHost>>
Sharon Yang9d7d5772024-02-16 20:08:414557 swapped_impl_;
4558};
4559
4560// Test the case that once an OpenURL IPC is sent, it is received and the
4561// navigation occurs even if the sender is deleted while the IPC is in flight.
4562// This test opens a main frame, which opens a cross-site popup. The test then
4563// does a form submission to the popup and closes the main frame.
4564// Unlike FormSubmissionInRemoteFrameThenDeleteFrame, the initiator is the last
4565// (and only) frame of that SiteInstance. Deleting it usually causes proxies in
4566// the same SiteInstanceGroup to be deleted, meaning the OpenURL IPC may never
4567// be received.
Hayato Ito66773032024-04-24 04:31:334568//
Harkiran Bolaria60d62522024-04-24 10:46:284569// Fails on linux-bfcache-rel and android-bfcache-rel. See crbug.com/336671248.
4570#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID)
Hayato Ito66773032024-04-24 04:31:334571#define MAYBE_FormSubmissionInRemoteFrameSenderDeletedBeforeReceivingOpenURL \
4572 DISABLED_FormSubmissionInRemoteFrameSenderDeletedBeforeReceivingOpenURL
4573#else
4574#define MAYBE_FormSubmissionInRemoteFrameSenderDeletedBeforeReceivingOpenURL \
4575 FormSubmissionInRemoteFrameSenderDeletedBeforeReceivingOpenURL
4576#endif
Sharon Yang9d7d5772024-02-16 20:08:414577IN_PROC_BROWSER_TEST_F(
4578 NavigationBrowserTest,
Hayato Ito66773032024-04-24 04:31:334579 MAYBE_FormSubmissionInRemoteFrameSenderDeletedBeforeReceivingOpenURL) {
Alex Attar95a682f2025-06-03 13:44:434580 // TODO(crbug.com/420851638): Skipping this test if the
4581 // TrackEmptyRendererProcessesForReuse feature is enabled while investigating
4582 // the issue between this test and the feature causing this test to fail.
4583 if (base::FeatureList::IsEnabled(
4584 features::kTrackEmptyRendererProcessesForReuse)) {
4585 return;
4586 }
Sharon Yang9d7d5772024-02-16 20:08:414587 // We crash a renderer in the OpenURL interceptor.
4588 content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
Sharon Yang417a5df2024-04-23 17:57:154589 content::IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
Sharon Yang9d7d5772024-02-16 20:08:414590
4591 // Get a unique_ptr to the shell, which will be used to transfer ownership
4592 // later on.
4593 std::unique_ptr<Shell> shell_a = base::WrapUnique(CreateBrowser());
4594
4595 // Setup the main page, a.com. The referrer policy is needed to test the
4596 // keep alive part of PolicyContainerHost.
4597 GURL always_referrer_url_a(embedded_test_server()->GetURL(
4598 "a.com", "/set-header?Referrer-Policy: unsafe-url"));
4599 EXPECT_TRUE(NavigateToURL(shell_a.get(), always_referrer_url_a));
4600 EXPECT_TRUE(WaitForLoadStop(shell_a->web_contents()));
4601
4602 // The a.com's RenderFrameHost will be the initiator of the form submission.
4603 RenderFrameHostImpl* rfh_a = static_cast<RenderFrameHostImpl*>(
4604 shell_a->web_contents()->GetPrimaryMainFrame());
4605
4606 // Create a cross origin popup that will be the target of the form submission.
4607 // This is cross-site so we can test the case where the last RenderFrameHost
4608 // for the initiator's site is gone when the initiator deletes itself, causing
4609 // all proxies in its SiteInstanceGroup to potentially delete themselves
4610 // before the OpenURL call can be received.
4611 // However, we also need to be able to navigate the target frame, so this is
4612 // opened as a popup.
4613 GURL url_b(embedded_test_server()->GetURL("b.com", "/empty.html"));
4614 WebContentsImpl* web_contents_b = nullptr;
4615 {
4616 WebContentsAddedObserver observer_b;
4617 ASSERT_TRUE(ExecJs(rfh_a, JsReplace("window.open($1, '_bpopup')", url_b)));
4618 web_contents_b = static_cast<WebContentsImpl*>(observer_b.GetWebContents());
4619 }
4620 WaitForLoadStop(web_contents_b);
4621
4622 base::RunLoop loop;
4623 auto initiator_global_token = rfh_a->GetGlobalFrameToken();
4624
4625 // Register a callback to make sure the script below triggers a
4626 // DidStartNavigation event to fire. This indicates that the popup main
4627 // frame's proxy in A's SiteInstanceGroup received and ran the OpenURL call.
4628 DidStartNavigationCallback callback(
4629 web_contents_b, base::BindLambdaForTesting([&](NavigationHandle* handle) {
4630 auto* request = NavigationRequest::From(handle);
4631 ASSERT_TRUE(request->IsPost());
4632
4633 const std::optional<blink::LocalFrameToken>& frame_token =
4634 request->GetInitiatorFrameToken();
4635 EXPECT_TRUE(frame_token.has_value());
4636 EXPECT_EQ(initiator_global_token.frame_token, frame_token.value());
4637 EXPECT_EQ(initiator_global_token.child_id,
4638 request->GetInitiatorProcessId());
4639
4640 // This is the RenderFrameHost in the WebContents that was forced to
4641 // `Close()` in the interceptor, so it should be deleted.
4642 auto* initiator_rfh = RenderFrameHostImpl::FromFrameToken(
4643 request->GetInitiatorProcessId(), frame_token.value());
4644 EXPECT_FALSE(initiator_rfh);
4645
4646 // Even if the initiator RenderFrameHost is gone, its
4647 // PolicyContainerHost should still be around since the LocalFrame has
4648 // not been destroyed yet.
Sharon Yang417a5df2024-04-23 17:57:154649 PolicyContainerHost* initiator_policy_container =
4650 RenderFrameHostImpl::GetPolicyContainerHost(
4651 base::OptionalToPtr(frame_token),
4652 request->GetInitiatorProcessId(),
4653 web_contents()->GetPrimaryMainFrame()->GetStoragePartition());
Sharon Yang9d7d5772024-02-16 20:08:414654 ASSERT_TRUE(initiator_policy_container);
4655 EXPECT_EQ(network::mojom::ReferrerPolicy::kAlways,
4656 initiator_policy_container->referrer_policy());
4657 EXPECT_EQ(
4658 network::mojom::ReferrerPolicy::kAlways,
4659 request->GetInitiatorPolicyContainerPolicies()->referrer_policy);
4660
4661 loop.Quit();
4662 }));
4663
4664 // Intercept the OpenURL call to the proxy for the popup in A's
4665 // SiteInstanceGroup, which will happen in a separate navigation task posted
4666 // from the form submission task in the script below.
4667 // Ownership of `shell_a` is being transferred to the interceptor. The
4668 // interceptor will delete `shell_a` so it should not be used after this.
4669 SiteInstanceGroup* a_sig = rfh_a->GetSiteInstance()->group();
4670 auto proxy_host_interceptor =
4671 std::make_unique<InitiatorClosingOpenURLInterceptor>(
4672 web_contents_b->GetPrimaryMainFrame()
4673 ->browsing_context_state()
4674 ->GetRenderFrameProxyHost(a_sig),
4675 std::move(shell_a), a_sig->process());
4676
4677 // Initiate a form submission into the b.com popup that will navigate the
4678 // popup to about:blank.
4679 // We want the initiator to be closed between the time the about:blank OpenURL
4680 // IPC is sent and received. This is done in the interceptor by closing the
4681 // shell the initiator belongs to. The timing means window.close() is not a
4682 // viable option: it would post a task after the OpenURL navigation task, so
4683 // we can't ensure the window is closed before OpenURL runs.
4684 ExecuteScriptAsync(rfh_a, R"(
4685 let input = document.createElement("input");
4686 input.setAttribute("type", "hidden");
4687 input.setAttribute("name", "my_token");
4688 input.setAttribute("value", "my_value");
4689
4690 // Schedule a form submission navigation (which will occur in a separate
4691 // task).
4692 let form = document.createElement('form');
4693 form.appendChild(input);
4694 form.setAttribute("method", "POST");
4695 form.setAttribute("action", "about:blank");
4696 form.setAttribute("target", "_bpopup");
4697 document.body.appendChild(form);
4698 form.submit();
4699 )");
4700 loop.Run();
4701
4702 // Make sure the about:blank navigation finishes successfully.
4703 WaitForLoadStop(web_contents_b);
4704 EXPECT_EQ(GURL("about:blank"), web_contents_b->GetLastCommittedURL());
4705}
4706
Alex Moshchukb7f08212024-09-11 22:57:354707// Check that when RenderProcessHostImpl::DisableRefCounts is called while a
4708// NavigationStateKeepAlive exists, the navigation still succeeds. This is a
4709// regression test for crbug.com/348150830.
4710IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
4711 DisableRefCountsWhileKeepAliveExists) {
4712 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
4713 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4714
4715 // This test needs the browser process to call DisableRefCounts after the form
4716 // submission's NavigationStateKeepAlive is created and before the task that
4717 // sends the BeginNavigation IPC. To do this, use EvalJS to return a string to
4718 // the test framework between those two renderer-side tasks, allowing the
4719 // browser process to reset the counts before the BeginNavigation IPC is
4720 // received and the NavigationStateKeepAlive is destroyed.
4721 std::string expected_str("Placeholder value");
4722 std::string js_str = base::StringPrintf(
4723 "f = document.createElement('form');"
4724 "f.action = 'about:blank';"
4725 "document.body.appendChild(f);"
4726 "f.submit();"
4727 "'%s';",
4728 expected_str.c_str());
4729
4730 TestNavigationObserver observer(shell()->web_contents());
4731 EXPECT_EQ(expected_str, EvalJs(shell(), js_str).ExtractString());
4732
4733 // Expect at this point that a NavigationStateKeepAlive has been created for
4734 // the form submission.
4735 NavigationStateKeepAlive* keep_alive =
4736 current_frame_host()->GetStoragePartition()->GetNavigationStateKeepAlive(
4737 current_frame_host()->GetFrameToken());
4738 ASSERT_TRUE(keep_alive);
4739
4740 // Disable ref counts on the process, which resets all ref counts to 0. This
4741 // seems to happen in practice in https://crbug.com/348150830 when a
4742 // BrowserContext is closed before all of its frames are properly cleaned up,
4743 // but the exact repro steps for this aren't known, so simulate this behavior
4744 // with an explicit DisableRefCounts() call.
4745 current_frame_host()->GetProcess()->DisableRefCounts();
4746
4747 // Wait for the navigation to complete. At that point, the
4748 // NavigationStateKeepAlive goes away, which can possibly decrement the
4749 // associated ref count. Since DisableRefCounts() was called, the ref count
4750 // should not be further decremented, and the navigation should complete
4751 // successfully.
4752 observer.Wait();
4753 EXPECT_TRUE(observer.last_navigation_succeeded());
4754 EXPECT_TRUE(current_frame_host()->GetLastCommittedURL().IsAboutBlank());
4755}
4756
Daniel Chengf762a4c52020-09-29 05:50:454757using MediaNavigationBrowserTest = NavigationBaseBrowserTest;
4758
4759// Media navigations synchronously complete the time of the `CommitNavigation`
4760// IPC call. Ensure that the renderer does not crash if the media navigation
4761// results in an HTTP error with no body, since the renderer will reentrantly
4762// commit an error page while handling the `CommitNavigation` IPC.
4763IN_PROC_BROWSER_TEST_F(MediaNavigationBrowserTest, FailedNavigation) {
4764 embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
4765 [](const net::test_server::HttpRequest& request)
4766 -> std::unique_ptr<net::test_server::HttpResponse> {
4767 auto response = std::make_unique<net::test_server::BasicHttpResponse>();
4768 response->set_code(net::HTTP_NOT_FOUND);
4769 response->set_content_type("video/mp4");
4770 return response;
4771 }));
4772 ASSERT_TRUE(embedded_test_server()->Start());
4773
4774 const GURL error_url(embedded_test_server()->GetURL("/moo.mp4"));
4775 EXPECT_FALSE(NavigateToURL(shell(), error_url));
arthursonzognia26fa9a2020-11-03 10:16:474776 EXPECT_EQ(error_url, current_frame_host()->GetLastCommittedURL());
Daniel Chengf762a4c52020-09-29 05:50:454777 NavigationEntry* entry =
arthursonzognia26fa9a2020-11-03 10:16:474778 web_contents()->GetController().GetLastCommittedEntry();
Daniel Chengf762a4c52020-09-29 05:50:454779 EXPECT_EQ(PAGE_TYPE_ERROR, entry->GetPageType());
4780}
4781
Jeremy Roman1e4b31f2023-11-14 20:56:474782using DocumentPolicyBrowserTest = NavigationBaseBrowserTest;
Nick Burris3c290e02020-03-10 01:08:024783
4784// Test that scroll restoration can be disabled with
4785// Document-Policy: force-load-at-top
4786IN_PROC_BROWSER_TEST_F(DocumentPolicyBrowserTest,
4787 ScrollRestorationDisabledByDocumentPolicy) {
4788 net::test_server::ControllableHttpResponse response(embedded_test_server(),
4789 "/target.html");
4790 ASSERT_TRUE(embedded_test_server()->Start());
4791 GURL url(embedded_test_server()->GetURL("/target.html"));
arthursonzognia26fa9a2020-11-03 10:16:474792 TestNavigationManager navigation_manager(web_contents(), url);
Rakina Zata Amni348fe2b2021-07-09 05:28:524793 // This test expects the document is freshly loaded on the back navigation
4794 // so that the document policy to force-load-at-top will run. This will not
4795 // happen if the document is back-forward cached, so we need to disable it.
4796 DisableBackForwardCacheForTesting(web_contents(),
Rakina Zata Amni30af7062022-01-19 23:46:364797 BackForwardCache::TEST_REQUIRES_NO_CACHING);
Nick Burris3c290e02020-03-10 01:08:024798
4799 // Load the document with document policy force-load-at-top
4800 shell()->LoadURL(url);
4801 EXPECT_TRUE(navigation_manager.WaitForRequestStart());
4802 navigation_manager.ResumeNavigation();
4803 response.WaitForRequest();
4804 response.Send(
4805 "HTTP/1.1 200 OK\r\n"
4806 "Content-Type: text/html; charset=utf-8\r\n"
4807 "Document-Policy: force-load-at-top\r\n"
4808 "\r\n"
4809 "<p style='position: absolute; top: 10000px;'>Some text</p>");
4810 response.Done();
4811
4812 EXPECT_TRUE(navigation_manager.WaitForResponse());
4813 navigation_manager.ResumeNavigation();
Fergal Daly83bc3cd2023-01-18 00:22:544814 ASSERT_TRUE(navigation_manager.WaitForNavigationFinished());
arthursonzognia26fa9a2020-11-03 10:16:474815 EXPECT_TRUE(WaitForLoadStop(web_contents()));
4816 EXPECT_TRUE(WaitForRenderFrameReady(current_frame_host()));
Nick Burris3c290e02020-03-10 01:08:024817
Rakina Zata Amni364eb5d2023-03-22 13:19:004818 {
4819 RenderFrameSubmissionObserver frame_observer(web_contents());
4820 // Scroll down the page a bit
4821 EXPECT_TRUE(ExecJs(web_contents(), "window.scrollTo(0, 1000)"));
4822 frame_observer.WaitForScrollOffsetAtTop(false);
4823 }
Nick Burris3c290e02020-03-10 01:08:024824
4825 // Navigate away
Avi Drissmanc91bd8e2021-04-19 23:58:444826 EXPECT_TRUE(ExecJs(web_contents(), "window.location = 'about:blank'"));
arthursonzognia26fa9a2020-11-03 10:16:474827 EXPECT_TRUE(WaitForLoadStop(web_contents()));
4828 EXPECT_TRUE(WaitForRenderFrameReady(current_frame_host()));
Nick Burris3c290e02020-03-10 01:08:024829
4830 // Navigate back
Avi Drissmanc91bd8e2021-04-19 23:58:444831 EXPECT_TRUE(ExecJs(web_contents(), "history.back()"));
arthursonzognia26fa9a2020-11-03 10:16:474832 EXPECT_TRUE(WaitForLoadStop(web_contents()));
4833 EXPECT_TRUE(WaitForRenderFrameReady(current_frame_host()));
Nick Burris3c290e02020-03-10 01:08:024834
4835 // Wait a short amount of time to ensure the page does not scroll.
4836 base::RunLoop run_loop;
Sean Maher5b9af51f2022-11-21 15:32:474837 base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
Nick Burris3c290e02020-03-10 01:08:024838 FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
4839 run_loop.Run();
4840 RunUntilInputProcessed(RenderWidgetHostImpl::From(
arthursonzognia26fa9a2020-11-03 10:16:474841 web_contents()->GetRenderViewHost()->GetWidget()));
danakj156a9bb542020-09-03 19:21:504842 const cc::RenderFrameMetadata& last_metadata =
arthursonzognia26fa9a2020-11-03 10:16:474843 RenderFrameSubmissionObserver(web_contents()).LastRenderFrameMetadata();
danakj156a9bb542020-09-03 19:21:504844 EXPECT_TRUE(last_metadata.is_scroll_offset_at_top);
Nick Burris3c290e02020-03-10 01:08:024845}
4846
4847// Test that scroll restoration works as expected with
David Bokan84c965362020-08-14 17:25:324848// Document-Policy: force-load-at-top=?0
Nick Burris3c290e02020-03-10 01:08:024849IN_PROC_BROWSER_TEST_F(DocumentPolicyBrowserTest,
4850 ScrollRestorationEnabledByDocumentPolicy) {
4851 net::test_server::ControllableHttpResponse response(embedded_test_server(),
4852 "/target.html");
4853 ASSERT_TRUE(embedded_test_server()->Start());
4854 GURL url(embedded_test_server()->GetURL("/target.html"));
arthursonzognia26fa9a2020-11-03 10:16:474855 RenderFrameSubmissionObserver frame_observer(web_contents());
4856 TestNavigationManager navigation_manager(web_contents(), url);
Nick Burris3c290e02020-03-10 01:08:024857
David Bokan84c965362020-08-14 17:25:324858 // Load the document with document policy force-load-at-top set to false.
Nick Burris3c290e02020-03-10 01:08:024859 shell()->LoadURL(url);
4860 EXPECT_TRUE(navigation_manager.WaitForRequestStart());
4861 navigation_manager.ResumeNavigation();
4862 response.WaitForRequest();
4863 response.Send(
4864 "HTTP/1.1 200 OK\r\n"
4865 "Content-Type: text/html; charset=utf-8\r\n"
David Bokan84c965362020-08-14 17:25:324866 "Document-Policy: force-load-at-top=?0\r\n"
Nick Burris3c290e02020-03-10 01:08:024867 "\r\n"
4868 "<p style='position: absolute; top: 10000px;'>Some text</p>");
4869 response.Done();
4870
4871 EXPECT_TRUE(navigation_manager.WaitForResponse());
4872 navigation_manager.ResumeNavigation();
Fergal Daly83bc3cd2023-01-18 00:22:544873 ASSERT_TRUE(navigation_manager.WaitForNavigationFinished());
arthursonzognia26fa9a2020-11-03 10:16:474874 EXPECT_TRUE(WaitForLoadStop(web_contents()));
4875 EXPECT_TRUE(WaitForRenderFrameReady(current_frame_host()));
Nick Burris3c290e02020-03-10 01:08:024876
4877 // Scroll down the page a bit
Avi Drissmanc91bd8e2021-04-19 23:58:444878 EXPECT_TRUE(ExecJs(web_contents(), "window.scrollTo(0, 1000)"));
Nick Burris3c290e02020-03-10 01:08:024879 frame_observer.WaitForScrollOffsetAtTop(false);
4880
4881 // Navigate away
Avi Drissmanc91bd8e2021-04-19 23:58:444882 EXPECT_TRUE(ExecJs(web_contents(), "window.location = 'about:blank'"));
arthursonzognia26fa9a2020-11-03 10:16:474883 EXPECT_TRUE(WaitForLoadStop(web_contents()));
4884 EXPECT_TRUE(WaitForRenderFrameReady(current_frame_host()));
Nick Burris3c290e02020-03-10 01:08:024885
4886 // Navigate back
Avi Drissmanc91bd8e2021-04-19 23:58:444887 EXPECT_TRUE(ExecJs(web_contents(), "history.back()"));
arthursonzognia26fa9a2020-11-03 10:16:474888 EXPECT_TRUE(WaitForLoadStop(web_contents()));
4889 EXPECT_TRUE(WaitForRenderFrameReady(current_frame_host()));
Nick Burris3c290e02020-03-10 01:08:024890
4891 // Ensure scroll restoration activated
4892 frame_observer.WaitForScrollOffsetAtTop(false);
danakj156a9bb542020-09-03 19:21:504893 const cc::RenderFrameMetadata& last_metadata =
arthursonzognia26fa9a2020-11-03 10:16:474894 RenderFrameSubmissionObserver(web_contents()).LastRenderFrameMetadata();
danakj156a9bb542020-09-03 19:21:504895 EXPECT_FALSE(last_metadata.is_scroll_offset_at_top);
Nick Burris3c290e02020-03-10 01:08:024896}
4897
4898// Test that element fragment anchor scrolling can be disabled with
4899// Document-Policy: force-load-at-top
4900IN_PROC_BROWSER_TEST_F(DocumentPolicyBrowserTest,
4901 FragmentAnchorDisabledByDocumentPolicy) {
4902 net::test_server::ControllableHttpResponse response(embedded_test_server(),
4903 "/target.html");
4904
4905 ASSERT_TRUE(embedded_test_server()->Start());
4906 GURL url(embedded_test_server()->GetURL("/target.html#text"));
Nick Burris3c290e02020-03-10 01:08:024907
4908 // Load the target document
arthursonzognia26fa9a2020-11-03 10:16:474909 TestNavigationManager navigation_manager(web_contents(), url);
Nick Burris3c290e02020-03-10 01:08:024910 shell()->LoadURL(url);
4911
4912 // Start navigation
4913 EXPECT_TRUE(navigation_manager.WaitForRequestStart());
4914 navigation_manager.ResumeNavigation();
4915
4916 // Send Document-Policy header
4917 response.WaitForRequest();
4918 response.Send(
4919 "HTTP/1.1 200 OK\r\n"
4920 "Content-Type: text/html; charset=utf-8\r\n"
4921 "Document-Policy: force-load-at-top\r\n"
4922 "\r\n"
4923 "<p id='text' style='position: absolute; top: 10000px;'>Some text</p>");
4924 response.Done();
4925
4926 EXPECT_TRUE(navigation_manager.WaitForResponse());
4927 navigation_manager.ResumeNavigation();
Fergal Daly83bc3cd2023-01-18 00:22:544928 ASSERT_TRUE(navigation_manager.WaitForNavigationFinished());
Nick Burris3c290e02020-03-10 01:08:024929
arthursonzognia26fa9a2020-11-03 10:16:474930 EXPECT_TRUE(WaitForLoadStop(web_contents()));
4931 EXPECT_TRUE(WaitForRenderFrameReady(current_frame_host()));
Nick Burris3c290e02020-03-10 01:08:024932 // Wait a short amount of time to ensure the page does not scroll.
4933 base::RunLoop run_loop;
Sean Maher5b9af51f2022-11-21 15:32:474934 base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
Nick Burris3c290e02020-03-10 01:08:024935 FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
4936 run_loop.Run();
4937 RunUntilInputProcessed(RenderWidgetHostImpl::From(
arthursonzognia26fa9a2020-11-03 10:16:474938 web_contents()->GetRenderViewHost()->GetWidget()));
danakj156a9bb542020-09-03 19:21:504939 const cc::RenderFrameMetadata& last_metadata =
arthursonzognia26fa9a2020-11-03 10:16:474940 RenderFrameSubmissionObserver(web_contents()).LastRenderFrameMetadata();
danakj156a9bb542020-09-03 19:21:504941 EXPECT_TRUE(last_metadata.is_scroll_offset_at_top);
Nick Burris3c290e02020-03-10 01:08:024942}
4943
4944// Test that element fragment anchor scrolling works as expected with
David Bokan84c965362020-08-14 17:25:324945// Document-Policy: force-load-at-top=?0
Nick Burris3c290e02020-03-10 01:08:024946IN_PROC_BROWSER_TEST_F(DocumentPolicyBrowserTest,
4947 FragmentAnchorEnabledByDocumentPolicy) {
4948 net::test_server::ControllableHttpResponse response(embedded_test_server(),
4949 "/target.html");
4950
4951 ASSERT_TRUE(embedded_test_server()->Start());
4952 GURL url(embedded_test_server()->GetURL("/target.html#text"));
arthursonzognia26fa9a2020-11-03 10:16:474953 RenderFrameSubmissionObserver frame_observer(web_contents());
Nick Burris3c290e02020-03-10 01:08:024954
4955 // Load the target document
arthursonzognia26fa9a2020-11-03 10:16:474956 TestNavigationManager navigation_manager(web_contents(), url);
Nick Burris3c290e02020-03-10 01:08:024957 shell()->LoadURL(url);
4958
4959 // Start navigation
4960 EXPECT_TRUE(navigation_manager.WaitForRequestStart());
4961 navigation_manager.ResumeNavigation();
4962
4963 // Send Document-Policy header
4964 response.WaitForRequest();
4965 response.Send(
4966 "HTTP/1.1 200 OK\r\n"
4967 "Content-Type: text/html; charset=utf-8\r\n"
David Bokan84c965362020-08-14 17:25:324968 "Document-Policy: force-load-at-top=?0\r\n"
Nick Burris3c290e02020-03-10 01:08:024969 "\r\n"
4970 "<p id='text' style='position: absolute; top: 10000px;'>Some text</p>");
4971 response.Done();
4972
4973 EXPECT_TRUE(navigation_manager.WaitForResponse());
4974 navigation_manager.ResumeNavigation();
Fergal Daly83bc3cd2023-01-18 00:22:544975 ASSERT_TRUE(navigation_manager.WaitForNavigationFinished());
Nick Burris3c290e02020-03-10 01:08:024976
arthursonzognia26fa9a2020-11-03 10:16:474977 EXPECT_TRUE(WaitForLoadStop(web_contents()));
4978 EXPECT_TRUE(WaitForRenderFrameReady(current_frame_host()));
Nick Burris3c290e02020-03-10 01:08:024979 frame_observer.WaitForScrollOffsetAtTop(
4980 /*expected_scroll_offset_at_top=*/false);
danakj156a9bb542020-09-03 19:21:504981 const cc::RenderFrameMetadata& last_metadata =
arthursonzognia26fa9a2020-11-03 10:16:474982 RenderFrameSubmissionObserver(web_contents()).LastRenderFrameMetadata();
danakj156a9bb542020-09-03 19:21:504983 EXPECT_FALSE(last_metadata.is_scroll_offset_at_top);
Nick Burris3c290e02020-03-10 01:08:024984}
4985
arthursonzogni052a1f252020-11-04 16:35:464986IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, OriginToCommitBasic) {
4987 GURL url = embedded_test_server()->GetURL("a.com", "/empty.html");
4988 auto origin_expected = url::Origin::Create(url);
4989 TestNavigationManager manager(web_contents(), url);
4990 shell()->LoadURL(url);
4991 EXPECT_TRUE(manager.WaitForResponse());
4992 NavigationRequest* navigation = main_frame()->navigation_request();
Arthur Sonzognic686e8f2024-01-11 08:36:374993 std::optional<url::Origin> origin_to_commit = navigation->GetOriginToCommit();
Lukasz Anforowicz435e68d2022-11-09 21:47:444994 ASSERT_TRUE(origin_to_commit.has_value());
Fergal Daly83bc3cd2023-01-18 00:22:544995 ASSERT_TRUE(manager.WaitForNavigationFinished());
arthursonzogni052a1f252020-11-04 16:35:464996 url::Origin origin_committed = current_frame_host()->GetLastCommittedOrigin();
4997
Lukasz Anforowicz435e68d2022-11-09 21:47:444998 EXPECT_FALSE(origin_to_commit->opaque());
arthursonzogni052a1f252020-11-04 16:35:464999 EXPECT_FALSE(origin_committed.opaque());
Lukasz Anforowicz435e68d2022-11-09 21:47:445000 EXPECT_EQ(origin_expected, *origin_to_commit);
arthursonzogni052a1f252020-11-04 16:35:465001 EXPECT_EQ(origin_expected, origin_committed);
5002}
5003
Lukasz Anforowicz435e68d2022-11-09 21:47:445004IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, OriginToCommit204) {
5005 GURL url = embedded_test_server()->GetURL("a.com", "/nocontent");
5006 TestNavigationManager manager(web_contents(), url);
5007 shell()->LoadURL(url);
5008 EXPECT_TRUE(manager.WaitForResponse());
5009 NavigationRequest* navigation = main_frame()->navigation_request();
Arthur Sonzognic686e8f2024-01-11 08:36:375010 std::optional<url::Origin> origin_to_commit = navigation->GetOriginToCommit();
Lukasz Anforowicz435e68d2022-11-09 21:47:445011 EXPECT_FALSE(origin_to_commit.has_value());
Fergal Daly83bc3cd2023-01-18 00:22:545012 ASSERT_TRUE(manager.WaitForNavigationFinished());
Lukasz Anforowicz435e68d2022-11-09 21:47:445013}
5014
arthursonzogni052a1f252020-11-04 16:35:465015IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
5016 OriginToCommitSandboxFromResponse) {
5017 GURL url = embedded_test_server()->GetURL(
5018 "a.com", "/set-header?Content-Security-Policy: sandbox");
5019 TestNavigationManager manager(web_contents(), url);
5020 shell()->LoadURL(url);
5021 EXPECT_TRUE(manager.WaitForResponse());
5022 NavigationRequest* navigation = main_frame()->navigation_request();
Lukasz Anforowicz435e68d2022-11-09 21:47:445023 url::Origin origin_to_commit = navigation->GetOriginToCommit().value();
Fergal Daly83bc3cd2023-01-18 00:22:545024 ASSERT_TRUE(manager.WaitForNavigationFinished());
arthursonzogni052a1f252020-11-04 16:35:465025 url::Origin origin_committed = current_frame_host()->GetLastCommittedOrigin();
5026
5027 EXPECT_TRUE(origin_to_commit.opaque());
5028 EXPECT_TRUE(origin_committed.opaque());
Alison Gale770f3fc2024-04-27 00:39:585029 // TODO(crbug.com/40092527). The nonce must match.
arthursonzogni052a1f252020-11-04 16:35:465030 EXPECT_NE(origin_to_commit, origin_committed);
5031}
5032
5033IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
5034 OriginToCommitSandboxFromParentDocument) {
5035 GURL url_top = embedded_test_server()->GetURL(
5036 "a.com", "/set-header?Content-Security-Policy: sandbox allow-scripts");
5037 EXPECT_TRUE(NavigateToURL(shell(), url_top));
5038 GURL url_iframe = embedded_test_server()->GetURL("a.com", "/empty.html");
5039 TestNavigationManager manager(web_contents(), url_iframe);
5040 ExecuteScriptAsync(current_frame_host(), R"(
5041 let iframe = document.createElement("iframe");
5042 iframe.src = "./empty.html";
5043 document.body.appendChild(iframe);
5044 )");
5045 EXPECT_TRUE(manager.WaitForResponse());
5046 FrameTreeNode* iframe = current_frame_host()->child_at(0);
5047 NavigationRequest* navigation = iframe->navigation_request();
Lukasz Anforowicz435e68d2022-11-09 21:47:445048 url::Origin origin_to_commit = navigation->GetOriginToCommit().value();
Fergal Daly83bc3cd2023-01-18 00:22:545049 ASSERT_TRUE(manager.WaitForNavigationFinished());
arthursonzogni052a1f252020-11-04 16:35:465050 url::Origin origin_committed =
5051 iframe->current_frame_host()->GetLastCommittedOrigin();
5052
5053 EXPECT_TRUE(origin_to_commit.opaque());
5054 EXPECT_TRUE(origin_committed.opaque());
Alison Gale770f3fc2024-04-27 00:39:585055 // TODO(crbug.com/40092527). The nonce must match.
arthursonzogni052a1f252020-11-04 16:35:465056 EXPECT_NE(origin_to_commit, origin_committed);
5057
5058 // Both document have the same URL. Only the first sets CSP:sandbox, but both
5059 // are sandboxed. They get an opaque origin different from each others.
5060 EXPECT_NE(current_frame_host()->GetLastCommittedOrigin(), origin_committed);
5061}
5062
arthursonzognie4135822020-12-15 10:32:165063// Regression test for https://crbug.com/1158306.
5064// Navigate to a response, which set Content-Security-Policy: sandbox AND block
5065// the response. The error page shouldn't set sandbox flags.
5066IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, ErrorPageFromCspSandboxResponse) {
5067 // Block every navigation in WillProcessResponse.
5068 std::unique_ptr<content::TestNavigationThrottleInserter> blocker =
5069 BlockNavigationWillProcessResponse(web_contents());
5070
5071 // Navigate toward a document witch sets CSP:sandbox.
5072 GURL url = embedded_test_server()->GetURL(
5073 "a.com", "/set-header?Content-Security-Policy: sandbox");
5074 TestNavigationManager manager(web_contents(), url);
5075 shell()->LoadURL(url);
Fergal Daly83bc3cd2023-01-18 00:22:545076 ASSERT_TRUE(manager.WaitForNavigationFinished());
arthursonzognie4135822020-12-15 10:32:165077
5078 // An error page committed. It doesn't have any sandbox flags, despite the
5079 // original response headers.
Miyoung Shina2dd6a42021-10-07 12:19:215080 EXPECT_TRUE(current_frame_host()->IsErrorDocument());
arthursonzognie4135822020-12-15 10:32:165081 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
5082 current_frame_host()->active_sandbox_flags());
5083
5084 EXPECT_EQ(url, current_frame_host()->GetLastCommittedURL());
5085 EXPECT_TRUE(current_frame_host()->GetLastCommittedOrigin().opaque());
5086 EXPECT_TRUE(
5087 current_frame_host()->GetLastCommittedOrigin().CanBeDerivedFrom(url));
5088}
5089
danakj79bd493f2021-02-10 20:20:385090IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
5091 ProcessShutdownDuringDeferredNavigationThrottle) {
5092 GURL url = embedded_test_server()->GetURL("a.com", "/empty.html");
5093 EXPECT_TRUE(NavigateToURL(shell(), url));
5094
5095 class ShutdownThrottle : public TaskRunnerDeferringThrottle,
5096 WebContentsObserver {
5097 public:
Takashi Toyoshimae9c959ce92025-05-20 19:18:385098 ShutdownThrottle(WebContents* web_contents,
5099 NavigationThrottleRegistry& registry)
Sean Maher5b9af51f2022-11-21 15:32:475100 : TaskRunnerDeferringThrottle(
5101 base::SingleThreadTaskRunner::GetCurrentDefault(),
5102 /*defer_start=*/false,
5103 /*defer_redirect=*/false,
5104 /*defer_response=*/true,
Takashi Toyoshimae9c959ce92025-05-20 19:18:385105 registry),
danakj79bd493f2021-02-10 20:20:385106 web_contents_(web_contents) {
5107 WebContentsObserver::Observe(web_contents_);
5108 }
5109
5110 void AsyncResume() override {
5111 // Shutdown the renderer and delay Resume() until then.
Dave Tapuska327c06c92022-06-13 20:31:515112 web_contents_->GetPrimaryMainFrame()->GetProcess()->Shutdown(1);
danakj79bd493f2021-02-10 20:20:385113 }
5114
5115 void RenderFrameDeleted(RenderFrameHost* frame_host) override {
5116 TaskRunnerDeferringThrottle::AsyncResume();
5117 }
5118
5119 private:
Keishi Hattori0e45c022021-11-27 09:25:525120 raw_ptr<WebContents> web_contents_;
danakj79bd493f2021-02-10 20:20:385121 };
5122
5123 auto inserter = std::make_unique<TestNavigationThrottleInserter>(
5124 shell()->web_contents(),
5125 base::BindLambdaForTesting(
Takashi Toyoshimae9c959ce92025-05-20 19:18:385126 [&](NavigationThrottleRegistry& registry) -> void {
5127 registry.AddThrottle(std::make_unique<ShutdownThrottle>(
5128 shell()->web_contents(), registry));
danakj79bd493f2021-02-10 20:20:385129 }));
5130
5131 class DoesNotReadyToCommitObserver : public WebContentsObserver {
5132 public:
5133 explicit DoesNotReadyToCommitObserver(WebContents* contents)
5134 : WebContentsObserver(contents) {}
5135
5136 // WebContentsObserver overrides.
5137 void ReadyToCommitNavigation(NavigationHandle* handle) override {
5138 // This method should not happen. Since the process is destroyed before
5139 // we become ready to commit, we can not ever reach
5140 // ReadyToCommitNavigation. Doing so would fail because the renderer is
5141 // gone.
5142 ADD_FAILURE() << "ReadyToCommitNavigation but renderer has crashed. "
5143 "IsRenderFrameLive: "
5144 << handle->GetRenderFrameHost()->IsRenderFrameLive();
5145 navigation_was_ready_to_commit_ = true;
5146 }
5147
5148 void DidFinishNavigation(NavigationHandle* handle) override {
5149 navigation_finished_ = true;
5150 navigation_committed_ = handle->HasCommitted();
5151 }
5152
5153 bool navigation_was_ready_to_commit() {
5154 return navigation_was_ready_to_commit_;
5155 }
5156 bool navigation_finished() { return navigation_finished_; }
5157 bool navigation_committed() { return navigation_committed_; }
5158
5159 private:
5160 bool navigation_was_ready_to_commit_ = false;
5161 bool navigation_finished_ = false;
5162 bool navigation_committed_ = false;
5163 };
5164
5165 // Watch that ReadyToCommitNavigation() will not happen when the renderer is
5166 // gone.
5167 DoesNotReadyToCommitObserver no_commit_obs(shell()->web_contents());
5168
5169 // We will shutdown the renderer during this navigation.
5170 ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
5171
5172 // Important: This is a browser-initiated navigation, so the NavigationRequest
5173 // does not have an open connection (NavigationClient) to the renderer that it
5174 // is listening to for termination while running NavigationThrottles.
5175 //
5176 // Expect this navigation to be aborted, so we stop waiting after the
5177 // uncommitted navigation is done.
5178 GURL url2 = embedded_test_server()->GetURL("a.com", "/title1.html");
5179 NavigateToURLBlockUntilNavigationsComplete(
5180 shell(), url2, /*number_of_navigations=*/1,
5181 /*ignore_uncommitted_navigations=*/false);
5182
5183 // The renderer was shutdown mid-navigation.
Dave Tapuska327c06c92022-06-13 20:31:515184 EXPECT_FALSE(
5185 shell()->web_contents()->GetPrimaryMainFrame()->IsRenderFrameLive());
danakj79bd493f2021-02-10 20:20:385186
5187 // The navigation was aborted, which means it finished but did not commit, and
5188 // _importantly_ it never reported "ReadyToCommitNavigation" without a live
5189 // renderer.
5190 EXPECT_TRUE(no_commit_obs.navigation_finished());
5191 EXPECT_FALSE(no_commit_obs.navigation_was_ready_to_commit());
5192 EXPECT_FALSE(no_commit_obs.navigation_committed());
5193}
5194
Arthur Sonzognib6889bb22022-07-14 10:16:415195// Sandbox flags defined by the parent must not apply to Chrome's error page.
arthursonzognie4135822020-12-15 10:32:165196IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, ErrorPageFromInSandboxedIframe) {
5197 GURL url = embedded_test_server()->GetURL("a.com", "/empty.html");
5198 EXPECT_TRUE(NavigateToURL(shell(), url));
5199
5200 // Block every navigation in WillProcessResponse.
5201 std::unique_ptr<content::TestNavigationThrottleInserter> blocker =
5202 BlockNavigationWillProcessResponse(web_contents());
5203
5204 TestNavigationManager manager(web_contents(), url);
5205 ExecuteScriptAsync(current_frame_host(), R"(
5206 let iframe = document.createElement("iframe");
5207 iframe.src = location.href;
5208 iframe.sandbox = "allow-orientation-lock";
5209 document.body.appendChild(iframe);
5210 )");
Fergal Daly83bc3cd2023-01-18 00:22:545211 ASSERT_TRUE(manager.WaitForNavigationFinished());
arthursonzognie4135822020-12-15 10:32:165212
5213 RenderFrameHostImpl* child_rfh =
5214 current_frame_host()->child_at(0)->current_frame_host();
5215
Miyoung Shina2dd6a42021-10-07 12:19:215216 EXPECT_TRUE(child_rfh->IsErrorDocument());
Arthur Sonzognib6889bb22022-07-14 10:16:415217 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
arthursonzognie4135822020-12-15 10:32:165218 child_rfh->active_sandbox_flags());
5219}
5220
arthursonzogni052a1f252020-11-04 16:35:465221IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, OriginToCommitSandboxFromFrame) {
5222 GURL url = embedded_test_server()->GetURL("a.com", "/empty.html");
5223 EXPECT_TRUE(NavigateToURL(shell(), url));
5224 TestNavigationManager manager(web_contents(), url);
5225 ExecuteScriptAsync(current_frame_host(), R"(
5226 let iframe = document.createElement("iframe");
5227 iframe.src = location.href;
5228 iframe.sandbox = "";
5229 document.body.appendChild(iframe);
5230 )");
5231 EXPECT_TRUE(manager.WaitForResponse());
5232 FrameTreeNode* iframe = current_frame_host()->child_at(0);
5233 NavigationRequest* navigation = iframe->navigation_request();
Lukasz Anforowicz435e68d2022-11-09 21:47:445234 url::Origin origin_to_commit = navigation->GetOriginToCommit().value();
Fergal Daly83bc3cd2023-01-18 00:22:545235 ASSERT_TRUE(manager.WaitForNavigationFinished());
arthursonzogni052a1f252020-11-04 16:35:465236 url::Origin origin_committed =
5237 iframe->current_frame_host()->GetLastCommittedOrigin();
5238
5239 EXPECT_TRUE(origin_to_commit.opaque());
5240 EXPECT_TRUE(origin_committed.opaque());
Alison Gale770f3fc2024-04-27 00:39:585241 // TODO(crbug.com/40092527). Make the nonce to match.
arthursonzogni052a1f252020-11-04 16:35:465242 EXPECT_NE(origin_to_commit, origin_committed);
5243}
5244
Dibyajyoti Pal08608bd52025-06-13 22:37:135245// TODO(crbug.com/424764870): Fix flakiness.
5246#if BUILDFLAG(IS_FUCHSIA)
5247#define MAYBE_NavigateToAboutBlankWhileFirstNavigationPending \
5248 DISABLED_NavigateToAboutBlankWhileFirstNavigationPending
5249#else
5250#define MAYBE_NavigateToAboutBlankWhileFirstNavigationPending \
5251 NavigateToAboutBlankWhileFirstNavigationPending
5252#endif // BUILDFLAG(IS_FUCHSIA)
Aaron Colwellff719d752020-12-11 01:29:525253IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
Dibyajyoti Pal08608bd52025-06-13 22:37:135254 MAYBE_NavigateToAboutBlankWhileFirstNavigationPending) {
Aaron Colwellff719d752020-12-11 01:29:525255 GURL url_a = embedded_test_server()->GetURL("a.com", "/empty.html");
5256 GURL url_b = embedded_test_server()->GetURL("b.com", "/empty.html");
5257
5258 EXPECT_TRUE(NavigateToURL(shell(), url_a));
5259
5260 ShellAddedObserver new_shell_observer;
5261 ExecuteScriptAsync(
5262 current_frame_host(),
5263 JsReplace("window.open($1, '_blank').location = 'about:blank'", url_b));
5264
5265 WebContents* popup_contents = new_shell_observer.GetShell()->web_contents();
5266 TestNavigationManager manager_1(popup_contents, url_b);
5267 TestNavigationManager manager_2(popup_contents, GURL("about:blank"));
5268
Fergal Daly83bc3cd2023-01-18 00:22:545269 ASSERT_TRUE(manager_1.WaitForNavigationFinished());
5270 ASSERT_TRUE(manager_2.WaitForNavigationFinished());
Aaron Colwellff719d752020-12-11 01:29:525271
Sharon Yangf08acfc2022-01-04 20:49:015272 EXPECT_EQ(popup_contents->GetLastCommittedURL(), "about:blank");
Aaron Colwellff719d752020-12-11 01:29:525273}
5274
arthursonzogni052a1f252020-11-04 16:35:465275class NetworkIsolationSplitCacheAppendIframeOrigin
5276 : public NavigationBaseBrowserTest {
5277 public:
5278 NetworkIsolationSplitCacheAppendIframeOrigin() {
Dustin J. Mitchell737904c2023-02-21 21:53:065279 feature_list_.InitAndEnableFeature(
5280 net::features::kSplitCacheByNetworkIsolationKey);
arthursonzogni052a1f252020-11-04 16:35:465281 }
5282
5283 private:
5284 base::test::ScopedFeatureList feature_list_;
5285};
5286
5287// Make a main document, have it request a cacheable subresources. Then make a
5288// same-site document in an iframe that serves the CSP:Sandbox header. Stop the
5289// test server, have the sandboxed document requests the same subresource. The
5290// request should fail. To make sure the request is actually in the cache, the
5291// main document should be able to request it again.
5292IN_PROC_BROWSER_TEST_F(NetworkIsolationSplitCacheAppendIframeOrigin,
5293 SandboxedUsesDifferentCache) {
5294 auto server = std::make_unique<net::EmbeddedTestServer>();
5295 server->AddDefaultHandlers(GetTestDataFilePath());
5296 EXPECT_TRUE(server->Start());
5297
5298 GURL url_main_document = server->GetURL("a.com", "/empty.html");
5299
5300 EXPECT_TRUE(NavigateToURL(shell(), url_main_document));
5301 EXPECT_TRUE(ExecJs(current_frame_host(), R"(
5302 new Promise(resolve => {
5303 let iframe = document.createElement("iframe");
5304 iframe.onload = resolve;
5305 iframe.src = "/set-header?Content-Security-Policy: sandbox allow-scripts";
5306 document.body.appendChild(iframe);
5307 })
5308 )"));
5309 EXPECT_TRUE(WaitForLoadStop(web_contents()));
5310
5311 RenderFrameHostImpl* main_rfh = current_frame_host();
5312 RenderFrameHostImpl* sub_rfh = main_rfh->child_at(0)->current_frame_host();
5313
5314 EXPECT_FALSE(main_rfh->GetLastCommittedOrigin().opaque());
5315 EXPECT_TRUE(sub_rfh->GetLastCommittedOrigin().opaque());
5316
5317 const char* fetch_cacheable = R"(
5318 fetch("cacheable.svg")
5319 .then(() => "success")
5320 .catch(() => "error")
5321 )";
5322
5323 EXPECT_EQ("success", EvalJs(main_rfh, fetch_cacheable));
5324
5325 server.reset();
5326
5327 EXPECT_EQ("error", EvalJs(sub_rfh, fetch_cacheable));
5328 EXPECT_EQ("success", EvalJs(main_rfh, fetch_cacheable));
5329}
5330
Antonio Sartori246d5c02020-11-18 18:47:015331// The Content Security Policy directive 'treat-as-public-address' is parsed
5332// into the parsed headers by services/network and applied there. That directive
5333// is ignored in report-only policies. Here we check that Blink reports a
5334// console message if 'treat-as-public-address' is delivered in a report-only
5335// policy. This serves also as a regression test for https://crbug.com/1150314
5336IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
5337 TreatAsPublicAddressInReportOnly) {
5338 WebContentsConsoleObserver console_observer(web_contents());
5339 console_observer.SetPattern(
5340 "The Content Security Policy directive 'treat-as-public-address' is "
5341 "ignored when delivered in a report-only policy.");
5342
5343 GURL url = embedded_test_server()->GetURL(
5344 "/set-header?"
5345 "Content-Security-Policy-Report-Only: treat-as-public-address");
5346 EXPECT_TRUE(NavigateToURL(shell(), url));
5347
Fergal Daly7723f9d2022-10-29 07:03:135348 ASSERT_TRUE(console_observer.Wait());
Antonio Sartori246d5c02020-11-18 18:47:015349}
5350
Antonio Sartori8f48b812021-02-08 17:29:315351// The Content Security Policy directive 'plugin-types' has been removed. Here
5352// we check that Blink reports a console message if 'plugin-type' is delivered
5353// in a policy.
5354IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
5355 ContentSecurityPolicyErrorPluginTypes) {
5356 WebContentsConsoleObserver console_observer(web_contents());
5357 console_observer.SetPattern(
5358 "The Content-Security-Policy directive 'plugin-types' has been removed "
5359 "from the specification. "
5360 "If you want to block plugins, consider specifying \"object-src 'none'\" "
5361 "instead.");
5362
5363 GURL url = embedded_test_server()->GetURL(
5364 "/set-header?"
5365 "Content-Security-Policy: plugin-types application/pdf");
5366 EXPECT_TRUE(NavigateToURL(shell(), url));
5367
Fergal Daly7723f9d2022-10-29 07:03:135368 ASSERT_TRUE(console_observer.Wait());
Antonio Sartori8f48b812021-02-08 17:29:315369}
5370
Lukasz Anforowicz23fbe112021-04-01 14:09:025371class SubresourceLoadingTest : public NavigationBrowserTest {
5372 public:
5373 SubresourceLoadingTest() = default;
5374 SubresourceLoadingTest(const SubresourceLoadingTest&) = delete;
5375 SubresourceLoadingTest& operator=(const SubresourceLoadingTest&) = delete;
Lukasz Anforowiczc9843242021-02-10 23:04:595376
Lukasz Anforowicz23fbe112021-04-01 14:09:025377 void DontTestNetworkServiceCrashes() {
5378 test_network_service_crashes_ = false;
5379 }
Lukasz Anforowicz94f7258f2021-03-25 00:01:145380
Lukasz Anforowicz23fbe112021-04-01 14:09:025381 void VerifyResultsOfAboutBlankNavigation(RenderFrameHost* target_frame,
5382 RenderFrameHost* initiator_frame) {
5383 // Verify that `target_frame` has been navigated to "about:blank".
5384 EXPECT_EQ(GURL(url::kAboutBlankURL), target_frame->GetLastCommittedURL());
Lukasz Anforowicz94f7258f2021-03-25 00:01:145385
Lukasz Anforowicz23fbe112021-04-01 14:09:025386 // Verify that "about:blank" committed with the expected origin, and in the
5387 // expected SiteInstance.
5388 EXPECT_EQ(target_frame->GetLastCommittedOrigin(),
5389 initiator_frame->GetLastCommittedOrigin());
5390 EXPECT_EQ(target_frame->GetSiteInstance(),
5391 initiator_frame->GetSiteInstance());
Lukasz Anforowiczc9843242021-02-10 23:04:595392
Lukasz Anforowicz23fbe112021-04-01 14:09:025393 // Ask for cookies in the `target_frame`. One implicit verification here
5394 // is whether this step will hit any `cookie_url`-related NOTREACHED or DwoC
5395 // in RestrictedCookieManager::ValidateAccessToCookiesAt. This verification
5396 // is non-racey, because `document.cookie` must have heard back from the
5397 // RestrictedCookieManager before returning the value of cookies (this
5398 // ignores possible Blink-side caching, but this is the first time the
5399 // renderer needs the cookies and so this is okay for this test).
5400 EXPECT_EQ("", EvalJs(target_frame, "document.cookie"));
Lukasz Anforowiczc9843242021-02-10 23:04:595401
Lukasz Anforowicz23fbe112021-04-01 14:09:025402 // Verify that the "about:blank" frame is able to load an image.
5403 VerifyImageSubresourceLoads(target_frame);
5404 }
Lukasz Anforowicz94f7258f2021-03-25 00:01:145405
Lukasz Anforowicz23fbe112021-04-01 14:09:025406 void VerifyImageSubresourceLoads(
5407 const ToRenderFrameHost& target,
5408 const std::string& target_document = "document") {
Lukasz Anforowiczf0fc1ca2021-07-16 15:03:155409 RenderFrameHostImpl* target_frame =
5410 static_cast<RenderFrameHostImpl*>(target.render_frame_host());
5411 VerifySingleImageSubresourceLoad(target_frame, target_document);
Lukasz Anforowiczc9843242021-02-10 23:04:595412
Lukasz Anforowicz23fbe112021-04-01 14:09:025413 // Verify detecting and recovering from a NetworkService crash (e.g. via the
5414 // `network_service_disconnect_handler_holder_mojo` field and the
5415 // UpdateSubresourceLoaderFactories method of RenderFrameHostImpl).
5416 if (!IsInProcessNetworkService() && test_network_service_crashes_) {
5417 SimulateNetworkServiceCrash();
Lukasz Anforowiczf0fc1ca2021-07-16 15:03:155418
5419 // In addition to waiting (inside SimulateNetworkServiceCrash above) for
5420 // getting notified about being disconnected from
5421 // network::mojom::NetworkServiceTest, we also want to make sure that the
5422 // relevant RenderFrameHost realizes that the NetworkService has crashed.
5423 // Which RenderFrameHost is relevant varies from test to test, so we
5424 // flush multiple frames and use kDoNothingIfNoNetworkServiceConnection.
5425 FlushNetworkInterfacesInOpenerChain(target_frame);
5426
5427 // Rerun the test after the NetworkService crash.
5428 VerifySingleImageSubresourceLoad(target_frame, target_document);
Lukasz Anforowicz23fbe112021-04-01 14:09:025429 }
5430 }
5431
5432 private:
Lukasz Anforowiczf0fc1ca2021-07-16 15:03:155433 void FlushNetworkInterfacesInOpenerChain(RenderFrameHostImpl* current_frame) {
5434 std::set<WebContents*> visited_contents;
5435 while (true) {
5436 // Check if we've already visited the current frame tree.
5437 DCHECK(current_frame);
5438 WebContents* current_contents =
5439 WebContents::FromRenderFrameHost(current_frame);
5440 DCHECK(current_contents);
Takashi Toyoshimae9c959ce92025-05-20 19:18:385441 if (base::Contains(visited_contents, current_contents)) {
Lukasz Anforowiczf0fc1ca2021-07-16 15:03:155442 break;
Takashi Toyoshimae9c959ce92025-05-20 19:18:385443 }
Lukasz Anforowiczf0fc1ca2021-07-16 15:03:155444 visited_contents.insert(current_contents);
5445
Dave Tapuskaed66163f2021-09-28 14:34:545446 // Flush all the frames in the `current_contents's active page.
Dave Tapuska327c06c92022-06-13 20:31:515447 current_contents->GetPrimaryMainFrame()->ForEachRenderFrameHost(
Daniel Cheng982f2b22022-08-25 23:46:165448 [](RenderFrameHost* frame_to_flush) {
Dave Tapuskaed66163f2021-09-28 14:34:545449 constexpr bool kDoNothingIfNoNetworkServiceConnection = true;
5450 frame_to_flush->FlushNetworkAndNavigationInterfacesForTesting(
5451 kDoNothingIfNoNetworkServiceConnection);
Daniel Cheng982f2b22022-08-25 23:46:165452 });
Lukasz Anforowiczf0fc1ca2021-07-16 15:03:155453
5454 // Traverse the `current_frame`'s opener chain.
5455 if (FrameTreeNode* opener_node =
5456 current_frame->frame_tree_node()->opener()) {
5457 current_frame = opener_node->current_frame_host();
5458 } else {
5459 break; // Break out of the loop if there is no opener.
5460 }
5461 }
5462 }
5463
5464 void VerifySingleImageSubresourceLoad(RenderFrameHost* target,
Peter Kasting472500482024-11-21 18:36:255465 std::string_view target_document) {
Lukasz Anforowicz23fbe112021-04-01 14:09:025466 // Use a random, GUID-based hostname, to avoid hitting the network cache.
5467 GURL image_url = embedded_test_server()->GetURL(
Claudio DeSouza11cb1cb602023-04-17 14:09:265468 base::Uuid::GenerateRandomV4().AsLowercaseString() + ".com",
5469 "/blank.jpg");
Peter Kasting472500482024-11-21 18:36:255470 const std::string script = base::StrCat({
5471 R"(new Promise(resolve => {
5472 let img = document.createElement('img'); )",
5473 JsReplace("img.src = $1;", image_url),
5474 R"( img.addEventListener('load', () => {
5475 resolve('allowed');
5476 });
5477 img.addEventListener('error', err => {
5478 resolve(`error: ${err}`);
5479 }); )",
5480 target_document, R"(.body.appendChild(img);
5481 }); )"});
Lukasz Anforowicz23fbe112021-04-01 14:09:025482 EXPECT_EQ("allowed", EvalJs(target, script));
5483 }
5484
5485 bool test_network_service_crashes_ = true;
5486};
Lukasz Anforowiczc9843242021-02-10 23:04:595487
Lukasz Anforowicze9db2652021-01-27 00:24:125488// The test below verifies that an "about:blank" navigation commits with the
5489// right origin, even when the initiator of the navigation is not the parent or
5490// opener of the frame targeted by the navigation. In the
5491// GrandchildToAboutBlank... testcases, the navigation is initiated by the
5492// grandparent of the target frame.
5493//
5494// In this test case there are no process swaps and the parent of the navigated
5495// frame is a local frame (even in presence of site-per-process). See also
5496// GrandchildToAboutBlank_ABA_CrossSite and
5497// GrandchildToAboutBlank_ABB_CrossSite.
Lukasz Anforowicz23fbe112021-04-01 14:09:025498IN_PROC_BROWSER_TEST_F(SubresourceLoadingTest,
Lukasz Anforowicze9db2652021-01-27 00:24:125499 GrandchildToAboutBlank_ABA_SameSite) {
5500 GURL url(embedded_test_server()->GetURL(
5501 "a.example.com",
5502 "/cross_site_iframe_factory.html?"
5503 "a.example.com(b.example.com(a.example.com))"));
5504 EXPECT_TRUE(NavigateToURL(shell(), url));
5505
5506 // Verify the desired properties of the test setup.
5507 RenderFrameHostImpl* main_frame = static_cast<RenderFrameHostImpl*>(
Dave Tapuska327c06c92022-06-13 20:31:515508 shell()->web_contents()->GetPrimaryMainFrame());
Lukasz Anforowicze9db2652021-01-27 00:24:125509 RenderFrameHostImpl* child_frame =
5510 main_frame->child_at(0)->current_frame_host();
5511 RenderFrameHostImpl* grandchild_frame =
5512 child_frame->child_at(0)->current_frame_host();
5513 EXPECT_EQ(main_frame->GetSiteInstance(), child_frame->GetSiteInstance());
5514 EXPECT_EQ(main_frame->GetSiteInstance(), grandchild_frame->GetSiteInstance());
5515 EXPECT_EQ(main_frame->GetLastCommittedOrigin(),
5516 grandchild_frame->GetLastCommittedOrigin());
5517 EXPECT_NE(main_frame->GetLastCommittedOrigin(),
5518 child_frame->GetLastCommittedOrigin());
5519
5520 // Navigate the grandchild frame to about:blank
5521 ASSERT_TRUE(ExecJs(grandchild_frame, "window.name = 'grandchild'"));
5522 TestNavigationObserver nav_observer(shell()->web_contents(), 1);
5523 ASSERT_TRUE(
5524 ExecJs(main_frame,
5525 "grandchild_window = window.open('about:blank', 'grandchild')"));
5526 nav_observer.Wait();
5527
5528 // Verify that the grandchild has the same origin as the main frame (*not* the
5529 // origin of the parent frame).
5530 main_frame = static_cast<RenderFrameHostImpl*>(
Dave Tapuska327c06c92022-06-13 20:31:515531 shell()->web_contents()->GetPrimaryMainFrame());
Lukasz Anforowicze9db2652021-01-27 00:24:125532 child_frame = main_frame->child_at(0)->current_frame_host();
5533 grandchild_frame = child_frame->child_at(0)->current_frame_host();
Lukasz Anforowicz23fbe112021-04-01 14:09:025534 VerifyResultsOfAboutBlankNavigation(grandchild_frame, main_frame);
Lukasz Anforowicze9db2652021-01-27 00:24:125535}
5536
5537// The test below verifies that an "about:blank" navigation commits with the
5538// right origin, even when the initiator of the navigation is not the parent or
5539// opener of the frame targeted by the navigation. In the
5540// GrandchildToAboutBlank... testcases, the navigation is initiated by the
5541// grandparent of the target frame.
5542//
5543// In this test case there are no process swaps and the parent of the navigated
5544// frame is a remote frame (in presence of site-per-process). See also
5545// GrandchildToAboutBlank_ABA_SameSite and GrandchildToAboutBlank_ABB_CrossSite.
Lukasz Anforowicz23fbe112021-04-01 14:09:025546IN_PROC_BROWSER_TEST_F(SubresourceLoadingTest,
Lukasz Anforowicze9db2652021-01-27 00:24:125547 GrandchildToAboutBlank_ABA_CrossSite) {
5548 GURL url(embedded_test_server()->GetURL(
5549 "a.com", "/cross_site_iframe_factory.html?a(b(a))"));
5550 EXPECT_TRUE(NavigateToURL(shell(), url));
5551
5552 // Verify the desired properties of the test setup.
5553 RenderFrameHostImpl* main_frame = static_cast<RenderFrameHostImpl*>(
Dave Tapuska327c06c92022-06-13 20:31:515554 shell()->web_contents()->GetPrimaryMainFrame());
Lukasz Anforowicze9db2652021-01-27 00:24:125555 RenderFrameHostImpl* child_frame =
5556 main_frame->child_at(0)->current_frame_host();
5557 RenderFrameHostImpl* grandchild_frame =
5558 child_frame->child_at(0)->current_frame_host();
Sharon Yang47453bb2025-04-25 14:48:095559 if (AreStrictSiteInstancesEnabled()) {
Aaron Colwella3bfc3442021-02-23 19:14:485560 EXPECT_NE(main_frame->GetSiteInstance(), child_frame->GetSiteInstance());
Sharon Yang02279222025-01-15 19:09:195561 } else {
5562 EXPECT_EQ(main_frame->GetSiteInstance(), child_frame->GetSiteInstance());
Lukasz Anforowicze9db2652021-01-27 00:24:125563 }
5564 EXPECT_EQ(main_frame->GetSiteInstance(), grandchild_frame->GetSiteInstance());
5565 EXPECT_EQ(main_frame->GetLastCommittedOrigin(),
5566 grandchild_frame->GetLastCommittedOrigin());
5567 EXPECT_NE(main_frame->GetLastCommittedOrigin(),
5568 child_frame->GetLastCommittedOrigin());
5569
5570 // Navigate the grandchild frame to about:blank
5571 ASSERT_TRUE(ExecJs(grandchild_frame, "window.name = 'grandchild'"));
5572 TestNavigationObserver nav_observer(shell()->web_contents(), 1);
5573 ASSERT_TRUE(
5574 ExecJs(main_frame,
5575 "grandchild_window = window.open('about:blank', 'grandchild')"));
5576 nav_observer.Wait();
5577
5578 // Verify that the grandchild has the same origin as the main frame (*not* the
5579 // origin of the parent frame).
5580 main_frame = static_cast<RenderFrameHostImpl*>(
Dave Tapuska327c06c92022-06-13 20:31:515581 shell()->web_contents()->GetPrimaryMainFrame());
Lukasz Anforowicze9db2652021-01-27 00:24:125582 child_frame = main_frame->child_at(0)->current_frame_host();
5583 grandchild_frame = child_frame->child_at(0)->current_frame_host();
Lukasz Anforowicz23fbe112021-04-01 14:09:025584 VerifyResultsOfAboutBlankNavigation(grandchild_frame, main_frame);
Lukasz Anforowicze9db2652021-01-27 00:24:125585}
5586
5587// The test below verifies that an "about:blank" navigation commits with the
5588// right origin, even when the initiator of the navigation is not the parent or
5589// opener of the frame targeted by the navigation. In the
5590// GrandchildToAboutBlank... testcases, the navigation is initiated by the
5591// grandparent of the target frame.
5592//
5593// In this test case the navigation forces a process swap of the target frame.
5594// See also GrandchildToAboutBlank_ABA_SameSite and
5595// GrandchildToAboutBlank_ABA_CrossSite.
Lukasz Anforowicz23fbe112021-04-01 14:09:025596IN_PROC_BROWSER_TEST_F(SubresourceLoadingTest,
Lukasz Anforowicze9db2652021-01-27 00:24:125597 GrandchildToAboutBlank_ABB_CrossSite) {
5598 GURL url(embedded_test_server()->GetURL(
5599 "a.com", "/cross_site_iframe_factory.html?a(b(b))"));
5600 EXPECT_TRUE(NavigateToURL(shell(), url));
5601
5602 // Verify the desired properties of the test setup.
5603 RenderFrameHostImpl* main_frame = static_cast<RenderFrameHostImpl*>(
Dave Tapuska327c06c92022-06-13 20:31:515604 shell()->web_contents()->GetPrimaryMainFrame());
Lukasz Anforowicze9db2652021-01-27 00:24:125605 RenderFrameHostImpl* child_frame =
5606 main_frame->child_at(0)->current_frame_host();
5607 RenderFrameHostImpl* grandchild_frame =
5608 child_frame->child_at(0)->current_frame_host();
Sharon Yang47453bb2025-04-25 14:48:095609 if (AreStrictSiteInstancesEnabled()) {
Aaron Colwella3bfc3442021-02-23 19:14:485610 EXPECT_NE(main_frame->GetSiteInstance(), child_frame->GetSiteInstance());
Sharon Yang02279222025-01-15 19:09:195611 } else {
5612 EXPECT_EQ(main_frame->GetSiteInstance(), child_frame->GetSiteInstance());
Lukasz Anforowicze9db2652021-01-27 00:24:125613 }
5614 EXPECT_EQ(child_frame->GetSiteInstance(),
5615 grandchild_frame->GetSiteInstance());
5616 EXPECT_EQ(child_frame->GetLastCommittedOrigin(),
5617 grandchild_frame->GetLastCommittedOrigin());
5618 EXPECT_NE(main_frame->GetLastCommittedOrigin(),
5619 grandchild_frame->GetLastCommittedOrigin());
5620
5621 // Navigate the grandchild frame to about:blank
5622 ASSERT_TRUE(ExecJs(grandchild_frame, "window.name = 'grandchild'"));
5623 TestNavigationObserver nav_observer(shell()->web_contents(), 1);
5624 ASSERT_TRUE(
5625 ExecJs(main_frame,
5626 "grandchild_window = window.open('about:blank', 'grandchild')"));
5627 nav_observer.Wait();
5628
5629 // Verify that the grandchild has the same origin as the main frame (*not* the
5630 // origin of the parent frame).
5631 main_frame = static_cast<RenderFrameHostImpl*>(
Dave Tapuska327c06c92022-06-13 20:31:515632 shell()->web_contents()->GetPrimaryMainFrame());
Lukasz Anforowicze9db2652021-01-27 00:24:125633 child_frame = main_frame->child_at(0)->current_frame_host();
5634 grandchild_frame = child_frame->child_at(0)->current_frame_host();
Lukasz Anforowicz23fbe112021-04-01 14:09:025635 VerifyResultsOfAboutBlankNavigation(grandchild_frame, main_frame);
Lukasz Anforowicze9db2652021-01-27 00:24:125636}
5637
5638// The test below verifies that an "about:blank" navigation commits with the
5639// right origin, even when the initiator of the navigation is not the parent or
5640// opener of the frame targeted by the navigation. In the
5641// TopToAboutBlank_CrossSite testcase, the top-level navigation is initiated by
5642// a cross-site subframe.
Lukasz Anforowicz23fbe112021-04-01 14:09:025643IN_PROC_BROWSER_TEST_F(SubresourceLoadingTest, TopToAboutBlank_CrossSite) {
Lukasz Anforowicze9db2652021-01-27 00:24:125644 GURL url(embedded_test_server()->GetURL(
5645 "a.com", "/cross_site_iframe_factory.html?a(b)"));
5646 EXPECT_TRUE(NavigateToURL(shell(), url));
5647
5648 // Verify the desired properties of the test setup.
5649 RenderFrameHostImpl* main_frame = static_cast<RenderFrameHostImpl*>(
Dave Tapuska327c06c92022-06-13 20:31:515650 shell()->web_contents()->GetPrimaryMainFrame());
Lukasz Anforowicze9db2652021-01-27 00:24:125651 RenderFrameHostImpl* child_frame =
5652 main_frame->child_at(0)->current_frame_host();
Sharon Yang47453bb2025-04-25 14:48:095653 if (AreStrictSiteInstancesEnabled()) {
Aaron Colwella3bfc3442021-02-23 19:14:485654 EXPECT_NE(main_frame->GetSiteInstance(), child_frame->GetSiteInstance());
Sharon Yang02279222025-01-15 19:09:195655 } else {
5656 EXPECT_EQ(main_frame->GetSiteInstance(), child_frame->GetSiteInstance());
Lukasz Anforowicze9db2652021-01-27 00:24:125657 }
5658 url::Origin a_origin =
5659 url::Origin::Create(embedded_test_server()->GetURL("a.com", "/"));
5660 url::Origin b_origin =
5661 url::Origin::Create(embedded_test_server()->GetURL("b.com", "/"));
5662 EXPECT_EQ(a_origin, main_frame->GetLastCommittedOrigin());
5663 EXPECT_EQ(b_origin, child_frame->GetLastCommittedOrigin());
5664
5665 // Have the subframe initiate navigation of the main frame to about:blank.
5666 //
5667 // (Note that this scenario is a bit artificial/silly, because the final
5668 // about:blank frame won't have any same-origin friends that could populate
5669 // it. OTOH, it is still important to maintain all the invariants in this
5670 // scenario. And it is still possible that a same-origin frame (e.g. in
5671 // another window in the same BrowsingInstance) exists and can populate the
5672 // about:blank frame.
5673 TestNavigationObserver nav_observer(shell()->web_contents(), 1);
5674 ASSERT_TRUE(ExecJs(child_frame, "window.top.location = 'about:blank'"));
5675 nav_observer.Wait();
5676
5677 // Verify that the main frame is the only remaining frame and that it has the
5678 // same origin as the navigation initiator.
5679 main_frame = static_cast<RenderFrameHostImpl*>(
Dave Tapuska327c06c92022-06-13 20:31:515680 shell()->web_contents()->GetPrimaryMainFrame());
Lukasz Anforowicze9db2652021-01-27 00:24:125681 EXPECT_EQ(0u, main_frame->child_count());
5682 EXPECT_EQ(b_origin, main_frame->GetLastCommittedOrigin());
5683 EXPECT_EQ(GURL(url::kAboutBlankURL), main_frame->GetLastCommittedURL());
5684}
5685
5686// The test below verifies that an "about:blank" navigation commits with the
5687// right origin, even when the initiator of the navigation is not the parent or
5688// opener of the frame targeted by the navigation. In the
5689// SameSiteSiblingToAboutBlank_CrossSiteTop testcase, the navigation is
5690// initiated by a same-origin sibling (notably, not by one of target frame's
5691// ancestors) and both siblings are subframes of a cross-site main frame.
Lukasz Anforowicz23fbe112021-04-01 14:09:025692IN_PROC_BROWSER_TEST_F(SubresourceLoadingTest,
Lukasz Anforowicze9db2652021-01-27 00:24:125693 SameSiteSiblingToAboutBlank_CrossSiteTop) {
5694 GURL url(embedded_test_server()->GetURL(
5695 "a.com", "/cross_site_iframe_factory.html?a(b,b)"));
5696 EXPECT_TRUE(NavigateToURL(shell(), url));
5697
5698 // Name the 2nd child.
5699 RenderFrameHostImpl* main_frame = static_cast<RenderFrameHostImpl*>(
Dave Tapuska327c06c92022-06-13 20:31:515700 shell()->web_contents()->GetPrimaryMainFrame());
Lukasz Anforowicze9db2652021-01-27 00:24:125701 RenderFrameHostImpl* child_frame1 =
5702 main_frame->child_at(0)->current_frame_host();
5703 RenderFrameHostImpl* child_frame2 =
5704 main_frame->child_at(1)->current_frame_host();
5705 ASSERT_TRUE(ExecJs(child_frame2, "window.name = 'child2'"));
5706
5707 // Grab `child2` window from the 1st child...
5708 ASSERT_TRUE(ExecJs(child_frame1, "child2 = window.open('', 'child2')"));
5709 // ...but make sure that child2's opener doesn't point to child1.
5710 ASSERT_TRUE(ExecJs(main_frame, "child2 = window.open('', 'child2')"));
5711 EXPECT_EQ(true, EvalJs(child_frame2, "window.opener == window.top"));
5712
5713 // From child1 initiate navigation of child2 to about:blank.
5714 TestNavigationObserver nav_observer(shell()->web_contents(), 1);
5715 ASSERT_TRUE(ExecJs(child_frame1, "child2.location = 'about:blank'"));
5716 nav_observer.Wait();
5717
5718 // Verify that child2 has the origin of the initiator of the navigation.
5719 main_frame = static_cast<RenderFrameHostImpl*>(
Dave Tapuska327c06c92022-06-13 20:31:515720 shell()->web_contents()->GetPrimaryMainFrame());
Lukasz Anforowicze9db2652021-01-27 00:24:125721 child_frame1 = main_frame->child_at(0)->current_frame_host();
5722 child_frame2 = main_frame->child_at(1)->current_frame_host();
Lukasz Anforowicz23fbe112021-04-01 14:09:025723 VerifyResultsOfAboutBlankNavigation(child_frame2, child_frame1);
5724}
5725
5726// The test below verifies that an initial empty document has a functional
5727// URLLoaderFactory. Note that some aspects of the current behavior (e.g. the
5728// synchronous re-navigation) are not spec-compliant - see
5729// https://crbug.com/778318 and https://github.com/whatwg/html/issues/3267.
5730// Note that the same behavior is expected in the ...NewFrameWithoutSrc and
5731// in the ...NewFrameWithAboutBlank testcases.
5732IN_PROC_BROWSER_TEST_F(SubresourceLoadingTest,
5733 URLLoaderFactoryInInitialEmptyDoc_NewFrameWithoutSrc) {
5734 GURL opener_url(embedded_test_server()->GetURL("/title1.html"));
5735 EXPECT_TRUE(NavigateToURL(shell(), opener_url));
5736
5737 // This inserts an `iframe` element without an `src` attribute. According to
5738 // some specs "the browsing context will remain at the initial about:blank
5739 // page", although other specs suggest that there is an explicit, separate
5740 // navigation. See:
5741 // https://html.spec.whatwg.org/dev/iframe-embed-object.html#the-iframe-element
5742 // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#shared-attribute-processing-steps-for-iframe-and-frame-elements
5743 ASSERT_TRUE(ExecJs(shell(), R"( let ifr = document.createElement('iframe');
5744 document.body.appendChild(ifr); )"));
5745 WaitForLoadStop(shell()->web_contents());
Dave Tapuska327c06c92022-06-13 20:31:515746 RenderFrameHost* main_frame = shell()->web_contents()->GetPrimaryMainFrame();
Lukasz Anforowicz23fbe112021-04-01 14:09:025747 RenderFrameHost* subframe = ChildFrameAt(main_frame, 0);
5748
5749 VerifyResultsOfAboutBlankNavigation(subframe, main_frame);
5750}
5751
5752// See the doc comment for the
5753// URLLoaderFactoryInInitialEmptyDoc_NewFrameWithoutSrc test case.
5754IN_PROC_BROWSER_TEST_F(
5755 SubresourceLoadingTest,
Lukasz Anforowiczf0fc1ca2021-07-16 15:03:155756 URLLoaderFactoryInInitialEmptyDoc_NewFrameWithAboutBlank) {
Lukasz Anforowicz23fbe112021-04-01 14:09:025757 GURL opener_url(embedded_test_server()->GetURL("/title1.html"));
5758 EXPECT_TRUE(NavigateToURL(shell(), opener_url));
5759
5760 ASSERT_TRUE(ExecJs(shell(), R"( ifr = document.createElement('iframe');
5761 ifr.src = 'about:blank';
5762 document.body.appendChild(ifr); )"));
5763 WaitForLoadStop(shell()->web_contents());
Dave Tapuska327c06c92022-06-13 20:31:515764 RenderFrameHost* main_frame = shell()->web_contents()->GetPrimaryMainFrame();
Lukasz Anforowicz23fbe112021-04-01 14:09:025765 RenderFrameHost* subframe = ChildFrameAt(main_frame, 0);
5766
5767 VerifyResultsOfAboutBlankNavigation(subframe, main_frame);
5768}
5769
Yoav Weiss3e46271d2021-04-30 20:35:215770IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
5771 SameOriginFlagOfSameOriginAboutBlankNavigation) {
5772 GURL parent_url(embedded_test_server()->GetURL("a.com", "/empty.html"));
5773 GURL iframe_url(embedded_test_server()->GetURL("a.com", "/empty.html"));
5774 EXPECT_TRUE(NavigateToURL(shell(), parent_url));
5775
5776 EXPECT_TRUE(ExecJs(current_frame_host(), JsReplace(R"(
5777 let iframe = document.createElement('iframe');
5778 iframe.src = $1;
5779 document.body.appendChild(iframe);
5780 )",
5781 iframe_url)));
5782 WaitForLoadStop(shell()->web_contents());
5783
5784 base::RunLoop loop;
5785 DidFinishNavigationCallback callback(
5786 shell()->web_contents(),
5787 base::BindLambdaForTesting([&](NavigationHandle* handle) {
5788 ASSERT_TRUE(handle->HasCommitted());
5789 EXPECT_TRUE(handle->IsSameOrigin());
5790 loop.Quit();
5791 }));
5792
5793 // Changing the src to trigger DidFinishNavigationCallback
5794 EXPECT_TRUE(ExecJs(current_frame_host(), R"(
5795 document.querySelector("iframe").src = 'about:blank';
5796 )"));
5797 loop.Run();
5798}
5799
5800IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
5801 SameOriginFlagOfCrossOriginAboutBlankNavigation) {
5802 GURL parent_url(embedded_test_server()->GetURL("a.com", "/empty.html"));
5803 GURL iframe_url(embedded_test_server()->GetURL("b.com", "/empty.html"));
5804 EXPECT_TRUE(NavigateToURL(shell(), parent_url));
5805
5806 EXPECT_TRUE(ExecJs(current_frame_host(), JsReplace(R"(
5807 let iframe = document.createElement('iframe');
5808 iframe.src = $1;
5809 document.body.appendChild(iframe);
5810 )",
5811 iframe_url)));
5812 WaitForLoadStop(shell()->web_contents());
5813
5814 base::RunLoop loop;
5815 DidFinishNavigationCallback callback(
5816 shell()->web_contents(),
5817 base::BindLambdaForTesting([&](NavigationHandle* handle) {
5818 ASSERT_TRUE(handle->HasCommitted());
5819 EXPECT_FALSE(handle->IsSameOrigin());
5820 loop.Quit();
5821 }));
5822
5823 // Changing the src to trigger DidFinishNavigationCallback
5824 EXPECT_TRUE(ExecJs(current_frame_host(), R"(
5825 document.querySelector("iframe").src = 'about:blank';
5826 )"));
5827 loop.Run();
5828}
5829
5830IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
5831 SameOriginFlagOfSrcdocNavigation) {
5832 GURL url = embedded_test_server()->GetURL("a.com", "/empty.html");
5833 GURL cross_origin = embedded_test_server()->GetURL("b.com", "/empty.html");
5834 EXPECT_TRUE(NavigateToURL(shell(), url));
5835
5836 // Navigating to about:srcdoc from the initial empty document is always a
5837 // same-origin navigation:
5838 // - about:srcdoc is same-origin with the parent.
5839 // - the initial empty document is same-origin with the parent.
5840 {
5841 base::RunLoop loop;
5842 DidFinishNavigationCallback callback(
5843 shell()->web_contents(),
5844 base::BindLambdaForTesting([&](NavigationHandle* handle) {
5845 ASSERT_TRUE(handle->HasCommitted());
5846 EXPECT_TRUE(handle->IsSameOrigin());
5847 loop.Quit();
5848 }));
5849 EXPECT_TRUE(ExecJs(current_frame_host(), R"(
5850 let iframe = document.createElement('iframe');
5851 iframe.srcdoc = "dummy content";
5852 document.body.appendChild(iframe);
5853 )"));
5854 loop.Run();
5855 }
5856
5857 // Now, navigate cross-origin, and back to about:srcdoc with a brand new
5858 // iframe. The navigation is now considered cross-origin.
5859 // - the previous document is cross-origin with the parent.
5860 // - about:srcdoc is same-origin with the parent.
5861 {
5862 EXPECT_TRUE(ExecJs(current_frame_host(), JsReplace(R"(
5863 let iframe2 = document.createElement('iframe');
5864 iframe2.src = $1;
5865 iframe2.id = 'iframe2';
5866 document.body.appendChild(iframe2);
5867 )",
5868 cross_origin)));
5869 WaitForLoadStop(shell()->web_contents());
5870
5871 base::RunLoop loop;
5872 DidFinishNavigationCallback callback(
5873 shell()->web_contents(),
5874 base::BindLambdaForTesting([&](NavigationHandle* handle) {
5875 ASSERT_TRUE(handle->HasCommitted());
5876 EXPECT_FALSE(handle->IsSameOrigin());
5877 loop.Quit();
5878 }));
5879 EXPECT_TRUE(ExecJs(current_frame_host(), R"(
5880 document.getElementById("iframe2").srcdoc = "dummy content";
5881 )"));
5882 loop.Run();
5883 }
5884}
5885
5886IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
5887 SameOriginFlagOfAboutBlankToAboutBlankNavigation) {
5888 GURL parent_url(embedded_test_server()->GetURL("a.com", "/empty.html"));
5889 GURL iframe_url(embedded_test_server()->GetURL("b.com", "/empty.html"));
5890 EXPECT_TRUE(NavigateToURL(shell(), parent_url));
5891
5892 EXPECT_TRUE(ExecJs(main_frame(), JsReplace(R"(
5893 let iframe = document.createElement('iframe');
5894 iframe.src = $1;
5895 document.body.appendChild(iframe);
5896 )",
5897 iframe_url)));
5898 WaitForLoadStop(shell()->web_contents());
5899
5900 // Test a same-origin about:blank navigation
5901 {
5902 base::RunLoop loop;
5903 DidFinishNavigationCallback callback(
5904 shell()->web_contents(),
5905 base::BindLambdaForTesting([&](NavigationHandle* handle) {
5906 ASSERT_TRUE(handle->HasCommitted());
5907 EXPECT_TRUE(handle->IsSameOrigin());
5908 loop.Quit();
5909 }));
5910 RenderFrameHostImpl* child_document =
5911 current_frame_host()->child_at(0)->current_frame_host();
5912 EXPECT_TRUE(ExecJs(child_document, R"(location.href = "about:blank";)"));
5913 loop.Run();
5914 }
5915
5916 // Test another same-origin about:blank navigation
5917 {
5918 base::RunLoop loop;
5919 DidFinishNavigationCallback callback(
5920 shell()->web_contents(),
5921 base::BindLambdaForTesting([&](NavigationHandle* handle) {
5922 ASSERT_TRUE(handle->HasCommitted());
5923 EXPECT_TRUE(handle->IsSameOrigin());
5924 loop.Quit();
5925 }));
5926 RenderFrameHostImpl* child_document =
5927 current_frame_host()->child_at(0)->current_frame_host();
5928 EXPECT_TRUE(ExecJs(child_document, R"(location.href = "about:blank";)"));
5929 loop.Run();
5930 }
5931
5932 // Test a cross-origin about:blank navigation
5933 {
5934 base::RunLoop loop;
5935 DidFinishNavigationCallback callback(
5936 shell()->web_contents(),
5937 base::BindLambdaForTesting([&](NavigationHandle* handle) {
5938 ASSERT_TRUE(handle->HasCommitted());
5939 EXPECT_FALSE(handle->IsSameOrigin());
5940 loop.Quit();
5941 }));
5942 EXPECT_TRUE(ExecJs(current_frame_host(), R"(
5943 document.querySelector('iframe').src = "about:blank";
5944 )"));
5945 loop.Run();
5946 }
5947}
5948
5949IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, SameOriginOfSandboxedIframe) {
5950 EXPECT_TRUE(NavigateToURL(
5951 shell(), embedded_test_server()->GetURL("a.com", "/empty.html")));
5952
5953 base::RunLoop loop;
5954 DidFinishNavigationCallback callback(
5955 shell()->web_contents(),
5956 base::BindLambdaForTesting([&](NavigationHandle* handle) {
5957 ASSERT_TRUE(handle->HasCommitted());
Alison Gale770f3fc2024-04-27 00:39:585958 // TODO(crbug.com/40092527) Take sandbox into account. Same Origin
Yoav Weiss3e46271d2021-04-30 20:35:215959 // should be true
5960 EXPECT_FALSE(handle->IsSameOrigin());
5961 loop.Quit();
5962 }));
5963 EXPECT_TRUE(ExecJs(current_frame_host(), R"(
5964 let iframe = document.createElement('iframe');
5965 iframe.sandbox = "allow-scripts";
5966 iframe.src = "/empty.html";
5967 document.body.appendChild(iframe);
5968 )"));
5969 loop.Run();
5970}
5971
Lukasz Anforowicz23fbe112021-04-01 14:09:025972// The test below verifies that an initial empty document has a functional
Rakina Zata Amniafd3c6582021-11-30 06:19:175973// URLLoaderFactory.
Lukasz Anforowicz23fbe112021-04-01 14:09:025974IN_PROC_BROWSER_TEST_F(SubresourceLoadingTest,
5975 URLLoaderFactoryInInitialEmptyDoc_NewPopupToEmptyUrl) {
5976 GURL opener_url(embedded_test_server()->GetURL("/title1.html"));
5977 EXPECT_TRUE(NavigateToURL(shell(), opener_url));
5978
Rakina Zata Amniafd3c6582021-11-30 06:19:175979 WebContentsImpl* popup = nullptr;
Lukasz Anforowicz23fbe112021-04-01 14:09:025980 {
5981 WebContentsAddedObserver popup_observer;
5982 ASSERT_TRUE(ExecJs(shell(), "window.open('', '_blank')"));
Rakina Zata Amniafd3c6582021-11-30 06:19:175983 popup = static_cast<WebContentsImpl*>(popup_observer.GetWebContents());
Lukasz Anforowicz23fbe112021-04-01 14:09:025984 }
5985 WaitForLoadStop(popup);
5986
5987 // Verify that we are at the initial empty document.
Rakina Zata Amni46087a12022-11-11 08:28:385988 EXPECT_EQ(1, popup->GetController().GetEntryCount());
5989 EXPECT_TRUE(popup->GetController().GetLastCommittedEntry()->IsInitialEntry());
Rakina Zata Amniafd3c6582021-11-30 06:19:175990 EXPECT_TRUE(
5991 popup->GetPrimaryFrameTree().root()->is_on_initial_empty_document());
Lukasz Anforowicz23fbe112021-04-01 14:09:025992
5993 // Verify that the `popup` is at "about:blank", with expected origin, with
5994 // working `document.cookie`, and with working subresource loads.
Dave Tapuska327c06c92022-06-13 20:31:515995 VerifyResultsOfAboutBlankNavigation(
5996 popup->GetPrimaryMainFrame(),
5997 shell()->web_contents()->GetPrimaryMainFrame());
Lukasz Anforowicz23fbe112021-04-01 14:09:025998}
5999
6000// See the doc comment for the
6001// URLLoaderFactoryInInitialEmptyDoc_NewPopupToEmptyUrl test case.
6002IN_PROC_BROWSER_TEST_F(SubresourceLoadingTest,
6003 URLLoaderFactoryInInitialEmptyDoc_NewPopupToAboutBlank) {
6004 GURL opener_url(embedded_test_server()->GetURL("/title1.html"));
6005 EXPECT_TRUE(NavigateToURL(shell(), opener_url));
6006
Rakina Zata Amniafd3c6582021-11-30 06:19:176007 WebContentsImpl* popup = nullptr;
Lukasz Anforowicz23fbe112021-04-01 14:09:026008 {
6009 WebContentsAddedObserver popup_observer;
6010 ASSERT_TRUE(ExecJs(shell(), "window.open('about:blank', '_blank')"));
Rakina Zata Amniafd3c6582021-11-30 06:19:176011 popup = static_cast<WebContentsImpl*>(popup_observer.GetWebContents());
Lukasz Anforowicz23fbe112021-04-01 14:09:026012 }
6013 WaitForLoadStop(popup);
6014
Rakina Zata Amniafd3c6582021-11-30 06:19:176015 // Verify that we are at the synchronously committed about:blank document.
Lukasz Anforowicz23fbe112021-04-01 14:09:026016 EXPECT_EQ(1, popup->GetController().GetEntryCount());
Rakina Zata Amni46087a12022-11-11 08:28:386017 EXPECT_TRUE(popup->GetController().GetLastCommittedEntry()->IsInitialEntry());
Rakina Zata Amniafd3c6582021-11-30 06:19:176018 EXPECT_TRUE(
6019 popup->GetPrimaryFrameTree().root()->is_on_initial_empty_document());
Lukasz Anforowicz23fbe112021-04-01 14:09:026020
6021 // Verify other about:blank things.
Dave Tapuska327c06c92022-06-13 20:31:516022 VerifyResultsOfAboutBlankNavigation(
6023 popup->GetPrimaryMainFrame(),
6024 shell()->web_contents()->GetPrimaryMainFrame());
Lukasz Anforowicz94f7258f2021-03-25 00:01:146025}
6026
Lukasz Anforowicz292ff5f2022-10-05 18:30:586027// The test below verifies that error pages have a functional URLLoaderFactory.
6028IN_PROC_BROWSER_TEST_F(SubresourceLoadingTest, URLLoaderFactoryInErrorPage) {
6029 GURL error_url(embedded_test_server()->GetURL("/close-socket"));
6030 EXPECT_FALSE(NavigateToURL(shell(), error_url));
6031 VerifyImageSubresourceLoads(shell()->web_contents()->GetPrimaryMainFrame());
6032}
6033
Lukasz Anforowicz94f7258f2021-03-25 00:01:146034// The test below verifies that an initial empty document has a functional
6035// URLLoaderFactory.
6036IN_PROC_BROWSER_TEST_F(
Lukasz Anforowicz23fbe112021-04-01 14:09:026037 SubresourceLoadingTest,
Lukasz Anforowicz7624ae22021-04-14 16:17:506038 URLLoaderFactoryInInitialEmptyDoc_HungNavigationInSubframe) {
Lukasz Anforowicz94f7258f2021-03-25 00:01:146039 ASSERT_TRUE(NavigateToURL(
6040 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
6041
6042 // Add a subframe that will never commit a navigation (i.e. that will be stuck
6043 // on the initial empty document).
6044 const GURL hung_url = embedded_test_server()->GetURL("a.com", "/hung");
6045 ASSERT_TRUE(
6046 ExecJs(shell(), JsReplace(R"(ifr = document.createElement('iframe');
6047 ifr.src = $1;
6048 document.body.appendChild(ifr); )",
6049 hung_url)));
6050
Lukasz Anforowicz23fbe112021-04-01 14:09:026051 // No process swaps are expected before ReadyToCommit (which will never happen
6052 // for a navigation to "/hung"). This test assertion double-checks that the
6053 // test will cover inheriting URLLoaderFactory from the creator/opener/parent
6054 // frame.
Dave Tapuska327c06c92022-06-13 20:31:516055 RenderFrameHost* main_frame = shell()->web_contents()->GetPrimaryMainFrame();
Lukasz Anforowicz23fbe112021-04-01 14:09:026056 RenderFrameHost* subframe = ChildFrameAt(main_frame, 0);
Emily Andrewsd15fd762024-12-10 20:41:546057 EXPECT_EQ(main_frame->GetProcess()->GetDeprecatedID(),
6058 subframe->GetProcess()->GetDeprecatedID());
Lukasz Anforowicz23fbe112021-04-01 14:09:026059
Lukasz Anforowicz94f7258f2021-03-25 00:01:146060 // Ask the parent to script the same-origin subframe and trigger some HTTP
6061 // subresource loads within the subframe.
6062 //
6063 // This tests the functionality of the URLLoaderFactory that gets used by the
6064 // initial empty document. In this test, the `request_initiator` will be a
6065 // non-opaque origin - it requires that the URLLoaderFactory will have a
6066 // matching `request_initiator_origin_lock` (e.g. inherited from the parent).
Lukasz Anforowicz23fbe112021-04-01 14:09:026067 VerifyImageSubresourceLoads(shell(), "ifr.contentDocument");
Lukasz Anforowicz94f7258f2021-03-25 00:01:146068}
6069
6070// The test below verifies that an initial empty document has a functional
6071// URLLoaderFactory.
6072IN_PROC_BROWSER_TEST_F(
Lukasz Anforowicz23fbe112021-04-01 14:09:026073 SubresourceLoadingTest,
Lukasz Anforowicz7624ae22021-04-14 16:17:506074 URLLoaderFactoryInInitialEmptyDoc_HungNavigationInPopup) {
Lukasz Anforowicz94f7258f2021-03-25 00:01:146075 ASSERT_TRUE(NavigateToURL(
6076 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
6077
6078 // Open a popup window that will never commit a navigation (i.e. that will be
6079 // stuck on the initial empty document).
6080 const GURL hung_url = embedded_test_server()->GetURL("a.com", "/hung");
Lukasz Anforowicz23fbe112021-04-01 14:09:026081 WebContents* popup = nullptr;
6082 {
6083 WebContentsAddedObserver popup_observer;
6084 ASSERT_TRUE(
6085 ExecJs(shell(), JsReplace("popup = window.open($1)", hung_url)));
6086 popup = popup_observer.GetWebContents();
6087 }
6088
6089 // No process swaps are expected before ReadyToCommit (which will never happen
6090 // for a navigation to "/hung"). This test assertion double-checks that the
6091 // test will cover inheriting URLLoaderFactory from the creator/opener/parent
6092 // frame.
Dave Tapuska327c06c92022-06-13 20:31:516093 RenderFrameHost* opener_frame =
6094 shell()->web_contents()->GetPrimaryMainFrame();
6095 RenderFrameHost* popup_frame = popup->GetPrimaryMainFrame();
Emily Andrewsd15fd762024-12-10 20:41:546096 EXPECT_EQ(opener_frame->GetProcess()->GetDeprecatedID(),
6097 popup_frame->GetProcess()->GetDeprecatedID());
Lukasz Anforowicz94f7258f2021-03-25 00:01:146098
6099 // Ask the opener to script the (same-origin) popup window and trigger some
6100 // HTTP subresource loads within the popup.
6101 //
6102 // This tests the functionality of the URLLoaderFactory that gets used by the
6103 // initial empty document. In this test, the `request_initiator` will be a
6104 // non-opaque origin - it requires that the URLLoaderFactory will have a
6105 // matching `request_initiator_origin_lock` (e.g. inherited from the opener).
Lukasz Anforowicz23fbe112021-04-01 14:09:026106 VerifyImageSubresourceLoads(shell(), "popup.document");
6107
Alison Gale770f3fc2024-04-27 00:39:586108 // TODO(crbug.com/40758605): Crash recovery doesn't work when there is
Lukasz Anforowicz23fbe112021-04-01 14:09:026109 // no opener.
6110 DontTestNetworkServiceCrashes();
6111 // Test again after closing the opener..
6112 shell()->Close();
6113 VerifyImageSubresourceLoads(popup);
6114}
6115
6116// The test below verifies that an initial empty document has a functional
6117// URLLoaderFactory. The ...WithClearedOpener testcase is a regression test for
6118// https://crbug.com/1191203.
6119IN_PROC_BROWSER_TEST_F(
6120 SubresourceLoadingTest,
Lukasz Anforowicz7624ae22021-04-14 16:17:506121 URLLoaderFactoryInInitialEmptyDoc_HungNavigationInPopupWithClearedOpener) {
Lukasz Anforowicz23fbe112021-04-01 14:09:026122 ASSERT_TRUE(NavigateToURL(
6123 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
6124
6125 // Open a new window that will never commit a navigation (i.e. that will be
6126 // stuck on the initial empty document). Clearing of `popup.opener` tests if
6127 // inheriting of URLLoaderFactory from the opener will work when the opener
6128 // has been cleared in DOM/Javascript.
6129 const GURL hung_url = embedded_test_server()->GetURL("a.com", "/hung");
6130 const char kScriptTemplate[] = R"(
6131 popup = window.open($1);
6132 popup.opener = null;
6133 )";
6134 content::WebContents* popup = nullptr;
6135 {
6136 WebContentsAddedObserver popup_observer;
6137 ASSERT_TRUE(ExecJs(shell(), JsReplace(kScriptTemplate, hung_url)));
6138 popup = popup_observer.GetWebContents();
6139 }
6140
6141 // No process swaps are expected before ReadyToCommit (which will never happen
6142 // for a navigation to "/hung"). This test assertion double-checks that the
6143 // test will cover inheriting URLLoaderFactory from the creator/opener/parent
6144 // frame. This differentiates the test from the "noopener" case covered in
6145 // another testcase.
Dave Tapuska327c06c92022-06-13 20:31:516146 RenderFrameHost* opener_frame =
6147 shell()->web_contents()->GetPrimaryMainFrame();
6148 RenderFrameHost* popup_frame = popup->GetPrimaryMainFrame();
Emily Andrewsd15fd762024-12-10 20:41:546149 EXPECT_EQ(opener_frame->GetProcess()->GetDeprecatedID(),
6150 popup_frame->GetProcess()->GetDeprecatedID());
Lukasz Anforowicz23fbe112021-04-01 14:09:026151
6152 // Double-check that the popup didn't commit any navigation and that it has
6153 // an the same origin as the initial opener.
Dave Tapuska327c06c92022-06-13 20:31:516154 EXPECT_EQ(GURL(), popup->GetPrimaryMainFrame()->GetLastCommittedURL());
Lukasz Anforowicz23fbe112021-04-01 14:09:026155 EXPECT_NE("null", EvalJs(popup, "window.origin"));
6156 EXPECT_EQ(shell()
6157 ->web_contents()
Dave Tapuska327c06c92022-06-13 20:31:516158 ->GetPrimaryMainFrame()
Lukasz Anforowicz23fbe112021-04-01 14:09:026159 ->GetLastCommittedOrigin()
6160 .Serialize(),
6161 EvalJs(popup, "window.origin"));
6162
6163 // Use the parent frame's `popup` reference to script the same-origin popup
6164 // window and trigger some HTTP subresource loads within the popup.
6165 //
6166 // This tests the functionality of the URLLoaderFactory that gets used by the
6167 // initial empty document. In this test, the `request_initiator` will be a
6168 // non-opaque origin - it requires that the URLLoaderFactory will have a
6169 // matching `request_initiator_origin_lock` (e.g. inherited from the opener).
6170 VerifyImageSubresourceLoads(popup);
6171
Alison Gale770f3fc2024-04-27 00:39:586172 // TODO(crbug.com/40758605): Crash recovery doesn't work when there is
Lukasz Anforowicz23fbe112021-04-01 14:09:026173 // no opener.
6174 DontTestNetworkServiceCrashes();
6175 // Test again after closing the opener..
6176 shell()->Close();
6177 VerifyImageSubresourceLoads(popup);
Lukasz Anforowicz94f7258f2021-03-25 00:01:146178}
6179
6180// The test below verifies that an initial empty document has a functional
6181// URLLoaderFactory.
Lukasz Anforowicz23fbe112021-04-01 14:09:026182IN_PROC_BROWSER_TEST_F(SubresourceLoadingTest,
Lukasz Anforowicz94f7258f2021-03-25 00:01:146183 URLLoaderFactoryInInitialEmptyDoc_204NoOpenerPopup) {
6184 ASSERT_TRUE(NavigateToURL(
6185 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
6186
6187 // Open a new window by following a no-opener link to /nocontent (204).
6188 const GURL no_content_url =
6189 embedded_test_server()->GetURL("a.com", "/nocontent");
6190 const char kScriptTemplate[] = R"(
6191 let anchor = document.createElement('a');
6192 anchor.href = $1;
6193 anchor.rel = 'noopener';
6194 anchor.target = '_blank';
6195 anchor.innerText = 'test link';
6196 document.body.appendChild(anchor);
6197 anchor.click();
6198 )";
6199 content::WebContents* popup = nullptr;
6200 {
6201 WebContentsAddedObserver popup_observer;
6202 ASSERT_TRUE(ExecJs(shell(), JsReplace(kScriptTemplate, no_content_url)));
6203 popup = popup_observer.GetWebContents();
6204 }
6205 WaitForLoadStop(popup);
6206
6207 // Double-check that the `popup` didn't commit any navigation and that it has
6208 // an opaque origin.
Dave Tapuska327c06c92022-06-13 20:31:516209 EXPECT_EQ(GURL(), popup->GetPrimaryMainFrame()->GetLastCommittedURL());
Lukasz Anforowicz94f7258f2021-03-25 00:01:146210 EXPECT_EQ("null", EvalJs(popup, "window.origin"));
6211
Lukasz Anforowicz23fbe112021-04-01 14:09:026212 // Process swap is expected because of 'noopener'. This test assertion
6213 // double-checks that in the test it is not possible to inheriting
6214 // URLLoaderFactory from the creator/opener/parent frame (because the popup is
6215 // in another process).
Dave Tapuska327c06c92022-06-13 20:31:516216 RenderFrameHost* opener_frame =
6217 shell()->web_contents()->GetPrimaryMainFrame();
6218 RenderFrameHost* popup_frame = popup->GetPrimaryMainFrame();
Emily Andrewsd15fd762024-12-10 20:41:546219 EXPECT_NE(opener_frame->GetProcess()->GetDeprecatedID(),
6220 popup_frame->GetProcess()->GetDeprecatedID());
Lukasz Anforowicz23fbe112021-04-01 14:09:026221
Lukasz Anforowicz94f7258f2021-03-25 00:01:146222 // Inject Javascript that triggers some subresource loads over HTTP.
6223 //
6224 // To some extent, this simulates an ability of 1) Android WebView (see
6225 // https://crbug.com/1189838) and 2) Chrome Extensions, to inject Javascript
6226 // into an initial empty document (even when no web/renderer content has
6227 // access to the document).
6228 //
6229 // This tests the functionality of the URLLoaderFactory that gets used by the
6230 // initial empty document. In this test, the `request_initiator` will be an
6231 // opaque, unique origin (since nothing has committed yet) and will be
6232 // compatible with `request_initiator_origin_lock` of the URLLoaderFactory.
Lukasz Anforowicz23fbe112021-04-01 14:09:026233 VerifyImageSubresourceLoads(popup);
6234}
6235
6236// The test below verifies that an initial empty document has a functional
6237// URLLoaderFactory.
Lukasz Anforowicz7624ae22021-04-14 16:17:506238IN_PROC_BROWSER_TEST_F(
6239 SubresourceLoadingTest,
6240 URLLoaderFactoryInInitialEmptyDoc_HungNavigationInNewWindow) {
Lukasz Anforowicz23fbe112021-04-01 14:09:026241 // Open a new shell, starting at the "/hung" URL.
6242 const GURL hung_url = embedded_test_server()->GetURL("a.com", "/hung");
6243 Shell* new_shell =
6244 Shell::CreateNewWindow(shell()->web_contents()->GetBrowserContext(),
6245 hung_url, nullptr, gfx::Size());
6246
6247 // Wait until the renderer process launches (this will flush the CreateView
6248 // IPC and make sure that ExecJs and EvalJs are able to work).
Dave Tapuska327c06c92022-06-13 20:31:516249 RenderFrameHost* main_frame =
6250 new_shell->web_contents()->GetPrimaryMainFrame();
Lukasz Anforowicz23fbe112021-04-01 14:09:026251 {
6252 RenderProcessHostWatcher process_watcher(
6253 main_frame->GetProcess(),
6254 RenderProcessHostWatcher::WATCH_FOR_PROCESS_READY);
6255 process_watcher.Wait();
6256 }
6257
6258 // Double-check that the new shell didn't commit any navigation and that it
6259 // has an opaque origin.
Rakina Zata Amni46087a12022-11-11 08:28:386260 EXPECT_EQ(1, new_shell->web_contents()->GetController().GetEntryCount());
6261 EXPECT_TRUE(new_shell->web_contents()
6262 ->GetController()
6263 .GetLastCommittedEntry()
6264 ->IsInitialEntry());
Lukasz Anforowicz23fbe112021-04-01 14:09:026265 EXPECT_EQ(GURL(), main_frame->GetLastCommittedURL());
6266 EXPECT_EQ("null", EvalJs(main_frame, "window.origin"));
6267
Lukasz Anforowicz23fbe112021-04-01 14:09:026268 // Inject Javascript that triggers some subresource loads over HTTP.
6269 //
6270 // To some extent, this simulates an ability of 1) Android WebView (see
6271 // https://crbug.com/1189838) and 2) Chrome Extensions, to inject Javascript
6272 // into an initial empty document (even when no web/renderer content has
6273 // access to the document).
6274 //
6275 // This tests the functionality of the URLLoaderFactory that gets used by the
6276 // initial empty document. In this test, the `request_initiator` will be an
6277 // opaque, unique origin (since nothing has committed yet) and will be
6278 // compatible with `request_initiator_origin_lock` of the URLLoaderFactory.
6279 VerifyImageSubresourceLoads(main_frame);
Lukasz Anforowicze9db2652021-01-27 00:24:126280}
6281
Daniel Chengd08a43a2023-03-16 05:10:506282namespace {
6283
6284struct Result {
6285 GURL url;
Arthur Sonzognic686e8f2024-01-11 08:36:376286 std::optional<url::Origin> origin;
Daniel Chengd08a43a2023-03-16 05:10:506287 bool committed;
6288};
6289
6290class NavigationLogger : public WebContentsObserver {
Daniel Chengc3d1e8d2021-06-23 02:11:456291 public:
Daniel Chengd08a43a2023-03-16 05:10:506292 explicit NavigationLogger(WebContents* contents)
6293 : WebContentsObserver(contents) {}
6294
6295 // WebContentsObserver overrides:
6296 void DidFinishNavigation(NavigationHandle* handle) override {
Daniel Chengfa8ffcf2023-05-04 05:43:466297 if (handle->HasCommitted()) {
6298 EXPECT_EQ(handle->GetRenderFrameHost()->GetLastCommittedURL(),
6299 handle->GetURL());
6300 RenderFrameHost* rfh = handle->GetRenderFrameHost();
6301 results_.push_back({.url = rfh->GetLastCommittedURL(),
6302 .origin = rfh->GetLastCommittedOrigin(),
6303 .committed = true});
6304 } else {
6305 results_.push_back({.url = handle->GetURL(), .committed = false});
6306 }
Daniel Chengd08a43a2023-03-16 05:10:506307 }
6308
6309 const std::vector<Result>& results() const { return results_; }
6310
6311 private:
6312 std::vector<Result> results_;
6313};
6314
6315} // namespace
6316
Daniel Cheng7e6fb322023-05-03 08:24:256317class UndoCommitNavigationBrowserTest : public NavigationBrowserTest {
6318 public:
6319 UndoCommitNavigationBrowserTest() {
6320 std::map<std::string, std::string> parameters = {
Rakina Zata Amnifdd28102023-05-26 08:44:226321 {"queueing_level", "none"},
Daniel Cheng7e6fb322023-05-03 08:24:256322 };
Rakina Zata Amni5cd16d22023-06-05 06:11:286323 // Note that RenderDocument needs to be disabled so that it won't enable
6324 // navigation queueing automatically.
6325 feature_list_.InitWithFeaturesAndParameters(
6326 /*enabled_features=*/{{features::kQueueNavigationsWhileWaitingForCommit,
6327 parameters}},
6328 /*disabled_features=*/{features::kRenderDocument});
Daniel Cheng7e6fb322023-05-03 08:24:256329 }
6330
6331 void SetUpOnMainThread() override {
6332 // These navigation tests require full site isolation since they test races
6333 // with committing a navigation in a speculative RenderFrameHost..
6334 if (!AreAllSitesIsolatedForTesting()) {
6335 GTEST_SKIP() << "Site isolation is not enabled!";
6336 }
6337
6338 NavigationBrowserTest::SetUpOnMainThread();
6339 }
6340
6341 void SetUpCommandLine(base::CommandLine* command_line) override {
6342 NavigationBrowserTest::SetUpCommandLine(command_line);
6343
6344 // PerformanceManager maintains its own parallel frame tree and has
6345 // sometimes been confused by things like `UndoCommitNavigation()`.
6346 // Force-enable it for test coverage; otherwise, by default,
6347 // PerformanceManager uses the dummy implementation.
6348 //
Alison Gale81f4f2c72024-04-22 19:33:316349 // TODO(crbug.com/40187286): Enable this by default in content_shell.
Daniel Cheng7e6fb322023-05-03 08:24:256350 command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
6351 "PerformanceManagerInstrumentation");
6352 }
6353
6354 private:
6355 base::test::ScopedFeatureList feature_list_;
6356};
6357
Daniel Cheng406fbd52023-05-09 04:58:246358// A helper that invokes `functor` on the next `DidStartNavigation()`.
6359template <typename F>
6360void OnNextDidStartNavigation(WebContents* web_contents, F&& functor) {
6361 class Observer : public WebContentsObserver {
6362 public:
6363 using Callback = base::OnceCallback<void(NavigationHandle*)>;
6364
6365 explicit Observer(WebContents* web_contents, Callback callback)
6366 : WebContentsObserver(web_contents), callback_(std::move(callback)) {}
6367
6368 // WebContentsObserver overrides:
6369 void DidStartNavigation(NavigationHandle* handle) override {
6370 std::move(callback_).Run(handle);
6371 delete this;
6372 }
6373
6374 private:
6375 Callback callback_;
6376 };
6377
6378 new Observer(web_contents,
6379 base::BindLambdaForTesting(std::forward<F>(functor)));
6380}
6381
Daniel Cheng7e6fb322023-05-03 08:24:256382IN_PROC_BROWSER_TEST_F(UndoCommitNavigationBrowserTest,
6383 PerformanceManagerFrameTreeConsistency) {
6384 // PerformanceManager reports when a remote frame is attached to a local
6385 // parent, and it was previously getting confused by the fact that a
6386 // `blink::RemoteFrame` with matching RemoteFrameTokens was being reported as
6387 // attached twice: once by the initial page loaded in the next statement, and
6388 // the next when the browser needs to send a `UndoCommitNavigation()` to the
6389 // a.com renderer.
6390 ASSERT_TRUE(NavigateToURL(
6391 shell(), embedded_test_server()->GetURL(
6392 "a.com", "/cross_site_iframe_factory.html?a(b)")));
6393
6394 WebContentsImpl* web_contents =
6395 static_cast<WebContentsImpl*>(shell()->web_contents());
6396 FrameTreeNode* first_subframe_node =
6397 web_contents->GetPrimaryMainFrame()->child_at(0);
6398 RenderProcessHost* const a_com_render_process_host =
6399 web_contents->GetPrimaryFrameTree()
6400 .root()
6401 ->render_manager()
6402 ->current_frame_host()
6403 ->GetProcess();
6404
6405 NavigationLogger logger(web_contents);
6406
6407 // Start a navigation that will create a speculative RFH in the existing
6408 // render process for a.com.
6409 const GURL infinitely_loading_url =
6410 embedded_test_server()->GetURL("a.com", "/infinitely_loading_image.html");
Jiacheng Guo4bdd0be2024-06-11 23:35:216411 SpeculativeRenderFrameHostObserver rfh_observer(web_contents,
6412 infinitely_loading_url);
Daniel Cheng7e6fb322023-05-03 08:24:256413 ASSERT_TRUE(BeginNavigateToURLFromRenderer(first_subframe_node,
6414 infinitely_loading_url));
Jiacheng Guo4bdd0be2024-06-11 23:35:216415 rfh_observer.Wait();
Daniel Cheng7e6fb322023-05-03 08:24:256416
6417 // Ensure the speculative RFH is in the expected process.
6418 RenderFrameHostImpl* speculative_render_frame_host =
6419 first_subframe_node->render_manager()->speculative_frame_host();
6420 ASSERT_TRUE(speculative_render_frame_host);
6421 EXPECT_EQ(a_com_render_process_host,
6422 speculative_render_frame_host->GetProcess());
6423
Daniel Cheng406fbd52023-05-09 04:58:246424 // Pause (and ignore) the next `DidCommitProvisionalLoad()` for a.com.
6425 CommitNavigationPauser commit_pauser(speculative_render_frame_host);
6426 commit_pauser.WaitForCommitAndPause();
Daniel Cheng7e6fb322023-05-03 08:24:256427
6428 // Update the id attribute to exercise a PerformanceManager-specific code
6429 // path: when the renderer swaps in a `blink::RemoteFrame` to undo the
6430 // `CommitNavigation()`, it will report the iframe attribution data again.
6431 // PerformanceManager should not complain that V8ContextTracker already has
6432 // the iframe attribution data, nor should it update the iframe attribution
6433 // data, to preserve existing behavior (unfortunately, the latter part is not
6434 // really tested in this browser test).
6435 EXPECT_TRUE(ExecJs(web_contents,
6436 "document.querySelector('iframe').id = 'new-name';"));
6437
Daniel Cheng406fbd52023-05-09 04:58:246438 // Now begin a new navigation to c.com while the previous a.com navigation
6439 // above is paused in the pending commit state.
6440 const GURL final_url =
6441 embedded_test_server()->GetURL("c.com", "/title1.html");
6442 ASSERT_TRUE(BeginNavigateToURLFromRenderer(first_subframe_node, final_url));
6443
Daniel Cheng7e6fb322023-05-03 08:24:256444 EXPECT_TRUE(WaitForLoadStop(web_contents));
6445 EXPECT_EQ(final_url, first_subframe_node->render_manager()
6446 ->current_frame_host()
6447 ->GetLastCommittedURL());
6448
6449 auto results = logger.results();
6450 ASSERT_EQ(2u, results.size());
Daniel Cheng406fbd52023-05-09 04:58:246451 // This test always uses UndoCommitNavigation, so navigation corresponding to
6452 // the paused commit should never commit.
Daniel Cheng7e6fb322023-05-03 08:24:256453 EXPECT_FALSE(results[0].committed);
Arthur Sonzognic686e8f2024-01-11 08:36:376454 EXPECT_EQ(std::nullopt, results[0].origin);
Daniel Cheng7e6fb322023-05-03 08:24:256455 EXPECT_EQ(infinitely_loading_url, results[0].url);
6456 EXPECT_TRUE(results[1].committed);
Daniel Chengfa8ffcf2023-05-04 05:43:466457 EXPECT_EQ(embedded_test_server()->GetOrigin("c.com"), results[1].origin);
Daniel Cheng7e6fb322023-05-03 08:24:256458 EXPECT_EQ(final_url, results[1].url);
6459}
6460
Daniel Cheng557868f2023-07-28 00:10:206461class ResumeCommitClosureSetWaiter {
6462 public:
6463 explicit ResumeCommitClosureSetWaiter(NavigationHandle* handle) {
6464 // `Helper` "observes" the set via its own destruction, since the navigation
6465 // code setting a resume commit closure will replace the testing one
6466 // installed here.
6467 NavigationRequest::From(handle)->set_resume_commit_closure(
6468 base::DoNothingWithBoundArgs(std::make_unique<Helper>(loop_)));
6469 }
6470
6471 void Wait() { loop_.Run(); }
6472
6473 private:
6474 class Helper {
6475 public:
6476 explicit Helper(base::RunLoop& loop) : loop_(loop) {}
6477
6478 ~Helper() { loop_->Quit(); }
6479
6480 private:
6481 raw_ref<base::RunLoop> loop_;
6482 };
6483
6484 base::RunLoop loop_;
6485};
6486
6487class NavigationQueueingBrowserTest : public NavigationBrowserTest {
6488 public:
6489 NavigationQueueingBrowserTest() {
6490 feature_list_.InitAndEnableFeatureWithParameters(
6491 features::kQueueNavigationsWhileWaitingForCommit,
6492 {{"queueing_level", "full"}});
6493 }
6494
6495 void SetUpOnMainThread() override {
6496 // These navigation tests require full site isolation since they test races
6497 // with committing a navigation in a speculative RenderFrameHost.
6498 if (!AreAllSitesIsolatedForTesting()) {
6499 GTEST_SKIP();
6500 }
6501
6502 NavigationBrowserTest::SetUpOnMainThread();
6503 }
6504
6505 const base::HistogramTester& histogram_tester() const {
6506 return histogram_tester_;
6507 }
6508
6509 private:
6510 base::test::ScopedFeatureList feature_list_;
6511 base::HistogramTester histogram_tester_;
6512};
6513
6514IN_PROC_BROWSER_TEST_F(NavigationQueueingBrowserTest, Regular) {
6515 ASSERT_TRUE(NavigateToURL(
6516 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
6517
6518 NavigationLogger logger(shell()->web_contents());
6519
6520 // Start a navigation that will create a speculative RFH.
6521 const GURL infinitely_loading_url =
6522 embedded_test_server()->GetURL("b.com", "/infinitely_loading_image.html");
Jiacheng Guo4bdd0be2024-06-11 23:35:216523 SpeculativeRenderFrameHostObserver rfh_observer(shell()->web_contents(),
6524 infinitely_loading_url);
Daniel Cheng557868f2023-07-28 00:10:206525 ASSERT_TRUE(BeginNavigateToURLFromRenderer(shell(), infinitely_loading_url));
Jiacheng Guo4bdd0be2024-06-11 23:35:216526 rfh_observer.Wait();
Daniel Cheng557868f2023-07-28 00:10:206527
6528 WebContentsImpl* web_contents =
6529 static_cast<WebContentsImpl*>(shell()->web_contents());
6530 RenderFrameHostImpl* speculative_render_frame_host =
6531 web_contents->GetPrimaryFrameTree()
6532 .root()
6533 ->render_manager()
6534 ->speculative_frame_host();
6535 ASSERT_TRUE(speculative_render_frame_host);
6536
6537 // Pause the next `DidCommitProvisionalLoad()` for b.com.
6538 CommitNavigationPauser commit_pauser(speculative_render_frame_host);
6539 commit_pauser.WaitForCommitAndPause();
6540
6541 // Now begin a new navigation to c.com while the previous b.com navigation
6542 // above is paused in the pending commit state.
6543 {
6544 const GURL next_url =
6545 embedded_test_server()->GetURL("c.com", "/title1.html");
6546
Arthur Sonzognic686e8f2024-01-11 08:36:376547 std::optional<ResumeCommitClosureSetWaiter>
Daniel Cheng557868f2023-07-28 00:10:206548 resume_commit_closure_set_waiter;
6549 OnNextDidStartNavigation(web_contents, [&](NavigationHandle* handle) {
6550 resume_commit_closure_set_waiter.emplace(handle);
6551 });
6552 ASSERT_TRUE(BeginNavigateToURLFromRenderer(shell(), next_url));
6553 resume_commit_closure_set_waiter->Wait();
6554 }
6555
6556 // Cancel the c.com navigation that was previously queued and (nearly) ready
6557 // for commit, to make sure one pending commit navigation that causes multiple
6558 // subsequent navigations to queue is correctly counted in the metrics.
6559 const GURL final_url =
6560 embedded_test_server()->GetURL("d.com", "/title1.html");
Arthur Sonzognic686e8f2024-01-11 08:36:376561 std::optional<ResumeCommitClosureSetWaiter> resume_commit_closure_set_waiter;
Daniel Cheng557868f2023-07-28 00:10:206562 OnNextDidStartNavigation(web_contents, [&](NavigationHandle* handle) {
6563 resume_commit_closure_set_waiter.emplace(handle);
6564 });
6565 ASSERT_TRUE(BeginNavigateToURLFromRenderer(shell(), final_url));
6566 resume_commit_closure_set_waiter->Wait();
6567
6568 commit_pauser.ResumePausedCommit();
6569
6570 EXPECT_TRUE(WaitForLoadStop(web_contents));
6571 EXPECT_EQ(final_url, web_contents->GetLastCommittedURL());
6572
6573 // Both the b.com commit and the d.com commit should record the PendingCommit
6574 // time.
6575 histogram_tester().ExpectTotalCount(
6576 "Navigation.PendingCommit.Duration.Regular", 2);
6577 // The d.com navigation should not have blocked any navigation requests from
6578 // making progress.
6579 histogram_tester().ExpectBucketCount(
6580 "Navigation.PendingCommit.DidBlockGetFrameHostForNavigation.Regular",
6581 false, 1);
6582 // But the b.com navigation blocked c.com and d.com.
6583 histogram_tester().ExpectBucketCount(
6584 "Navigation.PendingCommit.DidBlockGetFrameHostForNavigation.Regular",
6585 true, 1);
Jiacheng Guo297820872025-02-17 04:49:376586 if (base::FeatureList::IsEnabled(features::kDeferSpeculativeRFHCreation)) {
6587 // For 2 blocked navigations, 2 total blocks are expected when trying to
6588 // pick a final RenderFrameHost to commit the navigation. The attempt to
6589 // create a RenderFrameHost when starting the navigation will be skipped.
6590 histogram_tester().ExpectBucketCount(
6591 "Navigation.PendingCommit.BlockedCount.Regular", 2, 1);
6592 } else {
6593 // For 2 blocked navigations, 4 total blocks are expected: 2 when trying to
6594 // assign a RenderFrameHost when starting a navigation, and 2 when trying to
6595 // pick a final RenderFrameHost to commit the navigation.
6596 histogram_tester().ExpectBucketCount(
6597 "Navigation.PendingCommit.BlockedCount.Regular", 4, 1);
6598 }
Daniel Cheng557868f2023-07-28 00:10:206599 histogram_tester().ExpectBucketCount(
6600 "Navigation.PendingCommit.BlockedCommitCount.Regular", 2, 1);
6601}
6602
Daniel Chengd08a43a2023-03-16 05:10:506603class CommitNavigationRaceBrowserTest
6604 : public NavigationBrowserTest,
6605 public ::testing::WithParamInterface<bool> {
6606 public:
6607 CommitNavigationRaceBrowserTest() {
6608 std::map<std::string, std::string> parameters = {
Rakina Zata Amnifdd28102023-05-26 08:44:226609 {"queueing_level", GetParam() ? "full" : "none"},
Daniel Chengd08a43a2023-03-16 05:10:506610 };
6611 feature_list_.InitAndEnableFeatureWithParameters(
Rakina Zata Amnidffbe5b2023-05-23 10:58:086612 features::kQueueNavigationsWhileWaitingForCommit, parameters);
Daniel Chengd08a43a2023-03-16 05:10:506613 }
6614
6615 void SetUpOnMainThread() override {
6616 // These navigation tests require full site isolation since they test races
6617 // with committing a navigation in a speculative RenderFrameHost..
6618 if (!AreAllSitesIsolatedForTesting()) {
6619 GTEST_SKIP();
6620 }
6621
6622 NavigationBrowserTest::SetUpOnMainThread();
6623 }
6624
Daniel Chengd08a43a2023-03-16 05:10:506625 static std::string DescribeParams(
6626 const testing::TestParamInfo<ParamType>& info) {
Daniel Cheng32906ab2023-05-07 14:38:426627 return info.param ? "NavigationQueueing" : "UndoCommitNavigation";
Daniel Chengd08a43a2023-03-16 05:10:506628 }
6629
6630 private:
6631 base::test::ScopedFeatureList feature_list_;
Daniel Chengc3d1e8d2021-06-23 02:11:456632};
6633
Daniel Cheng066698d2024-04-18 21:24:556634// Test for https://crbug.com/40187807 and https://crbug.com/332746903.
6635//
6636// Ensure that racing a navigation commit in a speculative/provisional child
6637// frame in render process B with a detach IPC from render process A (i.e. the
6638// child frame's parent is in render process A and has removed the frame owner
6639// element—e.g. <iframe>—from the DOM) does not result in the detach IPC being
6640// discarded and never received by render process B.
6641IN_PROC_BROWSER_TEST_P(CommitNavigationRaceBrowserTest,
6642 DetachAfterCommitNavigationInSubFrame) {
6643 ASSERT_TRUE(NavigateToURL(
6644 shell(), embedded_test_server()->GetURL(
6645 "a.com", "/cross_site_iframe_factory.html?a(b,a)")));
6646
6647 WebContentsImpl* const web_contents =
6648 static_cast<WebContentsImpl*>(shell()->web_contents());
6649 FrameTreeNode* const first_subframe_node =
6650 web_contents->GetPrimaryMainFrame()->child_at(0);
6651 FrameTreeNode* const second_subframe_node =
6652 web_contents->GetPrimaryMainFrame()->child_at(1);
6653 RenderProcessHost* const b_com_render_process_host =
6654 first_subframe_node->render_manager()->current_frame_host()->GetProcess();
6655
6656 // Start a navigation in the second child frame that will create a speculative
6657 // RFH in the existing render process for b.com. The first child frame is
6658 // already hosted in the render process for b.com: this is to ensure the
6659 // render process remains live even after the second child frame is detached
6660 // later in this test.
Jiacheng Guo4bdd0be2024-06-11 23:35:216661 GURL b_url = embedded_test_server()->GetURL("b.com", "/title1.html");
6662 SpeculativeRenderFrameHostObserver observer(shell()->web_contents(), b_url);
6663 ASSERT_TRUE(BeginNavigateToURLFromRenderer(second_subframe_node, b_url));
Daniel Cheng066698d2024-04-18 21:24:556664
6665 // Ensure the speculative RFH is in the expected process.
Jiacheng Guo4bdd0be2024-06-11 23:35:216666 observer.Wait();
Daniel Cheng066698d2024-04-18 21:24:556667 RenderFrameHostImpl* speculative_render_frame_host =
6668 second_subframe_node->render_manager()->speculative_frame_host();
6669 ASSERT_TRUE(speculative_render_frame_host);
6670 EXPECT_EQ(b_com_render_process_host,
6671 speculative_render_frame_host->GetProcess());
6672
6673 // Pause (and ignore) the next `DidCommitProvisionalLoad()` for b.com.
6674 CommitNavigationPauser commit_pauser(speculative_render_frame_host);
6675 commit_pauser.WaitForCommitAndPause();
6676
6677 // At this point, the b.com renderer has already committed the RenderFrame,
6678 // but on the browser side, the RenderFrameHost is still speculative.
6679
6680 // Intentionally do not wait for script completion here. This runs an event
6681 // loop that pumps incoming messages, but IPCs from b.com would be processed
6682 // out of order, since the `DidCommitProvisionalLoad()` attempt was previously
6683 // paused above.
6684 ExecuteScriptAsync(
6685 web_contents,
6686 JsReplace("document.querySelectorAll('iframe')[1].remove()"));
6687
6688 // However, since it's not possible to wait for `remove()` to take effect,
6689 // the test must cheat a little and directly call the Mojo IPC that the JS
6690 // above would eventually trigger.
6691 second_subframe_node->render_manager()->current_frame_host()->Detach();
6692
6693 EXPECT_TRUE(WaitForLoadStop(web_contents));
6694 // Validate that render process for b.com has handled the detach message for
6695 // the provisional frame that was committing. Before the fix:
6696 // - without navigation queueing, the render process for b.com still had the
6697 // proxy for the second child frame, because the browser process's request
6698 // to delete it was sent via a broken message pipe. Thus, the frame tree in
6699 // the render process for b.com incorrectly thought there were still two
6700 // child frames.
6701 // - with navigation queueing, the render process for b.com has already
6702 // committed the navigation in the second child frame, so the renderer-side
6703 // proxy has already been destroyed and replaced. However, the browser
6704 // process has not heard about the commit yet and sends a detach to the
6705 // proxy, which the renderer ignores since it no longer exists.
6706 EXPECT_EQ(1, EvalJs(first_subframe_node, "top.length"));
6707}
6708
Daniel Chengd08a43a2023-03-16 05:10:506709IN_PROC_BROWSER_TEST_P(CommitNavigationRaceBrowserTest,
Daniel Cheng406fbd52023-05-09 04:58:246710 BeginNewNavigationDuringCommitNavigationInMainFrame) {
Daniel Cheng42cb7e9f52021-02-23 01:54:456711 ASSERT_TRUE(NavigateToURL(
6712 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
6713
Daniel Cheng77ae2b02023-05-10 04:16:466714 // Prior to implementing the UndoCommitNavigation() workaround, the race
6715 // condition being tested would result in a crash in the b.com renderer. Open
6716 // another b.com window in the same browsing instance to verify that the b.com
6717 // renderer does not unexpectedly crash even if the b.com speculative
6718 // RenderFrameHost is discarded.
Daniel Cheng42cb7e9f52021-02-23 01:54:456719 ASSERT_TRUE(ExecJs(
6720 shell(), JsReplace("window.open($1)", embedded_test_server()->GetURL(
6721 "b.com", "/title1.html"))));
6722 ASSERT_EQ(2u, Shell::windows().size());
6723 WebContentsImpl* new_web_contents =
6724 static_cast<WebContentsImpl*>(Shell::windows()[1]->web_contents());
6725 WaitForLoadStop(new_web_contents);
6726 RenderProcessHost* const b_com_render_process_host =
Dave Tapuska327c06c92022-06-13 20:31:516727 new_web_contents->GetPrimaryMainFrame()->GetProcess();
Daniel Cheng42cb7e9f52021-02-23 01:54:456728
Daniel Chengd08a43a2023-03-16 05:10:506729 NavigationLogger logger(shell()->web_contents());
6730
Daniel Cheng42cb7e9f52021-02-23 01:54:456731 // Start a navigation that will create a speculative RFH in the existing
6732 // render process for b.com.
Daniel Chengd08a43a2023-03-16 05:10:506733 const GURL infinitely_loading_url =
6734 embedded_test_server()->GetURL("b.com", "/infinitely_loading_image.html");
Jiacheng Guo4bdd0be2024-06-11 23:35:216735 SpeculativeRenderFrameHostObserver rfh_observer(shell()->web_contents(),
6736 infinitely_loading_url);
Daniel Chengd08a43a2023-03-16 05:10:506737 ASSERT_TRUE(BeginNavigateToURLFromRenderer(shell(), infinitely_loading_url));
Jiacheng Guo4bdd0be2024-06-11 23:35:216738 rfh_observer.Wait();
Daniel Cheng42cb7e9f52021-02-23 01:54:456739
6740 // Ensure the speculative RFH is in the expected process (i.e. the b.com
6741 // process that was created for the navigation in the new window earlier).
6742 WebContentsImpl* web_contents =
6743 static_cast<WebContentsImpl*>(shell()->web_contents());
6744 RenderFrameHostImpl* speculative_render_frame_host =
Carlos Caballero15caeeb2021-10-27 09:57:556745 web_contents->GetPrimaryFrameTree()
6746 .root()
Daniel Cheng42cb7e9f52021-02-23 01:54:456747 ->render_manager()
6748 ->speculative_frame_host();
6749 ASSERT_TRUE(speculative_render_frame_host);
6750 EXPECT_EQ(b_com_render_process_host,
6751 speculative_render_frame_host->GetProcess());
6752
Daniel Cheng406fbd52023-05-09 04:58:246753 // Pause (and potentially ignore, if navigation queueing is disabled) the next
6754 // `DidCommitProvisionalLoad()` for b.com.
6755 CommitNavigationPauser commit_pauser(speculative_render_frame_host);
6756 commit_pauser.WaitForCommitAndPause();
6757
Arthur Sonzognic686e8f2024-01-11 08:36:376758 std::optional<ResumeCommitClosureSetWaiter> resume_commit_closure_set_waiter;
Daniel Cheng406fbd52023-05-09 04:58:246759 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
6760 // If navigation queueing is enabled, the test should verify that a resume
6761 // commit closure is actually set. Install a watcher now, before beginning
Daniel Cheng77ae2b02023-05-10 04:16:466762 // the next navigation, since the resume commit closure may be synchronously
Daniel Cheng406fbd52023-05-09 04:58:246763 // set while handling the `BeginNavigation()` IPC in the browser.
6764 OnNextDidStartNavigation(web_contents, [&](NavigationHandle* handle) {
6765 resume_commit_closure_set_waiter.emplace(handle);
6766 });
6767 }
6768
6769 // Now begin a new navigation to c.com while the previous b.com navigation
6770 // above is paused in the pending commit state.
Daniel Chengc3d1e8d2021-06-23 02:11:456771 const GURL final_url =
6772 embedded_test_server()->GetURL("c.com", "/title1.html");
Daniel Cheng406fbd52023-05-09 04:58:246773 ASSERT_TRUE(BeginNavigateToURLFromRenderer(web_contents, final_url));
6774
6775 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
6776 resume_commit_closure_set_waiter->Wait();
6777 commit_pauser.ResumePausedCommit();
6778 }
Daniel Cheng42cb7e9f52021-02-23 01:54:456779
Daniel Chengc3d1e8d2021-06-23 02:11:456780 EXPECT_TRUE(WaitForLoadStop(web_contents));
6781 EXPECT_EQ(final_url, web_contents->GetLastCommittedURL());
Daniel Chengd08a43a2023-03-16 05:10:506782
6783 auto results = logger.results();
6784 ASSERT_EQ(2u, results.size());
6785 EXPECT_EQ(infinitely_loading_url, results[0].url);
6786 // If navigation queueing is enabled, the first navigation will complete the
6787 // commit as the new navigation gets queued until the first navigation's
6788 // commit finished. If navigation queueing is disabled, the pending commit
6789 // navigation will be cancelled.
6790 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
6791 EXPECT_TRUE(results[0].committed);
Daniel Chengfa8ffcf2023-05-04 05:43:466792 EXPECT_EQ(embedded_test_server()->GetOrigin("b.com"), results[0].origin);
Daniel Chengd08a43a2023-03-16 05:10:506793 } else {
6794 EXPECT_FALSE(results[0].committed);
Arthur Sonzognic686e8f2024-01-11 08:36:376795 EXPECT_EQ(std::nullopt, results[0].origin);
Daniel Chengd08a43a2023-03-16 05:10:506796 }
6797 EXPECT_TRUE(results[1].committed);
Daniel Chengfa8ffcf2023-05-04 05:43:466798 EXPECT_EQ(embedded_test_server()->GetOrigin("c.com"), results[1].origin);
Daniel Chengd08a43a2023-03-16 05:10:506799 EXPECT_EQ(final_url, results[1].url);
Daniel Chengc3d1e8d2021-06-23 02:11:456800}
6801
Daniel Chengd08a43a2023-03-16 05:10:506802IN_PROC_BROWSER_TEST_P(CommitNavigationRaceBrowserTest,
Daniel Cheng406fbd52023-05-09 04:58:246803 BeginNewNavigationDuringCommitNavigationInSubFrame) {
Daniel Chengc3d1e8d2021-06-23 02:11:456804 ASSERT_TRUE(NavigateToURL(
6805 shell(), embedded_test_server()->GetURL(
Daniel Cheng7e6fb322023-05-03 08:24:256806 "b.com", "/cross_site_iframe_factory.html?b(a)")));
Daniel Chengc3d1e8d2021-06-23 02:11:456807
6808 WebContentsImpl* web_contents =
6809 static_cast<WebContentsImpl*>(shell()->web_contents());
6810 FrameTreeNode* first_subframe_node =
Dave Tapuska327c06c92022-06-13 20:31:516811 web_contents->GetPrimaryMainFrame()->child_at(0);
Daniel Chengfa8ffcf2023-05-04 05:43:466812 RenderProcessHost* const b_com_render_process_host =
Carlos Caballero15caeeb2021-10-27 09:57:556813 web_contents->GetPrimaryFrameTree()
6814 .root()
Daniel Chengc3d1e8d2021-06-23 02:11:456815 ->render_manager()
6816 ->current_frame_host()
6817 ->GetProcess();
6818
Daniel Chengd08a43a2023-03-16 05:10:506819 NavigationLogger logger(web_contents);
6820
Daniel Chengc3d1e8d2021-06-23 02:11:456821 // Start a navigation that will create a speculative RFH in the existing
Daniel Cheng7e6fb322023-05-03 08:24:256822 // render process for b.com.
Daniel Chengd08a43a2023-03-16 05:10:506823 const GURL infinitely_loading_url =
Daniel Cheng7e6fb322023-05-03 08:24:256824 embedded_test_server()->GetURL("b.com", "/infinitely_loading_image.html");
Jiacheng Guo4bdd0be2024-06-11 23:35:216825 SpeculativeRenderFrameHostObserver rfh_observer(shell()->web_contents(),
6826 infinitely_loading_url);
Daniel Chengd08a43a2023-03-16 05:10:506827 ASSERT_TRUE(BeginNavigateToURLFromRenderer(first_subframe_node,
6828 infinitely_loading_url));
Jiacheng Guo4bdd0be2024-06-11 23:35:216829 rfh_observer.Wait();
Daniel Chengc3d1e8d2021-06-23 02:11:456830
6831 // Ensure the speculative RFH is in the expected process.
6832 RenderFrameHostImpl* speculative_render_frame_host =
6833 first_subframe_node->render_manager()->speculative_frame_host();
6834 ASSERT_TRUE(speculative_render_frame_host);
Daniel Chengfa8ffcf2023-05-04 05:43:466835 EXPECT_EQ(b_com_render_process_host,
Daniel Chengc3d1e8d2021-06-23 02:11:456836 speculative_render_frame_host->GetProcess());
6837
Daniel Cheng406fbd52023-05-09 04:58:246838 // Pause (and potentially ignore, if navigation queueing is disabled) the next
6839 // `DidCommitProvisionalLoad()` for b.com.
6840 CommitNavigationPauser commit_pauser(speculative_render_frame_host);
6841 commit_pauser.WaitForCommitAndPause();
6842
Arthur Sonzognic686e8f2024-01-11 08:36:376843 std::optional<ResumeCommitClosureSetWaiter> resume_commit_closure_set_waiter;
Daniel Cheng406fbd52023-05-09 04:58:246844 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
6845 // If navigation queueing is enabled, the test should verify that a resume
6846 // commit closure is actually set. Install a watcher now, before beginning
Daniel Cheng77ae2b02023-05-10 04:16:466847 // the next navigation, since the resume commit closure may be synchronously
Daniel Cheng406fbd52023-05-09 04:58:246848 // set while handling the `BeginNavigation()` IPC in the browser.
6849 OnNextDidStartNavigation(web_contents, [&](NavigationHandle* handle) {
6850 resume_commit_closure_set_waiter.emplace(handle);
6851 });
6852 }
6853
6854 // Now begin a new navigation to c.com while the previous b.com navigation
6855 // above is paused in the pending commit state.
Daniel Chengc3d1e8d2021-06-23 02:11:456856 const GURL final_url =
6857 embedded_test_server()->GetURL("c.com", "/title1.html");
Daniel Cheng406fbd52023-05-09 04:58:246858 ASSERT_TRUE(BeginNavigateToURLFromRenderer(first_subframe_node, final_url));
6859
6860 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
6861 resume_commit_closure_set_waiter->Wait();
6862 commit_pauser.ResumePausedCommit();
6863 }
Daniel Chengc3d1e8d2021-06-23 02:11:456864
6865 EXPECT_TRUE(WaitForLoadStop(web_contents));
6866 EXPECT_EQ(final_url, first_subframe_node->render_manager()
6867 ->current_frame_host()
6868 ->GetLastCommittedURL());
Daniel Chengd08a43a2023-03-16 05:10:506869
6870 auto results = logger.results();
6871 ASSERT_EQ(2u, results.size());
6872 // If navigation queueing is enabled, the first navigation will complete the
6873 // commit as the new navigation gets queued until the first navigation's
6874 // commit finished. If navigation queueing is disabled, the pending commit
6875 // navigation will be cancelled.
6876 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
6877 EXPECT_TRUE(results[0].committed);
Daniel Chengfa8ffcf2023-05-04 05:43:466878 EXPECT_EQ(embedded_test_server()->GetOrigin("b.com"), results[0].origin);
Daniel Chengd08a43a2023-03-16 05:10:506879 } else {
6880 EXPECT_FALSE(results[0].committed);
Arthur Sonzognic686e8f2024-01-11 08:36:376881 EXPECT_EQ(std::nullopt, results[0].origin);
Daniel Chengd08a43a2023-03-16 05:10:506882 }
6883 EXPECT_EQ(infinitely_loading_url, results[0].url);
6884 EXPECT_TRUE(results[1].committed);
Daniel Chengfa8ffcf2023-05-04 05:43:466885 EXPECT_EQ(embedded_test_server()->GetOrigin("c.com"), results[1].origin);
6886 EXPECT_EQ(final_url, results[1].url);
6887}
6888
Daniel Cheng77ae2b02023-05-10 04:16:466889// Test the behavior of a navigation that gets suspended in the pending commit
6890// state followed by another navigation that results in a failed navigation. A
6891// failed navigation is not a navigation that results in an HTTP error page; it
6892// is a situation where the network request itself fails, e.g. DNS resolution
6893// failed, and Chrome commits an error page instead.
6894IN_PROC_BROWSER_TEST_P(
6895 CommitNavigationRaceBrowserTest,
6896 BeginNewNavigationDuringCommitFailedNavigationInMainFrame) {
6897 ASSERT_TRUE(NavigateToURL(
6898 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
6899
6900 // Prior to implementing the UndoCommitNavigation() workaround, the race
6901 // condition being tested would result in a crash in the b.com renderer. Open
6902 // another b.com window in the same browsing instance to verify that the b.com
6903 // renderer does not unexpectedly crash even if the b.com speculative
6904 // RenderFrameHost is discarded.
6905 ASSERT_TRUE(ExecJs(
6906 shell(), JsReplace("window.open($1)", embedded_test_server()->GetURL(
6907 "b.com", "/title1.html"))));
6908 ASSERT_EQ(2u, Shell::windows().size());
6909 WebContentsImpl* new_web_contents =
6910 static_cast<WebContentsImpl*>(Shell::windows()[1]->web_contents());
6911 WaitForLoadStop(new_web_contents);
6912 RenderProcessHost* const b_com_render_process_host =
6913 new_web_contents->GetPrimaryMainFrame()->GetProcess();
6914
6915 NavigationLogger logger(shell()->web_contents());
6916
6917 // Start a navigation that will create a speculative RFH in the existing
6918 // render process for b.com.
6919 const GURL infinitely_loading_url =
6920 embedded_test_server()->GetURL("b.com", "/infinitely_loading_image.html");
Jiacheng Guo4bdd0be2024-06-11 23:35:216921 SpeculativeRenderFrameHostObserver rfh_observer(shell()->web_contents(),
6922 infinitely_loading_url);
Daniel Cheng77ae2b02023-05-10 04:16:466923 ASSERT_TRUE(BeginNavigateToURLFromRenderer(shell(), infinitely_loading_url));
Jiacheng Guo4bdd0be2024-06-11 23:35:216924 rfh_observer.Wait();
Daniel Cheng77ae2b02023-05-10 04:16:466925
6926 // Ensure the speculative RFH is in the expected process (i.e. the b.com
6927 // process that was created for the navigation in the new window earlier).
6928 WebContentsImpl* web_contents =
6929 static_cast<WebContentsImpl*>(shell()->web_contents());
6930 RenderFrameHostImpl* speculative_render_frame_host =
6931 web_contents->GetPrimaryFrameTree()
6932 .root()
6933 ->render_manager()
6934 ->speculative_frame_host();
6935 ASSERT_TRUE(speculative_render_frame_host);
6936 EXPECT_EQ(b_com_render_process_host,
6937 speculative_render_frame_host->GetProcess());
6938
6939 // Pause (and potentially ignore, if navigation queueing is disabled) the next
6940 // `DidCommitProvisionalLoad()` for b.com.
6941 CommitNavigationPauser commit_pauser(speculative_render_frame_host);
6942 commit_pauser.WaitForCommitAndPause();
6943
Arthur Sonzognic686e8f2024-01-11 08:36:376944 std::optional<ResumeCommitClosureSetWaiter> resume_commit_closure_set_waiter;
Daniel Cheng77ae2b02023-05-10 04:16:466945 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
6946 // If navigation queueing is enabled, the test should verify that a resume
6947 // commit closure is actually set. Install a watcher now, before beginning
6948 // the next navigation, since the resume commit closure may be synchronously
6949 // set while handling the `BeginNavigation()` IPC in the browser.
6950 OnNextDidStartNavigation(web_contents, [&](NavigationHandle* handle) {
6951 resume_commit_closure_set_waiter.emplace(handle);
6952 });
6953 }
6954
6955 // Now begin a new navigation to c.com while the previous b.com navigation
6956 // above is paused in the pending commit state. This navigation will fail and
6957 // commit an error page.
6958 const GURL final_url =
6959 embedded_test_server()->GetURL("c.com", "/title1.html");
6960 std::unique_ptr<URLLoaderInterceptor> url_interceptor =
6961 URLLoaderInterceptor::SetupRequestFailForURL(final_url,
6962 net::ERR_DNS_TIMED_OUT);
6963 ASSERT_TRUE(BeginNavigateToURLFromRenderer(web_contents, final_url));
6964
6965 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
6966 resume_commit_closure_set_waiter->Wait();
6967 commit_pauser.ResumePausedCommit();
6968 }
6969
6970 // The top-level page completes loading but is an error page, so
6971 // `WaitForLoadStop()` should return false, since the navigation entry will
6972 // have `PAGE_TYPE_ERROR`.
6973 EXPECT_FALSE(WaitForLoadStop(web_contents));
6974 EXPECT_EQ(final_url, web_contents->GetLastCommittedURL());
6975 EXPECT_TRUE(web_contents->GetPrimaryMainFrame()->IsErrorDocument());
6976
6977 auto results = logger.results();
6978 ASSERT_EQ(2u, results.size());
6979 EXPECT_EQ(infinitely_loading_url, results[0].url);
6980 // If navigation queueing is enabled, the first navigation will complete the
6981 // commit as the new navigation gets queued until the first navigation's
6982 // commit finished. If navigation queueing is disabled, the pending commit
6983 // navigation will be cancelled.
6984 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
6985 EXPECT_TRUE(results[0].committed);
6986 EXPECT_EQ(embedded_test_server()->GetOrigin("b.com"), results[0].origin);
6987 } else {
6988 EXPECT_FALSE(results[0].committed);
Arthur Sonzognic686e8f2024-01-11 08:36:376989 EXPECT_EQ(std::nullopt, results[0].origin);
Daniel Cheng77ae2b02023-05-10 04:16:466990 }
6991 EXPECT_TRUE(results[1].committed);
6992 EXPECT_TRUE(results[1].origin->opaque());
6993 EXPECT_EQ(embedded_test_server()
6994 ->GetOrigin("c.com")
6995 .GetTupleOrPrecursorTupleIfOpaque(),
6996 results[1].origin->GetTupleOrPrecursorTupleIfOpaque());
6997 EXPECT_EQ(final_url, results[1].url);
6998}
6999
7000IN_PROC_BROWSER_TEST_P(
7001 CommitNavigationRaceBrowserTest,
7002 BeginNewNavigationDuringCommitFailedNavigationInSubFrame) {
7003 ASSERT_TRUE(NavigateToURL(
7004 shell(), embedded_test_server()->GetURL(
7005 "b.com", "/cross_site_iframe_factory.html?b(a)")));
7006
7007 WebContentsImpl* web_contents =
7008 static_cast<WebContentsImpl*>(shell()->web_contents());
7009 FrameTreeNode* first_subframe_node =
7010 web_contents->GetPrimaryMainFrame()->child_at(0);
7011 RenderProcessHost* const b_com_render_process_host =
7012 web_contents->GetPrimaryFrameTree()
7013 .root()
7014 ->render_manager()
7015 ->current_frame_host()
7016 ->GetProcess();
7017
7018 NavigationLogger logger(web_contents);
7019
7020 // Start a navigation that will create a speculative RFH in the existing
7021 // render process for b.com.
7022 const GURL infinitely_loading_url =
7023 embedded_test_server()->GetURL("b.com", "/infinitely_loading_image.html");
Jiacheng Guo4bdd0be2024-06-11 23:35:217024 SpeculativeRenderFrameHostObserver rfh_observer(web_contents,
7025 infinitely_loading_url);
Daniel Cheng77ae2b02023-05-10 04:16:467026 ASSERT_TRUE(BeginNavigateToURLFromRenderer(first_subframe_node,
7027 infinitely_loading_url));
Jiacheng Guo4bdd0be2024-06-11 23:35:217028 rfh_observer.Wait();
Daniel Cheng77ae2b02023-05-10 04:16:467029
7030 // Ensure the speculative RFH is in the expected process.
7031 RenderFrameHostImpl* speculative_render_frame_host =
7032 first_subframe_node->render_manager()->speculative_frame_host();
7033 ASSERT_TRUE(speculative_render_frame_host);
7034 EXPECT_EQ(b_com_render_process_host,
7035 speculative_render_frame_host->GetProcess());
7036
7037 // Pause (and potentially ignore, if navigation queueing is disabled) the next
7038 // `DidCommitProvisionalLoad()` for b.com.
7039 CommitNavigationPauser commit_pauser(speculative_render_frame_host);
7040 commit_pauser.WaitForCommitAndPause();
7041
Arthur Sonzognic686e8f2024-01-11 08:36:377042 std::optional<ResumeCommitClosureSetWaiter> resume_commit_closure_set_waiter;
Daniel Cheng77ae2b02023-05-10 04:16:467043 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
7044 // If navigation queueing is enabled, the test should verify that a resume
7045 // commit closure is actually set. Install a watcher now, before beginning
7046 // the next navigation, since the resume commit closure may be synchronously
7047 // set while handling the `BeginNavigation()` IPC in the browser.
7048 OnNextDidStartNavigation(web_contents, [&](NavigationHandle* handle) {
7049 resume_commit_closure_set_waiter.emplace(handle);
7050 });
7051 }
7052
7053 // Now begin a new navigation to c.com while the previous b.com navigation
7054 // above is paused in the pending commit state. This navigation will fail and
7055 // commit an error page.
7056 const GURL final_url =
7057 embedded_test_server()->GetURL("c.com", "/title1.html");
7058 std::unique_ptr<URLLoaderInterceptor> url_interceptor =
7059 URLLoaderInterceptor::SetupRequestFailForURL(final_url,
7060 net::ERR_DNS_TIMED_OUT);
7061 ASSERT_TRUE(BeginNavigateToURLFromRenderer(first_subframe_node, final_url));
7062
7063 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
7064 resume_commit_closure_set_waiter->Wait();
7065 commit_pauser.ResumePausedCommit();
7066 }
7067
7068 // The top-level page completes loading. Unlike the main frame variant of this
7069 // test, `WaitForLoadStop()` should return true, since the navigation entry
7070 // will have `PAGE_TYPE_NORMAL` as only a subframe failed to load.
7071 EXPECT_TRUE(WaitForLoadStop(web_contents));
7072 EXPECT_EQ(final_url, first_subframe_node->render_manager()
7073 ->current_frame_host()
7074 ->GetLastCommittedURL());
7075 EXPECT_TRUE(first_subframe_node->render_manager()
7076 ->current_frame_host()
7077 ->IsErrorDocument());
7078
7079 auto results = logger.results();
7080 ASSERT_EQ(2u, results.size());
7081 // If navigation queueing is enabled, the first navigation will complete the
7082 // commit as the new navigation gets queued until the first navigation's
7083 // commit finished. If navigation queueing is disabled, the pending commit
7084 // navigation will be cancelled.
7085 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
7086 EXPECT_TRUE(results[0].committed);
7087 EXPECT_EQ(embedded_test_server()->GetOrigin("b.com"), results[0].origin);
7088 } else {
7089 EXPECT_FALSE(results[0].committed);
Arthur Sonzognic686e8f2024-01-11 08:36:377090 EXPECT_EQ(std::nullopt, results[0].origin);
Daniel Cheng77ae2b02023-05-10 04:16:467091 }
7092 EXPECT_EQ(infinitely_loading_url, results[0].url);
7093 EXPECT_TRUE(results[1].committed);
7094 EXPECT_TRUE(results[1].origin->opaque());
7095 EXPECT_EQ(embedded_test_server()
7096 ->GetOrigin("c.com")
7097 .GetTupleOrPrecursorTupleIfOpaque(),
7098 results[1].origin->GetTupleOrPrecursorTupleIfOpaque());
7099 EXPECT_EQ(final_url, results[1].url);
7100}
7101
Daniel Chengfa8ffcf2023-05-04 05:43:467102// about:blank navigations do not require a URL loader and go through a
7103// different path to commit the navigation in the renderer.
7104IN_PROC_BROWSER_TEST_P(
7105 CommitNavigationRaceBrowserTest,
Daniel Cheng406fbd52023-05-09 04:58:247106 BeginNewNavigationWithNoUrlLoaderDuringCommitNavigationInMainFrame) {
Daniel Chengfa8ffcf2023-05-04 05:43:467107 ASSERT_TRUE(NavigateToURL(
7108 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
7109
Daniel Cheng77ae2b02023-05-10 04:16:467110 // Prior to implementing the UndoCommitNavigation() workaround, the race
7111 // condition being tested would result in a crash in the b.com renderer. Open
7112 // another b.com window in the same browsing instance to verify that the b.com
7113 // renderer does not unexpectedly crash even if the b.com speculative
7114 // RenderFrameHost is discarded.
Daniel Chengfa8ffcf2023-05-04 05:43:467115 ASSERT_TRUE(ExecJs(
7116 shell(), JsReplace("window.open($1)", embedded_test_server()->GetURL(
7117 "b.com", "/title1.html"))));
7118 ASSERT_EQ(2u, Shell::windows().size());
7119 WebContentsImpl* new_web_contents =
7120 static_cast<WebContentsImpl*>(Shell::windows()[1]->web_contents());
7121 EXPECT_TRUE(WaitForLoadStop(new_web_contents));
7122 RenderProcessHost* const b_com_render_process_host =
7123 new_web_contents->GetPrimaryMainFrame()->GetProcess();
7124
7125 NavigationLogger logger(shell()->web_contents());
7126
7127 // Start a navigation that will create a speculative RFH in the existing
7128 // render process for b.com.
7129 const GURL infinitely_loading_url =
7130 embedded_test_server()->GetURL("b.com", "/infinitely_loading_image.html");
Jiacheng Guo4bdd0be2024-06-11 23:35:217131 SpeculativeRenderFrameHostObserver rfh_observer(shell()->web_contents(),
7132 infinitely_loading_url);
Daniel Chengfa8ffcf2023-05-04 05:43:467133 ASSERT_TRUE(BeginNavigateToURLFromRenderer(shell(), infinitely_loading_url));
Jiacheng Guo4bdd0be2024-06-11 23:35:217134 rfh_observer.Wait();
Daniel Chengfa8ffcf2023-05-04 05:43:467135
7136 // Ensure the speculative RFH is in the expected process (i.e. the b.com
7137 // process that was created for the navigation in the new window earlier).
7138 WebContentsImpl* web_contents =
7139 static_cast<WebContentsImpl*>(shell()->web_contents());
7140 RenderFrameHostImpl* speculative_render_frame_host =
7141 web_contents->GetPrimaryFrameTree()
7142 .root()
7143 ->render_manager()
7144 ->speculative_frame_host();
7145 ASSERT_TRUE(speculative_render_frame_host);
7146 EXPECT_EQ(b_com_render_process_host,
7147 speculative_render_frame_host->GetProcess());
7148
Daniel Cheng406fbd52023-05-09 04:58:247149 // Pause (and potentially ignore, if navigation queueing is disabled) the next
7150 // `DidCommitProvisionalLoad()` for b.com.
7151 CommitNavigationPauser commit_pauser(speculative_render_frame_host);
7152 commit_pauser.WaitForCommitAndPause();
7153
Arthur Sonzognic686e8f2024-01-11 08:36:377154 std::optional<ResumeCommitClosureSetWaiter> resume_commit_closure_set_waiter;
Daniel Cheng406fbd52023-05-09 04:58:247155 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
7156 // If navigation queueing is enabled, the test should verify that a resume
7157 // commit closure is actually set. Install a watcher now, before beginning
Daniel Cheng77ae2b02023-05-10 04:16:467158 // the next navigation, since the resume commit closure may be synchronously
Daniel Cheng406fbd52023-05-09 04:58:247159 // set while handling the `BeginNavigation()` IPC in the browser.
7160 OnNextDidStartNavigation(web_contents, [&](NavigationHandle* handle) {
7161 resume_commit_closure_set_waiter.emplace(handle);
7162 });
7163 }
7164
7165 // Note that this navigation is initiated by the a.com renderer, as the a.com
7166 // renderer is still the current frame host for the main frame.
Daniel Chengfa8ffcf2023-05-04 05:43:467167 const GURL final_url("about:blank");
Daniel Cheng406fbd52023-05-09 04:58:247168 ASSERT_TRUE(BeginNavigateToURLFromRenderer(web_contents, final_url));
7169
7170 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
7171 resume_commit_closure_set_waiter->Wait();
7172 commit_pauser.ResumePausedCommit();
7173 }
Daniel Chengfa8ffcf2023-05-04 05:43:467174
7175 EXPECT_TRUE(WaitForLoadStop(web_contents));
7176 EXPECT_EQ(final_url, web_contents->GetLastCommittedURL());
7177
7178 auto results = logger.results();
7179 ASSERT_EQ(2u, results.size());
7180 EXPECT_EQ(infinitely_loading_url, results[0].url);
7181 // If navigation queueing is enabled, the first navigation will complete the
7182 // commit as the new navigation gets queued until the first navigation's
7183 // commit finished. If navigation queueing is disabled, the pending commit
7184 // navigation will be cancelled.
7185 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
7186 EXPECT_TRUE(results[0].committed);
7187 EXPECT_EQ(embedded_test_server()->GetOrigin("b.com"), results[0].origin);
7188 } else {
7189 EXPECT_FALSE(results[0].committed);
Arthur Sonzognic686e8f2024-01-11 08:36:377190 EXPECT_EQ(std::nullopt, results[0].origin);
Daniel Chengfa8ffcf2023-05-04 05:43:467191 }
7192 EXPECT_TRUE(results[1].committed);
7193 EXPECT_EQ(embedded_test_server()->GetOrigin("a.com"), results[1].origin);
7194 EXPECT_EQ(final_url, results[1].url);
7195}
7196
7197IN_PROC_BROWSER_TEST_P(
7198 CommitNavigationRaceBrowserTest,
Daniel Cheng406fbd52023-05-09 04:58:247199 BeginNewNavigationWithNoUrlLoaderDuringCommitNavigationInSubFrame) {
Daniel Chengfa8ffcf2023-05-04 05:43:467200 ASSERT_TRUE(NavigateToURL(
7201 shell(), embedded_test_server()->GetURL(
7202 "b.com", "/cross_site_iframe_factory.html?b(a)")));
7203
7204 WebContentsImpl* web_contents =
7205 static_cast<WebContentsImpl*>(shell()->web_contents());
7206 FrameTreeNode* first_subframe_node =
7207 web_contents->GetPrimaryMainFrame()->child_at(0);
7208 RenderProcessHost* const b_com_render_process_host =
7209 web_contents->GetPrimaryFrameTree()
7210 .root()
7211 ->render_manager()
7212 ->current_frame_host()
7213 ->GetProcess();
7214
7215 NavigationLogger logger(web_contents);
7216
7217 // Start a navigation that will create a speculative RFH in the existing
7218 // render process for b.com.
7219 const GURL infinitely_loading_url =
7220 embedded_test_server()->GetURL("b.com", "/infinitely_loading_image.html");
Jiacheng Guo4bdd0be2024-06-11 23:35:217221 SpeculativeRenderFrameHostObserver rfh_observer(shell()->web_contents(),
7222 infinitely_loading_url);
Daniel Chengfa8ffcf2023-05-04 05:43:467223 ASSERT_TRUE(BeginNavigateToURLFromRenderer(first_subframe_node,
7224 infinitely_loading_url));
Jiacheng Guo4bdd0be2024-06-11 23:35:217225 rfh_observer.Wait();
Daniel Chengfa8ffcf2023-05-04 05:43:467226
7227 // Ensure the speculative RFH is in the expected process.
7228 RenderFrameHostImpl* speculative_render_frame_host =
7229 first_subframe_node->render_manager()->speculative_frame_host();
7230 ASSERT_TRUE(speculative_render_frame_host);
7231 EXPECT_EQ(b_com_render_process_host,
7232 speculative_render_frame_host->GetProcess());
7233
Daniel Cheng406fbd52023-05-09 04:58:247234 CommitNavigationPauser commit_pauser(speculative_render_frame_host);
7235 commit_pauser.WaitForCommitAndPause();
7236
Arthur Sonzognic686e8f2024-01-11 08:36:377237 std::optional<ResumeCommitClosureSetWaiter> resume_commit_closure_set_waiter;
Daniel Cheng406fbd52023-05-09 04:58:247238 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
7239 // If navigation queueing is enabled, the test should verify that a resume
7240 // commit closure is actually set. Install a watcher now, before beginning
Daniel Cheng77ae2b02023-05-10 04:16:467241 // the next navigation, since the resume commit closure may be synchronously
Daniel Cheng406fbd52023-05-09 04:58:247242 // set while handling the `BeginNavigation()` IPC in the browser.
7243 OnNextDidStartNavigation(web_contents, [&](NavigationHandle* handle) {
7244 resume_commit_closure_set_waiter.emplace(handle);
7245 });
7246 }
7247
7248 // Note that this navigation is initiated by the a.com renderer, as the a.com
7249 // renderer is still the current frame host for the main frame.
Daniel Chengfa8ffcf2023-05-04 05:43:467250 const GURL final_url("about:blank");
Daniel Cheng406fbd52023-05-09 04:58:247251 ASSERT_TRUE(BeginNavigateToURLFromRenderer(first_subframe_node, final_url));
7252
7253 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
7254 resume_commit_closure_set_waiter->Wait();
7255 commit_pauser.ResumePausedCommit();
7256 }
Daniel Chengfa8ffcf2023-05-04 05:43:467257
7258 EXPECT_TRUE(WaitForLoadStop(web_contents));
7259 EXPECT_EQ(final_url, first_subframe_node->render_manager()
7260 ->current_frame_host()
7261 ->GetLastCommittedURL());
7262
7263 auto results = logger.results();
7264 ASSERT_EQ(2u, results.size());
7265 // If navigation queueing is enabled, the first navigation will complete the
7266 // commit as the new navigation gets queued until the first navigation's
7267 // commit finished. If navigation queueing is disabled, the pending commit
7268 // navigation will be cancelled.
7269 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
7270 EXPECT_TRUE(results[0].committed);
7271 EXPECT_EQ(embedded_test_server()->GetOrigin("b.com"), results[0].origin);
7272 } else {
7273 EXPECT_FALSE(results[0].committed);
Arthur Sonzognic686e8f2024-01-11 08:36:377274 EXPECT_EQ(std::nullopt, results[0].origin);
Daniel Chengfa8ffcf2023-05-04 05:43:467275 }
7276 EXPECT_EQ(infinitely_loading_url, results[0].url);
7277 EXPECT_TRUE(results[1].committed);
7278 EXPECT_EQ(embedded_test_server()->GetOrigin("a.com"), results[1].origin);
Daniel Chengd08a43a2023-03-16 05:10:507279 EXPECT_EQ(final_url, results[1].url);
Daniel Cheng42cb7e9f52021-02-23 01:54:457280}
7281
Rakina Zata Amni9fba59a52023-05-17 00:25:267282// Tests when a navigation is pending commit, two new navigations start one
7283// after another in the same frame.
7284IN_PROC_BROWSER_TEST_P(CommitNavigationRaceBrowserTest,
7285 BeginTwoNavigationsDuringCommitNavigation) {
7286 ASSERT_TRUE(NavigateToURL(
7287 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
7288
7289 // Prior to implementing the UndoCommitNavigation() workaround, the race
7290 // condition being tested would result in a crash in the b.com renderer. Open
7291 // another b.com window in the same browsing instance to verify that the b.com
7292 // renderer does not unexpectedly crash even if the b.com speculative
7293 // RenderFrameHost is discarded.
7294 ASSERT_TRUE(ExecJs(
7295 shell(), JsReplace("window.open($1)", embedded_test_server()->GetURL(
7296 "b.com", "/title1.html"))));
7297 ASSERT_EQ(2u, Shell::windows().size());
7298 WebContentsImpl* new_web_contents =
7299 static_cast<WebContentsImpl*>(Shell::windows()[1]->web_contents());
7300 WaitForLoadStop(new_web_contents);
7301 RenderProcessHost* const b_com_render_process_host =
7302 new_web_contents->GetPrimaryMainFrame()->GetProcess();
7303
7304 NavigationLogger logger(shell()->web_contents());
7305
7306 // Start a navigation that will create a speculative RFH in the existing
7307 // render process for b.com.
7308 const GURL url_b =
7309 embedded_test_server()->GetURL("b.com", "/infinitely_loading_image.html");
Jiacheng Guo4bdd0be2024-06-11 23:35:217310 SpeculativeRenderFrameHostObserver rfh_observer(shell()->web_contents(),
7311 url_b);
Rakina Zata Amni9fba59a52023-05-17 00:25:267312 ASSERT_TRUE(BeginNavigateToURLFromRenderer(shell(), url_b));
Jiacheng Guo4bdd0be2024-06-11 23:35:217313 rfh_observer.Wait();
Rakina Zata Amni9fba59a52023-05-17 00:25:267314
7315 // Ensure the speculative RFH is in the expected process (i.e. the b.com
7316 // process that was created for the navigation in the new window earlier).
7317 WebContentsImpl* web_contents =
7318 static_cast<WebContentsImpl*>(shell()->web_contents());
7319 FrameTreeNode* root = web_contents->GetPrimaryFrameTree().root();
7320 RenderFrameHostImpl* speculative_render_frame_host =
7321 root->render_manager()->speculative_frame_host();
7322 ASSERT_TRUE(speculative_render_frame_host);
7323 EXPECT_EQ(b_com_render_process_host,
7324 speculative_render_frame_host->GetProcess());
7325
7326 // Pause (and potentially ignore, if navigation queueing is disabled) the next
7327 // `DidCommitProvisionalLoad()` for b.com.
7328 CommitNavigationPauser commit_pauser(speculative_render_frame_host);
7329 commit_pauser.WaitForCommitAndPause();
7330
7331 // Now begin a new navigation to c.com while the previous b.com navigation
7332 // above is paused in the pending commit state.
Arthur Sonzognic686e8f2024-01-11 08:36:377333 std::optional<ResumeCommitClosureSetWaiter>
Rakina Zata Amni9fba59a52023-05-17 00:25:267334 url_c_resume_commit_closure_set_waiter;
7335 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
7336 // If navigation queueing is enabled, the test should verify that a resume
7337 // commit closure is actually set. Install a watcher now, before beginning
7338 // the `url_c` navigation, since the resume commit closure may be
7339 // synchronously set while handling the `BeginNavigation()` IPC in the
7340 // browser.
7341 OnNextDidStartNavigation(web_contents, [&](NavigationHandle* handle) {
7342 url_c_resume_commit_closure_set_waiter.emplace(handle);
7343 });
7344 }
7345
7346 const GURL url_c = embedded_test_server()->GetURL("c.com", "/title1.html");
7347 TestNavigationManager url_c_nav(web_contents, url_c);
7348 ASSERT_TRUE(BeginNavigateToURLFromRenderer(web_contents, url_c));
Jiacheng Guo4bdd0be2024-06-11 23:35:217349 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
7350 ASSERT_TRUE(url_c_nav.WaitForRequestStart());
7351 } else {
7352 url_c_nav.WaitForSpeculativeRenderFrameHostCreation();
7353 }
Rakina Zata Amni9fba59a52023-05-17 00:25:267354 EXPECT_EQ(url_c, root->navigation_request()->GetURL());
7355
7356 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
7357 // The navigation to c.com should be queued.
7358 url_c_nav.ResumeNavigation();
7359 url_c_resume_commit_closure_set_waiter->Wait();
7360 }
7361
7362 // Now begin another navigation to d.com, which will cancel the navigation to
7363 // c.com.
Arthur Sonzognic686e8f2024-01-11 08:36:377364 std::optional<ResumeCommitClosureSetWaiter>
Rakina Zata Amni9fba59a52023-05-17 00:25:267365 url_d_resume_commit_closure_set_waiter;
7366 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
7367 // Install a commit closure watched for the `url_d` navigation too.
7368 OnNextDidStartNavigation(web_contents, [&](NavigationHandle* handle) {
7369 url_d_resume_commit_closure_set_waiter.emplace(handle);
7370 });
7371 }
7372 const GURL url_d = embedded_test_server()->GetURL("d.com", "/title1.html");
7373 TestNavigationManager url_d_nav(web_contents, url_d);
7374 ASSERT_TRUE(BeginNavigateToURLFromRenderer(web_contents, url_d));
7375 ASSERT_TRUE(url_d_nav.WaitForRequestStart());
7376 EXPECT_EQ(url_d, root->navigation_request()->GetURL());
7377
7378 // The navigation to c.com didn't commit as it was replaced by the d.com
7379 // navigation.
7380 EXPECT_TRUE(url_c_nav.WaitForNavigationFinished());
7381 EXPECT_FALSE(url_c_nav.was_committed());
7382
7383 // Continue the d.com navigation.
7384 url_d_nav.ResumeNavigation();
7385 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
7386 // Wait for the `url_d` navigation to be queued, and finish the pending
7387 // commit b.com navigation.
7388 url_d_resume_commit_closure_set_waiter->Wait();
7389 commit_pauser.ResumePausedCommit();
7390 }
7391
7392 // After all the navigations finished, we will end up in d.com.
7393 EXPECT_TRUE(url_d_nav.WaitForNavigationFinished());
7394 EXPECT_EQ(url_d, web_contents->GetLastCommittedURL());
7395
7396 // Check the order of navigations finishing.
7397 auto results = logger.results();
7398 ASSERT_EQ(3u, results.size());
7399
7400 EXPECT_FALSE(results[0].committed);
Arthur Sonzognic686e8f2024-01-11 08:36:377401 EXPECT_EQ(std::nullopt, results[0].origin);
Rakina Zata Amni9fba59a52023-05-17 00:25:267402 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
7403 // When navigation queueing is enabled, the pending commit navigation to
7404 // b.com won't get canceled when the c.com navigation starts. Then when the
7405 // d.com navigation starts, the c.com navigation will get canceled and
7406 // finishes first without commmitting (while the b.com navigation stays as
7407 // it is pending commit).
7408 EXPECT_EQ(url_c, results[0].url);
7409 EXPECT_TRUE(results[1].committed);
7410 // After continuing b.com's commit, it finishes and commits succesfully.
7411 EXPECT_EQ(url_b, results[1].url);
7412 EXPECT_EQ(embedded_test_server()->GetOrigin("b.com"), results[1].origin);
7413 } else {
7414 // When navigation queueing is disabled, the pending commit navigation to
7415 // b.com gets canceled when the c.com navigation starts. Then when the
7416 // d.com navigation starts, the c.com navigation will get canceled too.
7417 EXPECT_EQ(url_b, results[0].url);
7418 EXPECT_FALSE(results[1].committed);
7419 EXPECT_EQ(url_c, results[1].url);
Arthur Sonzognic686e8f2024-01-11 08:36:377420 EXPECT_EQ(std::nullopt, results[1].origin);
Rakina Zata Amni9fba59a52023-05-17 00:25:267421 }
7422 // Finally, the d.com navigation finishes and commits last.
7423 EXPECT_TRUE(results[2].committed);
7424 EXPECT_EQ(url_d, results[2].url);
7425 EXPECT_EQ(embedded_test_server()->GetOrigin("d.com"), results[2].origin);
7426}
7427
Daniel Cheng0df435e2023-05-19 17:35:267428// Verify that a speculative RFH in the pending commit state is still cleaned up
7429// if the renderer crashes.
7430IN_PROC_BROWSER_TEST_P(CommitNavigationRaceBrowserTest,
7431 CrashedInPendingCommit) {
Jiacheng Guo4bdd0be2024-06-11 23:35:217432 GURL url_a = embedded_test_server()->GetURL("a.com", "/title1.html");
7433 GURL url_b = embedded_test_server()->GetURL("b.com", "/title1.html");
7434 ASSERT_TRUE(NavigateToURL(shell(), url_a));
Daniel Cheng0df435e2023-05-19 17:35:267435
7436 WebContentsImpl* web_contents =
7437 static_cast<WebContentsImpl*>(shell()->web_contents());
Jiacheng Guo4bdd0be2024-06-11 23:35:217438 SpeculativeRenderFrameHostObserver rfh_observer(web_contents, url_b);
7439 ASSERT_TRUE(BeginNavigateToURLFromRenderer(shell(), url_b));
7440 rfh_observer.Wait();
7441
Daniel Cheng0df435e2023-05-19 17:35:267442 base::WeakPtr<RenderFrameHostImpl> speculative_render_frame_host =
7443 web_contents->GetPrimaryFrameTree()
7444 .root()
7445 ->render_manager()
7446 ->speculative_frame_host()
7447 ->GetWeakPtr();
7448 ASSERT_TRUE(speculative_render_frame_host);
7449
7450 // Wait for the next `DidCommitProvisionalLoad()` and ignore it.
7451 CommitNavigationPauser commit_pauser(speculative_render_frame_host.get());
7452 commit_pauser.WaitForCommitAndPause();
7453
7454 ASSERT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kPendingCommit,
7455 speculative_render_frame_host->lifecycle_state());
7456
7457 // Terminate the renderer process while `speculative_render_frame_host` is in
7458 // `kPendingCommit`.
7459 RenderProcessHostWatcher watcher(
7460 speculative_render_frame_host->GetProcess(),
7461 RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
7462 speculative_render_frame_host->GetProcess()->ShutdownForBadMessage(
7463 RenderProcessHost::CrashReportMode::NO_CRASH_DUMP);
7464 watcher.Wait();
7465
7466 // The speculative RFH should be gone:
7467 ASSERT_FALSE(speculative_render_frame_host);
7468 EXPECT_FALSE(web_contents->GetPrimaryFrameTree()
7469 .root()
7470 ->render_manager()
7471 ->speculative_frame_host());
7472
7473 // And a new navigation should not hit any DCHECKs in
7474 // `GetFrameHostForNavigation()`.
7475 EXPECT_TRUE(NavigateToURLFromRenderer(
7476 shell(), embedded_test_server()->GetURL("b.com", "/title1.html")));
7477}
7478
Rakina Zata Amni15378fb2023-06-02 09:56:107479// Tests when a back navigation is pending commit, then another back navigation
7480// starts.
7481IN_PROC_BROWSER_TEST_P(CommitNavigationRaceBrowserTest,
7482 MultipleBackNavigation) {
7483 // This test expects the document is freshly loaded on the back navigation.
7484 DisableBackForwardCacheForTesting(web_contents(),
7485 BackForwardCache::TEST_REQUIRES_NO_CACHING);
7486
7487 // Navigate to a.com, then b.com.
7488 const GURL url_a = embedded_test_server()->GetURL("a.com", "/title1.html");
7489 const GURL url_b = embedded_test_server()->GetURL("b.com", "/title1.html");
7490 ASSERT_TRUE(NavigateToURL(shell(), url_a));
7491 ASSERT_TRUE(NavigateToURL(shell(), url_b));
7492
7493 // Prior to implementing the UndoCommitNavigation() workaround, the race
7494 // condition being tested would result in a crash in the b.com renderer. Open
7495 // another b.com window in the same browsing instance to verify that the b.com
7496 // renderer does not unexpectedly crash even if the b.com speculative
7497 // RenderFrameHost is discarded.
7498 ASSERT_TRUE(ExecJs(shell(), JsReplace("window.open($1)", url_b)));
7499 ASSERT_EQ(2u, Shell::windows().size());
7500 WebContentsImpl* new_web_contents =
7501 static_cast<WebContentsImpl*>(Shell::windows()[1]->web_contents());
7502 EXPECT_TRUE(WaitForLoadStop(new_web_contents));
7503 RenderProcessHost* const b_com_render_process_host =
7504 new_web_contents->GetPrimaryMainFrame()->GetProcess();
7505
7506 // Navigate to c.com.
7507 const GURL url_c = embedded_test_server()->GetURL("c.com", "/title1.html");
7508 ASSERT_TRUE(NavigateToURL(shell(), url_c));
7509
7510 NavigationLogger logger(shell()->web_contents());
7511
7512 // Start a back navigation that will create a speculative RFH in the existing
7513 // render process for b.com.
7514 WebContentsImpl* web_contents =
7515 static_cast<WebContentsImpl*>(shell()->web_contents());
7516 FrameTreeNode* root = web_contents->GetPrimaryFrameTree().root();
7517 TestNavigationManager first_back_nav(web_contents, url_b);
7518 ASSERT_TRUE(ExecJs(shell(), "history.back()"));
7519 ASSERT_TRUE(first_back_nav.WaitForResponse());
7520 EXPECT_EQ(url_b, root->navigation_request()->GetURL());
7521
7522 // Ensure the speculative RFH is in the expected process (i.e. the b.com
7523 // process that was created for the navigation in the new window earlier).
7524 RenderFrameHostImpl* speculative_render_frame_host =
7525 root->render_manager()->speculative_frame_host();
7526 ASSERT_TRUE(speculative_render_frame_host);
7527 EXPECT_EQ(b_com_render_process_host,
7528 speculative_render_frame_host->GetProcess());
7529
7530 // Pause (and potentially ignore, if navigation queueing is disabled) the next
7531 // `DidCommitProvisionalLoad()` for b.com.
7532 CommitNavigationPauser commit_pauser(speculative_render_frame_host);
7533 first_back_nav.ResumeNavigation();
7534 commit_pauser.WaitForCommitAndPause();
7535
7536 // Now begin a new back navigation while the previous back navigation above is
7537 // paused in the pending commit state.
Arthur Sonzognic686e8f2024-01-11 08:36:377538 std::optional<ResumeCommitClosureSetWaiter>
Rakina Zata Amni15378fb2023-06-02 09:56:107539 second_back_nav_resume_commit_closure_set_waiter;
7540 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
7541 // If navigation queueing is enabled, the test should verify that a resume
7542 // commit closure is actually set. Install a watcher now, before beginning
7543 // the second back navigation, since the resume commit closure may be
7544 // synchronously set while handling the `BeginNavigation()` IPC in the
7545 // browser.
7546 OnNextDidStartNavigation(web_contents, [&](NavigationHandle* handle) {
7547 second_back_nav_resume_commit_closure_set_waiter.emplace(handle);
7548 });
7549 }
7550
7551 TestNavigationManager second_back_nav(web_contents, url_a);
7552 NavigationControllerImpl& controller = static_cast<NavigationControllerImpl&>(
7553 shell()->web_contents()->GetController());
7554 controller.GoBack();
7555 ASSERT_TRUE(second_back_nav.WaitForRequestStart());
7556 EXPECT_EQ(url_a, root->navigation_request()->GetURL());
7557
7558 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
7559 // The second back navigation should be queued.
7560 second_back_nav.ResumeNavigation();
7561 second_back_nav_resume_commit_closure_set_waiter->Wait();
7562 // Continue the first navigation's commit.
7563 commit_pauser.ResumePausedCommit();
7564 }
7565
7566 // After all the navigations finished, we will end up in a.com.
7567 EXPECT_TRUE(second_back_nav.WaitForNavigationFinished());
7568 EXPECT_EQ(url_a, web_contents->GetLastCommittedURL());
7569
7570 // Check the order of navigations finishing.
7571 auto results = logger.results();
7572 ASSERT_EQ(2u, results.size());
7573
7574 if (ShouldQueueNavigationsWhenPendingCommitRFHExists()) {
7575 // When navigation queueing is enabled, the pending commit back navigation
7576 // to b.com won't get canceled when the second back navigation starts. After
7577 // continuing the second back navigation, it finishes and commits
7578 // successfully to a.com.
7579 EXPECT_EQ(url_b, results[0].url);
7580 EXPECT_TRUE(results[0].committed);
7581 EXPECT_EQ(embedded_test_server()->GetOrigin("b.com"), results[0].origin);
7582 } else {
7583 // When navigation queueing is disabled, the pending commit back navigation
7584 // to b.com gets canceled when the second back navigation starts. Then the
7585 // second back navigation will successfully commit to a.com.
7586 EXPECT_EQ(url_b, results[0].url);
7587 EXPECT_FALSE(results[0].committed);
7588 }
7589
7590 EXPECT_EQ(url_a, results[1].url);
7591 EXPECT_TRUE(results[1].committed);
7592 EXPECT_EQ(embedded_test_server()->GetOrigin("a.com"), results[1].origin);
7593}
7594
Daniel Chengd08a43a2023-03-16 05:10:507595INSTANTIATE_TEST_SUITE_P(,
7596 CommitNavigationRaceBrowserTest,
7597 ::testing::Bool(),
7598 &CommitNavigationRaceBrowserTest::DescribeParams);
7599
Daniel Cheng32ab3502024-04-25 21:10:007600// Validate browser-side state when a pending commit RFH sends a bad
7601// CommitNavigation() IPC. Immediately after the bad message is reported, the
7602// speculative RFH should remain in the kPendingCommit state, but with no
7603// pending commit for a cross-document navigation. This somewhat odd state comes
7604// about because processing the commit navigation ack consumes the
7605// NavigationRequest early on, before the bad message is reported. Reporting the
7606// bad message cancels any further processing of the commit navigation ack, but
7607// does not directly clear any other navigation-related state.
7608//
7609// Instead, the pending commit speculative RFH will be asynchronously torn down
7610// later, when the browser process observes the renderer process going away,
7611// which then implicitly ends the navigation.
7612IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
7613 CommitBadNavigationInPendingCommitRFHCleanup) {
7614 if (!AreAllSitesIsolatedForTesting()) {
7615 GTEST_SKIP();
7616 }
7617
7618 // Populate the main window with something so a subsequent navigation will
7619 // create a speculative RFH.
7620 EXPECT_TRUE(NavigateToURL(
7621 web_contents(), embedded_test_server()->GetURL("b.com", "/title1.html")));
7622
7623 class CommitBadOriginInterceptor : public DidCommitNavigationInterceptor {
7624 public:
7625 using DidCommitNavigationInterceptor::DidCommitNavigationInterceptor;
7626
7627 WebContentsImpl* web_contents() {
7628 return static_cast<WebContentsImpl*>(
7629 DidCommitNavigationInterceptor::web_contents());
7630 }
7631
7632 bool WillProcessDidCommitNavigation(
7633 RenderFrameHost* render_frame_host,
7634 NavigationRequest* navigation_request,
7635 mojom::DidCommitProvisionalLoadParamsPtr* params,
7636 mojom::DidCommitProvisionalLoadInterfaceParamsPtr* interface_params)
7637 override {
7638 // Mismatch from the expected origin for the renderer process, which
7639 // should trigger a bad message kill.
7640 (*params)->origin = url::Origin::Create(GURL("https://example.com/"));
7641
7642 frame_watcher_.emplace(render_frame_host);
7643 process_watcher_.emplace(
7644 render_frame_host->GetProcess(),
7645 RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
7646
7647 base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
7648 FROM_HERE, base::BindLambdaForTesting([this]() {
7649 // This task should run after the browser has reported a bad
7650 // message, but before the browser process has observed render
7651 // process termination, so the pending commit speculative RFH should
7652 // still be present...
7653 auto* speculative_rfh = web_contents()
7654 ->GetPrimaryFrameTree()
7655 .root()
7656 ->render_manager()
7657 ->speculative_frame_host();
7658 ASSERT_TRUE(speculative_rfh);
7659 EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kPendingCommit,
7660 speculative_rfh->lifecycle_state());
7661
7662 // But it should not have any pending cross-document navigation
7663 // commits, since the NavigationRequest is consumed from
7664 // `RenderFrameHostImpl::navigation_requests_` as one of the first
7665 // parts of handling the commit navigation ack from the renderer.
7666 EXPECT_FALSE(
7667 speculative_rfh->HasPendingCommitForCrossDocumentNavigation());
7668
7669 validated_speculative_rfh_state_ = true;
7670 }));
7671
7672 return true;
7673 }
7674
7675 void CheckPendingCommitRenderFrameHostIsGone() const {
7676 // Make sure the validations in the posted callback actually ran.
7677 EXPECT_TRUE(validated_speculative_rfh_state_);
7678 EXPECT_TRUE(frame_watcher_->IsDestroyed());
7679 }
7680 void WaitForRenderProcessExit() { process_watcher_->Wait(); }
7681
7682 private:
7683 bool validated_speculative_rfh_state_ = false;
7684 std::optional<RenderFrameHostWrapper> frame_watcher_;
7685 std::optional<RenderProcessHostWatcher> process_watcher_;
7686 };
7687
7688 CommitBadOriginInterceptor interceptor(web_contents());
7689
7690 content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
7691 // The infinitely loading page is load-bearing here: `NavigateToURL()` waits
7692 // for `DidStopLoading()`, which can be triggered by:
7693 // 1. The renderer completing the load and sending `DidStopLoading()` (this is
7694 // typical).
7695 // 2. The renderer process going away (e.g. crashing) and the browser manually
7696 // triggering `DidStopLoading()` (this is unusual).
7697 //
7698 // However, this test is specifically testing case #2. To avoid the potential
7699 // of #1 and #2 racing (and causing the test to flakily fail if #1 wins the
7700 // race), set up the test so that the renderer will never call
7701 // `DidStopLoading()`.
7702 EXPECT_FALSE(NavigateToURL(web_contents(),
7703 embedded_test_server()->GetURL(
7704 "a.com", "/infinitely_loading_image.html")));
7705
7706 // NavigateToURL() should fail; at this point, make sure the speculative RFH
7707 // was in the expected state after the browser reported a bad message.
7708 //
7709 // Furthermore, after the navigation completes, the speculative RFH should
7710 // also be destroyed (implicitly, by the renderer process being terminated for
7711 // a bad message).
7712 interceptor.CheckPendingCommitRenderFrameHostIsGone();
7713
7714 // This should actually be signalled inside `NavigateToURL()`, since it waits
7715 // for the navigation to finish (whether successful or not) before returning.
7716 // Make sure the render process host actually exited: if it didn't, then the
7717 // test will timeout here.
7718 interceptor.WaitForRenderProcessExit();
7719}
7720
Antonio Sartori4f5373792021-05-31 10:56:477721// The following test checks what happens if a WebContentsDelegate navigates
7722// away in response to the NavigationStateChanged event. Previously
7723// (https://crbug.com/1210234), this was triggering a crash when creating the
7724// new NavigationRequest, because it was trying to access the current
7725// RenderFrameHost's PolicyContainerHost, which had not been set up yet by
7726// RenderFrameHostImpl::DidNavigate.
Antonio Sartorifca409c2022-07-27 08:20:587727IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, Bug1210234) {
Antonio Sartori4f5373792021-05-31 10:56:477728 class NavigationWebContentsDelegate : public WebContentsDelegate {
7729 public:
7730 NavigationWebContentsDelegate(const GURL& url_to_intercept,
7731 const GURL& url_to_navigate_to)
7732 : url_to_intercept_(url_to_intercept),
7733 url_to_navigate_to_(url_to_navigate_to) {}
7734 void NavigationStateChanged(WebContents* source,
7735 InvalidateTypes changed_flags) override {
7736 if (!navigated_ && source->GetLastCommittedURL() == url_to_intercept_) {
7737 navigated_ = true;
7738 source->GetController().LoadURL(url_to_navigate_to_, Referrer(),
7739 ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
7740 std::string());
7741 }
7742 }
7743
7744 private:
7745 bool navigated_ = false;
7746 GURL url_to_intercept_;
7747 GURL url_to_navigate_to_;
7748 };
7749
7750 GURL warmup_url = embedded_test_server()->GetURL("a.com", "/title1.html");
7751 GURL initial_url = embedded_test_server()->GetURL("b.com", "/title1.html");
7752 GURL redirection_url =
7753 embedded_test_server()->GetURL("c.com", "/title1.html");
7754
7755 NavigationWebContentsDelegate delegate(initial_url, redirection_url);
7756 web_contents()->SetDelegate(&delegate);
7757
7758 ASSERT_TRUE(NavigateToURL(shell(), warmup_url));
7759
Antonio Sartorifca409c2022-07-27 08:20:587760 // Note that since we committed a navigation, the next cross-origin navigation
7761 // will create a speculative RenderFrameHost (when site isolation is enabled).
Antonio Sartori4f5373792021-05-31 10:56:477762
Antonio Sartorifca409c2022-07-27 08:20:587763 // Start the navigation to `initial_url` and wait until the web contents
7764 // navigates to `redirection_url`. We cannot use helper functions like
7765 // `NavigateToURLBlockUntilNavigationsComplete` because they wait for
7766 // DidStopLoading and check the LastCommittedURL when they receive it.
7767 // However, without SiteIsolation, an earlier DidStopLoading might be received
7768 // when the WebContents has not yet committed the `redirection_url`.
7769
7770 // Prepare for the navigation.
7771 WaitForLoadStop(web_contents());
7772 TestNavigationObserver navigation_observer(redirection_url);
7773 navigation_observer.WatchExistingWebContents();
7774
7775 shell()->LoadURL(initial_url);
7776
7777 navigation_observer.Wait();
Lukasz Anforowicz051314d2021-09-29 20:07:397778
Antonio Sartori1139a792021-06-22 13:04:197779 EXPECT_TRUE(IsLastCommittedEntryOfPageType(web_contents(), PAGE_TYPE_NORMAL));
Antonio Sartori4f5373792021-05-31 10:56:477780 EXPECT_EQ(redirection_url, web_contents()->GetLastCommittedURL());
7781}
7782
Arthur Sonzogni64457592022-11-22 11:08:597783class NavigationBrowserTestCredentiallessIframe : public NavigationBrowserTest {
Antonio Sartori5abc8de2021-07-13 08:42:477784 public:
7785 void SetUpCommandLine(base::CommandLine* command_line) override {
7786 NavigationBrowserTest::SetUpCommandLine(command_line);
7787
Antonio Sartori753cd6d2021-07-23 08:34:557788 command_line->AppendSwitch(switches::kEnableBlinkTestFeatures);
Antonio Sartori5abc8de2021-07-13 08:42:477789 }
7790};
7791
Arthur Sonzogni64457592022-11-22 11:08:597792IN_PROC_BROWSER_TEST_F(NavigationBrowserTestCredentiallessIframe,
Joshua Thomaseb084802025-03-27 14:23:117793 CheckCookiesForCredentiallessIframeNavigation) {
7794 GURL main_url =
7795 embedded_test_server()->GetURL("/page_with_credentialless_iframe.html");
7796 GURL iframe_url_1 = embedded_test_server()->GetURL("/title1.html");
7797 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7798
7799 // Set a cookie on the main frame
7800 EXPECT_TRUE(ExecJs(main_frame(), "document.cookie = 'name=main;';"));
7801 EXPECT_EQ("name=main", EvalJs(main_frame(), "document.cookie;"));
7802
7803 // The main page has a child iframe with url `iframe_url_1`.
7804 EXPECT_EQ(1U, main_frame()->child_count());
7805 FrameTreeNode* child = main_frame()->child_at(0);
7806 EXPECT_EQ(iframe_url_1, child->current_url());
7807 EXPECT_TRUE(child->Credentialless());
7808 EXPECT_TRUE(child->current_frame_host()->IsCredentialless());
7809 EXPECT_TRUE(ExecJs(child->current_frame_host(), "window.credentialless"));
7810
7811 // Set up devtools client so we can check which cookies were sent and blocked
7812 // with frame navigation
7813 TestDevToolsProtocolClient fenced_frame_devtools_client;
7814 fenced_frame_devtools_client.AttachToFrameTreeHost(
7815 child->current_frame_host());
7816 fenced_frame_devtools_client.SendCommandAsync("Network.enable");
7817 fenced_frame_devtools_client.ClearNotifications();
7818
7819 // Set a cookie on the frame and reload so we can see which cookies are
7820 // included in the network request
7821 EXPECT_TRUE(ExecJs(child->current_frame_host(),
7822 "document.cookie = 'name=credentialless;';"));
7823
7824 EXPECT_EQ("name=credentialless",
7825 EvalJs(child->current_frame_host(), "document.cookie;"));
7826
7827 EXPECT_TRUE(ExecJs(child->current_frame_host(), "location.reload();"));
7828
7829 // Check associated cookies according to devtools
7830 base::Value::Dict params = fenced_frame_devtools_client.WaitForNotification(
7831 "Network.requestWillBeSentExtraInfo", /*allow_existing=*/true);
7832
7833 const base::Value::List* associated_cookies =
7834 params.FindList("associatedCookies");
7835
7836 EXPECT_THAT(
7837 associated_cookies,
7838 testing::Pointee(testing::UnorderedElementsAre(
7839 base::test::IsSupersetOfValue(base::test::ParseJsonDict(R"({
7840 "blockedReasons": [ "AnonymousContext" ],
7841 "cookie" : {
7842 "name": "name",
7843 "value": "main"
7844 }
7845 })")),
7846 testing::AllOf(
7847 base::test::IsSupersetOfValue(base::test::ParseJsonDict(R"({
7848 "blockedReasons": [ ],
7849 "cookie" : {
7850 "name": "name",
7851 "value": "credentialless"
7852 }
7853 })")),
7854 testing::ResultOf(
7855 [](const base::Value& dict) {
7856 return dict.GetDict().FindList("blockedReasons");
7857 },
7858 testing::Pointee(testing::IsEmpty()))))));
7859
7860 fenced_frame_devtools_client.DetachProtocolClient();
7861}
7862
7863IN_PROC_BROWSER_TEST_F(NavigationBrowserTestCredentiallessIframe,
Arthur Sonzogni64457592022-11-22 11:08:597864 CredentiallessAttributeIsHonoredByNavigation) {
Antonio Sartori5abc8de2021-07-13 08:42:477865 GURL main_url = embedded_test_server()->GetURL("/page_with_iframe.html");
7866 GURL iframe_url_1 = embedded_test_server()->GetURL("/title1.html");
7867 GURL iframe_url_2 = embedded_test_server()->GetURL("/title2.html");
7868 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7869
7870 // The main page has a child iframe with url `iframe_url_1`.
7871 EXPECT_EQ(1U, main_frame()->child_count());
7872 FrameTreeNode* child = main_frame()->child_at(0);
7873 EXPECT_EQ(iframe_url_1, child->current_url());
Miyoung Shinc9ff4812023-01-05 08:58:057874 EXPECT_FALSE(child->Credentialless());
Arthur Sonzogni64457592022-11-22 11:08:597875 EXPECT_FALSE(child->current_frame_host()->IsCredentialless());
Yifan Luo8048aa8462022-05-06 19:12:017876 EXPECT_EQ(false,
Arthur Sonzogni64457592022-11-22 11:08:597877 EvalJs(child->current_frame_host(), "window.credentialless"));
Antonio Sartori5abc8de2021-07-13 08:42:477878
Arthur Sonzogni64457592022-11-22 11:08:597879 // Changes to the iframe 'credentialless' attribute are propagated to the
Antonio Sartori5abc8de2021-07-13 08:42:477880 // FrameTreeNode. The RenderFrameHost, however, is updated only on navigation.
7881 EXPECT_TRUE(
7882 ExecJs(main_frame(),
Arthur Sonzogni64457592022-11-22 11:08:597883 "document.getElementById('test_iframe').credentialless = true;"));
Miyoung Shinc9ff4812023-01-05 08:58:057884 EXPECT_TRUE(child->Credentialless());
Arthur Sonzogni64457592022-11-22 11:08:597885 EXPECT_FALSE(child->current_frame_host()->IsCredentialless());
Yifan Luo8048aa8462022-05-06 19:12:017886 EXPECT_EQ(false,
Arthur Sonzogni64457592022-11-22 11:08:597887 EvalJs(child->current_frame_host(), "window.credentialless"));
Antonio Sartori5abc8de2021-07-13 08:42:477888
7889 // Create a grandchild iframe.
7890 EXPECT_TRUE(ExecJs(
7891 child, JsReplace("let grandchild = document.createElement('iframe');"
7892 "grandchild.src = $1;"
7893 "document.body.appendChild(grandchild);",
7894 iframe_url_2)));
7895 WaitForLoadStop(web_contents());
7896 EXPECT_EQ(1U, child->child_count());
7897 FrameTreeNode* grandchild = child->child_at(0);
7898
Arthur Sonzogni64457592022-11-22 11:08:597899 // The grandchild FrameTreeNode does not set the 'credentialless'
7900 // attribute. The grandchild RenderFrameHost is not credentialless, since its
7901 // parent RenderFrameHost is not credentialless.
Miyoung Shinc9ff4812023-01-05 08:58:057902 EXPECT_FALSE(grandchild->Credentialless());
Arthur Sonzogni64457592022-11-22 11:08:597903 EXPECT_FALSE(grandchild->current_frame_host()->IsCredentialless());
7904 EXPECT_EQ(false,
7905 EvalJs(grandchild->current_frame_host(), "window.credentialless"));
Antonio Sartori5abc8de2021-07-13 08:42:477906
7907 // Navigate the child iframe same-document. This does not change anything.
7908 EXPECT_TRUE(ExecJs(main_frame(),
7909 JsReplace("document.getElementById('test_iframe')"
7910 " .contentWindow.location.href = $1;",
7911 iframe_url_1.Resolve("#here").spec())));
7912 WaitForLoadStop(web_contents());
Miyoung Shinc9ff4812023-01-05 08:58:057913 EXPECT_TRUE(child->Credentialless());
Arthur Sonzogni64457592022-11-22 11:08:597914 EXPECT_FALSE(child->current_frame_host()->IsCredentialless());
Yifan Luo8048aa8462022-05-06 19:12:017915 EXPECT_EQ(false,
Arthur Sonzogni64457592022-11-22 11:08:597916 EvalJs(child->current_frame_host(), "window.credentialless"));
Antonio Sartori5abc8de2021-07-13 08:42:477917
7918 // Now navigate the child iframe cross-document.
7919 EXPECT_TRUE(ExecJs(
7920 main_frame(), JsReplace("document.getElementById('test_iframe').src = $1",
7921 iframe_url_2)));
7922 WaitForLoadStop(web_contents());
Miyoung Shinc9ff4812023-01-05 08:58:057923 EXPECT_TRUE(child->Credentialless());
Arthur Sonzogni64457592022-11-22 11:08:597924 EXPECT_TRUE(child->current_frame_host()->IsCredentialless());
7925 EXPECT_EQ(true, EvalJs(child->current_frame_host(), "window.credentialless"));
7926 // A credentialless document has a storage key with a nonce.
Mariam Ali8338d9fa2023-07-24 16:57:317927 EXPECT_TRUE(child->current_frame_host()->GetStorageKey().nonce().has_value());
Arthur Sonzogni64457592022-11-22 11:08:597928 base::UnguessableToken credentialless_nonce =
Adithya Srinivasan4e0bb9642023-12-19 15:51:587929 current_frame_host()->GetPage().credentialless_iframes_nonce();
Arthur Sonzogni64457592022-11-22 11:08:597930 EXPECT_EQ(credentialless_nonce,
Mariam Ali8338d9fa2023-07-24 16:57:317931 child->current_frame_host()->GetStorageKey().nonce().value());
Antonio Sartori5abc8de2021-07-13 08:42:477932
7933 // Create a grandchild iframe.
7934 EXPECT_TRUE(ExecJs(
7935 child, JsReplace("let grandchild = document.createElement('iframe');"
7936 "grandchild.id = 'grandchild_iframe';"
7937 "document.body.appendChild(grandchild);",
7938 iframe_url_1)));
7939 EXPECT_EQ(1U, child->child_count());
7940 grandchild = child->child_at(0);
7941
Arthur Sonzogni64457592022-11-22 11:08:597942 // The grandchild does not set the 'credentialless' attribute, but the
7943 // grandchild document is credentialless.
Miyoung Shinc9ff4812023-01-05 08:58:057944 EXPECT_FALSE(grandchild->Credentialless());
Arthur Sonzogni64457592022-11-22 11:08:597945 EXPECT_TRUE(grandchild->current_frame_host()->IsCredentialless());
7946 EXPECT_EQ(true,
7947 EvalJs(grandchild->current_frame_host(), "window.credentialless"));
Antonio Sartori5abc8de2021-07-13 08:42:477948
Arthur Sonzogni64457592022-11-22 11:08:597949 // The storage key's nonce is the same for all credentialless documents in the
7950 // same page.
Mariam Ali8338d9fa2023-07-24 16:57:317951 EXPECT_TRUE(child->current_frame_host()->GetStorageKey().nonce().has_value());
Arthur Sonzogni64457592022-11-22 11:08:597952 EXPECT_EQ(credentialless_nonce,
Mariam Ali8338d9fa2023-07-24 16:57:317953 child->current_frame_host()->GetStorageKey().nonce().value());
Antonio Sartori5cd33732021-07-20 14:52:347954
Antonio Sartori5abc8de2021-07-13 08:42:477955 // Now navigate the grandchild iframe.
7956 EXPECT_TRUE(ExecJs(
7957 child, JsReplace("document.getElementById('grandchild_iframe').src = $1",
7958 iframe_url_2)));
7959 WaitForLoadStop(web_contents());
Arthur Sonzogni64457592022-11-22 11:08:597960 EXPECT_TRUE(grandchild->current_frame_host()->IsCredentialless());
7961 EXPECT_EQ(true,
7962 EvalJs(grandchild->current_frame_host(), "window.credentialless"));
Antonio Sartori5abc8de2021-07-13 08:42:477963
Antonio Sartori5cd33732021-07-20 14:52:347964 // The storage key's nonce is still the same.
Mariam Ali8338d9fa2023-07-24 16:57:317965 EXPECT_TRUE(child->current_frame_host()->GetStorageKey().nonce().has_value());
Arthur Sonzogni64457592022-11-22 11:08:597966 EXPECT_EQ(credentialless_nonce,
Mariam Ali8338d9fa2023-07-24 16:57:317967 child->current_frame_host()->GetStorageKey().nonce().value());
Antonio Sartori5cd33732021-07-20 14:52:347968
Arthur Sonzogni64457592022-11-22 11:08:597969 // Remove the 'credentialless' attribute from the iframe. This propagates to
7970 // the FrameTreeNode. The RenderFrameHost, however, is updated only on
7971 // navigation.
Antonio Sartori5abc8de2021-07-13 08:42:477972 EXPECT_TRUE(
7973 ExecJs(main_frame(),
Arthur Sonzogni64457592022-11-22 11:08:597974 "document.getElementById('test_iframe').credentialless = false;"));
Miyoung Shinc9ff4812023-01-05 08:58:057975 EXPECT_FALSE(child->Credentialless());
Arthur Sonzogni64457592022-11-22 11:08:597976 EXPECT_TRUE(child->current_frame_host()->IsCredentialless());
7977 EXPECT_EQ(true, EvalJs(child->current_frame_host(), "window.credentialless"));
Mariam Ali8338d9fa2023-07-24 16:57:317978 EXPECT_TRUE(child->current_frame_host()->GetStorageKey().nonce().has_value());
Arthur Sonzogni64457592022-11-22 11:08:597979 EXPECT_EQ(credentialless_nonce,
Mariam Ali8338d9fa2023-07-24 16:57:317980 child->current_frame_host()->GetStorageKey().nonce().value());
Antonio Sartori5abc8de2021-07-13 08:42:477981
7982 // Create another grandchild iframe. Even if the parent iframe element does
Arthur Sonzogni64457592022-11-22 11:08:597983 // not have the 'credentialless' attribute anymore, the grandchild document is
7984 // still loaded inside of a credentialless RenderFrameHost, so it will be
7985 // credentialless.
Antonio Sartori5abc8de2021-07-13 08:42:477986 EXPECT_TRUE(ExecJs(
7987 child, JsReplace("let grandchild2 = document.createElement('iframe');"
7988 "document.body.appendChild(grandchild2);",
7989 iframe_url_1)));
7990 EXPECT_EQ(2U, child->child_count());
7991 FrameTreeNode* grandchild2 = child->child_at(1);
Miyoung Shinc9ff4812023-01-05 08:58:057992 EXPECT_FALSE(grandchild2->Credentialless());
Arthur Sonzogni64457592022-11-22 11:08:597993 EXPECT_TRUE(grandchild2->current_frame_host()->IsCredentialless());
7994 EXPECT_EQ(true,
7995 EvalJs(grandchild2->current_frame_host(), "window.credentialless"));
Antonio Sartori5cd33732021-07-20 14:52:347996 EXPECT_TRUE(
Mariam Ali8338d9fa2023-07-24 16:57:317997 grandchild2->current_frame_host()->GetStorageKey().nonce().has_value());
Arthur Sonzogni64457592022-11-22 11:08:597998 EXPECT_EQ(credentialless_nonce,
Mariam Ali8338d9fa2023-07-24 16:57:317999 grandchild2->current_frame_host()->GetStorageKey().nonce().value());
Antonio Sartori5abc8de2021-07-13 08:42:478000
8001 // Navigate the child iframe. Since the iframe element does not set the
Arthur Sonzogni64457592022-11-22 11:08:598002 // 'credentialless' attribute, the resulting RenderFrameHost will not be
8003 // credentialless.
Antonio Sartori5abc8de2021-07-13 08:42:478004 EXPECT_TRUE(
8005 ExecJs(main_frame(),
8006 JsReplace("document.getElementById('test_iframe').src = $1;",
8007 iframe_url_2)));
8008 WaitForLoadStop(web_contents());
Miyoung Shinc9ff4812023-01-05 08:58:058009 EXPECT_FALSE(child->Credentialless());
Arthur Sonzogni64457592022-11-22 11:08:598010 EXPECT_FALSE(child->current_frame_host()->IsCredentialless());
Yifan Luo8048aa8462022-05-06 19:12:018011 EXPECT_EQ(false,
Arthur Sonzogni64457592022-11-22 11:08:598012 EvalJs(child->current_frame_host(), "window.credentialless"));
Mariam Ali8338d9fa2023-07-24 16:57:318013 EXPECT_FALSE(
8014 child->current_frame_host()->GetStorageKey().nonce().has_value());
Antonio Sartori5cd33732021-07-20 14:52:348015
8016 // Now navigate the whole page away.
8017 GURL main_url_b = embedded_test_server()->GetURL(
Arthur Sonzogni64457592022-11-22 11:08:598018 "b.com", "/page_with_credentialless_iframe.html");
Antonio Sartori5cd33732021-07-20 14:52:348019 GURL iframe_url_b = embedded_test_server()->GetURL("b.com", "/title1.html");
8020 EXPECT_TRUE(NavigateToURL(shell(), main_url_b));
8021
Arthur Sonzogni64457592022-11-22 11:08:598022 // The main page has a credentialless child iframe with url `iframe_url_b`.
Antonio Sartori5cd33732021-07-20 14:52:348023 EXPECT_EQ(1U, main_frame()->child_count());
8024 FrameTreeNode* child_b = main_frame()->child_at(0);
8025 EXPECT_EQ(iframe_url_b, child_b->current_url());
Miyoung Shinc9ff4812023-01-05 08:58:058026 EXPECT_TRUE(child_b->Credentialless());
Arthur Sonzogni64457592022-11-22 11:08:598027 EXPECT_TRUE(child_b->current_frame_host()->IsCredentialless());
Arthur Sonzogni064187612022-07-29 16:06:478028 EXPECT_EQ(true,
Arthur Sonzogni64457592022-11-22 11:08:598029 EvalJs(child_b->current_frame_host(), "window.credentialless"));
Antonio Sartori5cd33732021-07-20 14:52:348030
Mariam Ali8338d9fa2023-07-24 16:57:318031 EXPECT_TRUE(
8032 child_b->current_frame_host()->GetStorageKey().nonce().has_value());
Arthur Sonzogni64457592022-11-22 11:08:598033 base::UnguessableToken credentialless_nonce_b =
Adithya Srinivasan4e0bb9642023-12-19 15:51:588034 current_frame_host()->GetPage().credentialless_iframes_nonce();
Arthur Sonzogni64457592022-11-22 11:08:598035 EXPECT_NE(credentialless_nonce, credentialless_nonce_b);
8036 EXPECT_EQ(credentialless_nonce_b,
Mariam Ali8338d9fa2023-07-24 16:57:318037 child_b->current_frame_host()->GetStorageKey().nonce().value());
Antonio Sartori5abc8de2021-07-13 08:42:478038}
8039
tom836aa6d2021-09-28 19:40:168040// Ensures that OpenURLParams::FromNavigationHandle translates navigation params
8041// correctly when used to initiate a navigation in another WebContents.
8042IN_PROC_BROWSER_TEST_F(
8043 NavigationBrowserTest,
8044 FromNavigationHandleTranslatesNavigationParamsCorrectly) {
8045 // Test that the params are translated correctly for a redirected navigation.
8046 const GURL kRedirectedURL(
8047 embedded_test_server()->GetURL("/server-redirect?/simple_page.html"));
8048 NavigationController::LoadURLParams load_params(kRedirectedURL);
8049 TestNavigationManager first_tab_manager(web_contents(), kRedirectedURL);
8050 web_contents()->GetController().LoadURLWithParams(load_params);
8051
8052 // Wait for response to allow the navigation to resolve the redirect.
8053 EXPECT_TRUE(first_tab_manager.WaitForResponse());
8054
8055 // Create LoadURLParams from the navigation after redirection.
8056 NavigationController::LoadURLParams load_url_params(
8057 OpenURLParams::FromNavigationHandle(
8058 first_tab_manager.GetNavigationHandle()));
Kevin McNee37a109792025-04-14 21:16:388059
8060 // Check that the FrameTreeNode id is set, but then clear it since we'll be
8061 // navigating another tab with the rest of the params.
8062 EXPECT_EQ(load_url_params.frame_tree_node_id,
8063 main_frame()->frame_tree_node_id());
8064 load_url_params.frame_tree_node_id = content::FrameTreeNodeId();
8065
tom836aa6d2021-09-28 19:40:168066 Shell* second_tab = CreateBrowser();
8067 TestNavigationManager second_tab_manager(second_tab->web_contents(),
8068 load_url_params.url);
8069 second_tab->web_contents()->GetController().LoadURLWithParams(
8070 load_url_params);
8071
8072 EXPECT_TRUE(second_tab_manager.WaitForResponse());
8073
8074 // Ensure params from the navigation in the first tab are translated to the
8075 // navigation in the second tab as expected.
8076 auto* first_tab_handle = first_tab_manager.GetNavigationHandle();
8077 auto* second_tab_handle = second_tab_manager.GetNavigationHandle();
8078 EXPECT_EQ(embedded_test_server()->GetURL("/simple_page.html"),
8079 second_tab_handle->GetURL());
8080 EXPECT_EQ(first_tab_handle->GetReferrer(), second_tab_handle->GetReferrer());
8081 EXPECT_TRUE(
8082 ui::PageTransitionCoreTypeIs(first_tab_handle->GetPageTransition(),
8083 second_tab_handle->GetPageTransition()));
8084 EXPECT_EQ(first_tab_handle->IsRendererInitiated(),
8085 second_tab_handle->IsRendererInitiated());
8086 EXPECT_EQ(first_tab_handle->GetInitiatorOrigin(),
8087 second_tab_handle->GetInitiatorOrigin());
8088 EXPECT_EQ(first_tab_handle->GetSourceSiteInstance(),
8089 second_tab_handle->GetSourceSiteInstance());
8090 EXPECT_EQ(first_tab_handle->HasUserGesture(),
8091 second_tab_handle->HasUserGesture());
8092 EXPECT_EQ(first_tab_handle->WasStartedFromContextMenu(),
8093 second_tab_handle->WasStartedFromContextMenu());
8094 EXPECT_EQ(first_tab_handle->GetHrefTranslate(),
8095 second_tab_handle->GetHrefTranslate());
8096 EXPECT_EQ(first_tab_handle->GetReloadType(),
8097 second_tab_handle->GetReloadType());
8098 EXPECT_EQ(first_tab_handle->GetRedirectChain(),
8099 second_tab_handle->GetRedirectChain());
8100}
8101
Alex Moshchuk07cd47a2023-03-16 22:44:268102// Regression test for https://crbug.com/1392653. Ensure that loading a URL
8103// that doesn't go through the network stack but does assign a site for its
8104// SiteInstance in an unassigned SiteInstance does not fail. An example of
8105// such a URL is about:srcdoc. This ensures that the SiteInstance's site is set
8106// even on the WillCommitWithoutUrlLoader() path in NavigationRequest.
8107IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
8108 AboutSrcdocInjectedOnAboutBlankPage) {
8109 // Start on an about:blank page, which should stay in an unassigned
8110 // SiteInstance.
8111 EXPECT_TRUE(NavigateToURL(shell(), GURL(url::kAboutBlankURL)));
8112 SiteInstanceImpl* site_instance = current_frame_host()->GetSiteInstance();
8113 EXPECT_FALSE(site_instance->HasSite());
8114
Alex Moshchukc1b10bc2023-04-03 19:04:188115 // The process should be considered unused at this point.
8116 EXPECT_TRUE(site_instance->GetProcess()->IsUnused());
8117
Alex Moshchuk07cd47a2023-03-16 22:44:268118 // Inject a srcdoc iframe into the blank document. This shouldn't really be
8119 // possible on the open web, since an about:blank page with an unassigned
8120 // SiteInstance shouldn't be scriptable by other pages, but it could still
8121 // happen in automation scenarios or through DevTools.
8122 TestNavigationObserver navigation_observer(web_contents());
8123 EXPECT_TRUE(ExecJs(current_frame_host(), JsReplace(R"(
8124 let frame = document.createElement('iframe');
8125 frame.srcdoc = 'test';
8126 document.body.appendChild(frame);
8127 )")));
8128 navigation_observer.Wait();
8129 EXPECT_TRUE(navigation_observer.last_navigation_succeeded());
8130 EXPECT_EQ("about:srcdoc", navigation_observer.last_navigation_url());
8131
8132 // The srcdoc child should stay in its about:blank parent SiteInstance.
8133 EXPECT_EQ(1U, main_frame()->child_count());
8134 FrameTreeNode* child = main_frame()->child_at(0);
8135 EXPECT_EQ(child->current_frame_host()->GetSiteInstance(), site_instance);
8136
8137 // Committing an about:srcdoc navigation currently forces the SiteInstance's
8138 // site to be set. Prior to fixing https://crbug.com/1392653, this happened
8139 // after the actual commit was processed at DidNavigate() time, which is a
8140 // path that is no longer supported, and hence this triggered a NOTREACHED().
8141 // Now, the site should be set before we send the CommitNavigation IPC.
8142 EXPECT_TRUE(site_instance->HasSite());
8143
Sharon Yang47453bb2025-04-25 14:48:098144 if (AreStrictSiteInstancesEnabled()) {
Alex Moshchuk07cd47a2023-03-16 22:44:268145 // When we get into this situation with strict site isolation, the site URL
8146 // currently used is "about:". This may be changed in the future (e.g., to
8147 // an opaque ID).
8148 EXPECT_EQ("about:", site_instance->GetSiteInfo().site_url());
Sharon Yang02279222025-01-15 19:09:198149 } else {
8150 EXPECT_TRUE(site_instance->IsDefaultSiteInstance());
8151 EXPECT_EQ(SiteInstanceImpl::GetDefaultSiteURL(),
8152 site_instance->GetSiteInfo().site_url());
Alex Moshchuk07cd47a2023-03-16 22:44:268153 }
Alex Moshchukc1b10bc2023-04-03 19:04:188154
8155 // Ensure that the process was marked as used as part of setting the site.
8156 EXPECT_FALSE(site_instance->GetProcess()->IsUnused());
Alex Moshchuk07cd47a2023-03-16 22:44:268157}
8158
Arthur Sonzogni410ad682022-10-18 12:17:398159class NavigationBrowserTestWarnSandboxIneffective
8160 : public NavigationBrowserTest {
8161 public:
Arthur Sonzogni410ad682022-10-18 12:17:398162 static constexpr char kSandboxEscapeWarningMessage[] =
8163 "An iframe which has both allow-scripts and allow-same-origin for its "
Arthur Sonzogni82291f42023-03-06 09:57:268164 "sandbox attribute can escape its sandboxing.";
Arthur Sonzogni410ad682022-10-18 12:17:398165};
8166
8167IN_PROC_BROWSER_TEST_F(NavigationBrowserTestWarnSandboxIneffective,
8168 WarnEscapableSandboxSameOrigin) {
8169 EXPECT_TRUE(NavigateToURL(
8170 shell(), embedded_test_server()->GetURL("a.com", "/empty.html")));
8171
8172 WebContentsConsoleObserver console_observer(web_contents());
8173 console_observer.SetPattern(kSandboxEscapeWarningMessage);
8174
8175 // Create same-origin iframe.
8176 EXPECT_TRUE(ExecJs(current_frame_host(), R"(
8177 const iframe = document.createElement("iframe");
8178 iframe.src = location.href; // Same-origin iframe.
8179 iframe.sandbox = "allow-same-origin allow-scripts";
8180 document.body.appendChild(iframe);
8181 )"));
Fergal Daly7723f9d2022-10-29 07:03:138182 ASSERT_TRUE(console_observer.Wait());
Arthur Sonzogni410ad682022-10-18 12:17:398183}
8184
8185IN_PROC_BROWSER_TEST_F(NavigationBrowserTestWarnSandboxIneffective,
8186 WarnEscapableSandboxCrossOrigin) {
8187 EXPECT_TRUE(NavigateToURL(
8188 shell(), embedded_test_server()->GetURL("a.com", "/empty.html")));
8189
8190 WebContentsConsoleObserver console_observer(web_contents());
8191 console_observer.SetPattern(kSandboxEscapeWarningMessage);
8192
8193 // Create cross-origin iframe.
8194 EXPECT_TRUE(ExecJs(current_frame_host(), R"(
8195 const iframe = document.createElement("iframe");
8196 // Cross-origin iframe:
8197 iframe.src = location.href.replace("a.com", "b.com");
8198 iframe.sandbox = "allow-same-origin allow-scripts";
8199 document.body.appendChild(iframe);
8200 )"));
8201
8202 EXPECT_TRUE(WaitForLoadStop(web_contents()));
8203 EXPECT_EQ(console_observer.messages().size(), 0u);
8204}
8205
8206IN_PROC_BROWSER_TEST_F(NavigationBrowserTestWarnSandboxIneffective,
8207 WarnEscapableSandboxSameOriginGrandChild) {
8208 EXPECT_TRUE(NavigateToURL(
8209 shell(), embedded_test_server()->GetURL("a.com", "/empty.html")));
8210
8211 WebContentsConsoleObserver console_observer(web_contents());
8212 console_observer.SetPattern(kSandboxEscapeWarningMessage);
8213
8214 // Create a same-origin doubly nested sandboxed iframe.
8215 EXPECT_TRUE(ExecJs(current_frame_host(), R"(
8216 const child = document.createElement("iframe");
8217 document.body.appendChild(child);
8218
8219 const grand_child = child.contentDocument.createElement("iframe");
8220 grand_child.src = location.href;
8221 grand_child.sandbox = "allow-same-origin allow-scripts";
8222 child.contentDocument.body.appendChild(grand_child);
8223 )"));
8224
8225 EXPECT_TRUE(WaitForLoadStop(web_contents()));
8226 EXPECT_EQ(console_observer.messages().size(), 0u);
8227}
8228
Fergal Daly0bda4ab2023-04-19 05:45:038229// We may have an unload handler in the main frame or a subframe or nowhere.
8230enum class UnloadFrameType {
8231 kMainFrame,
8232 kSubFrame,
8233 kNone,
8234};
8235
8236static std::string ToString(UnloadFrameType v) {
8237 switch (v) {
8238 case UnloadFrameType::kMainFrame:
8239 return "MainFrame";
8240 case UnloadFrameType::kSubFrame:
8241 return "SubFrame";
8242 case UnloadFrameType::kNone:
8243 return "None";
8244 }
8245}
8246
8247// We may navigate the main frame, the subframe that may have an unload handler
8248// or another subframe that will never have an unload handler.
8249enum class NavigateFrameType {
8250 kMainFrame,
8251 kSubFrame,
8252 kOther,
8253};
8254
8255static std::string ToString(NavigateFrameType v) {
8256 switch (v) {
8257 case NavigateFrameType::kMainFrame:
8258 return "MainFrame";
8259 case NavigateFrameType::kSubFrame:
8260 return "SubFrame";
8261 case NavigateFrameType::kOther:
8262 return "Other";
8263 }
8264}
8265
8266void AddUnloadHandler(RenderFrameHostImpl* rfh) {
8267 ASSERT_TRUE(ExecJs(rfh, "addEventListener('unload', () => {})"));
8268 ASSERT_TRUE(rfh->GetSuddenTerminationDisablerState(
8269 blink::mojom::SuddenTerminationDisablerType::kUnloadHandler));
8270}
8271
8272class NavigationSuddenTerminationDisablerTypeBrowserTest
Mingyu Lei30edf462023-11-22 13:45:298273 : public NavigationBrowserTest {
8274 public:
8275 NavigationSuddenTerminationDisablerTypeBrowserTest() {
8276 feature_list_.InitWithFeaturesAndParameters(
8277 /*enabled_features=*/{},
Sandor Majora9a29ad52025-02-20 16:00:148278 /*disabled_features=*/{network::features::kDeprecateUnload});
Mingyu Lei30edf462023-11-22 13:45:298279 }
8280
8281 private:
8282 base::test::ScopedFeatureList feature_list_;
8283};
8284
8285class NavigationSuddenTerminationDisablerTypeWithFrameTypeBrowserTest
8286 : public NavigationSuddenTerminationDisablerTypeBrowserTest,
Fergal Daly0bda4ab2023-04-19 05:45:038287 public ::testing::WithParamInterface<
8288 std::tuple<UnloadFrameType, NavigateFrameType>> {
8289 public:
8290 static std::string DescribeParams(
8291 const ::testing::TestParamInfo<ParamType>& info) {
8292 return "Unload" + ToString(std::get<0>(info.param)) + "Navigate" +
8293 ToString(std::get<1>(info.param));
8294 }
8295
8296 protected:
8297 UnloadFrameType GetUnloadFrameType() { return std::get<0>(GetParam()); }
8298 NavigateFrameType GetNavigateFrameType() { return std::get<1>(GetParam()); }
8299
8300 void MaybeAddUnloadHandler() {
8301 RenderFrameHostImpl* rfh = nullptr;
8302 switch (GetUnloadFrameType()) {
8303 case UnloadFrameType::kMainFrame:
8304 rfh = current_frame_host();
8305 break;
8306 case UnloadFrameType::kSubFrame:
8307 rfh = DescendantRenderFrameHostImplAt(current_frame_host(), {0});
8308 break;
8309 case UnloadFrameType::kNone:
8310 break;
8311 }
8312 if (rfh) {
8313 AddUnloadHandler(rfh);
8314 }
8315 }
8316
8317 RenderFrameHostImpl* GetFrameToNavigate() {
8318 switch (GetNavigateFrameType()) {
8319 case NavigateFrameType::kMainFrame:
8320 return current_frame_host();
8321 case NavigateFrameType::kSubFrame:
8322 return DescendantRenderFrameHostImplAt(current_frame_host(), {0});
8323 case NavigateFrameType::kOther:
8324 return DescendantRenderFrameHostImplAt(current_frame_host(), {1});
8325 }
8326 }
8327
8328 bool NavigatedFrameHasUnload() {
8329 switch (GetNavigateFrameType()) {
8330 case NavigateFrameType::kOther:
8331 // It never has unload.
8332 return false;
8333 case NavigateFrameType::kMainFrame:
8334 // Navigating the main will have unload if there is any unload.
8335 return GetUnloadFrameType() != UnloadFrameType::kNone;
8336 case NavigateFrameType::kSubFrame:
8337 // Navigating the subframe will have unload if the subframe has unload.
8338 return GetUnloadFrameType() == UnloadFrameType::kSubFrame;
8339 }
8340 }
8341};
8342
Mingyu Lei30edf462023-11-22 13:45:298343INSTANTIATE_TEST_SUITE_P(
8344 All,
8345 NavigationSuddenTerminationDisablerTypeWithFrameTypeBrowserTest,
8346 ::testing::Combine(::testing::Values(UnloadFrameType::kMainFrame,
8347 UnloadFrameType::kSubFrame,
8348 UnloadFrameType::kNone),
8349 ::testing::Values(NavigateFrameType::kMainFrame,
8350 NavigateFrameType::kSubFrame,
8351 NavigateFrameType::kOther)),
8352 &NavigationSuddenTerminationDisablerTypeWithFrameTypeBrowserTest::
8353 DescribeParams);
8354
Fergal Daly0bda4ab2023-04-19 05:45:038355// Set up a page with 2 subframes. The main frame or one of the subframes may
8356// have an unload handler. Then navigate one of the frames and verify that we
8357// correctly record which type of frame navigates combined with whether it
8358// involved an unload handler.
Mingyu Lei30edf462023-11-22 13:45:298359IN_PROC_BROWSER_TEST_P(
8360 NavigationSuddenTerminationDisablerTypeWithFrameTypeBrowserTest,
8361 RecordUma) {
Fergal Daly0bda4ab2023-04-19 05:45:038362 ASSERT_TRUE(NavigateToURL(
8363 shell(), embedded_test_server()->GetURL(
8364 "a.com", "/cross_site_iframe_factory.html?a(a,a)")));
8365
Fergal Daly0adc3b22024-01-25 03:56:208366 current_frame_host()->DisableUnloadTimerForTesting();
Fergal Daly0bda4ab2023-04-19 05:45:038367 // Set up the unload handler if needed.
8368 MaybeAddUnloadHandler();
8369
8370 // Navigate the relevant frame and capture histograms.
8371 base::HistogramTester histograms;
8372 ASSERT_TRUE(NavigateFrameToURL(GetFrameToNavigate()->frame_tree_node(),
8373 GURL("about:blank")));
8374
8375 // Check that we got the expected histogram value.
8376 uint32_t expected_histogram_value = 0;
8377 if (GetNavigateFrameType() == NavigateFrameType::kMainFrame) {
8378 expected_histogram_value |= RenderFrameHostImpl::
8379 NavigationSuddenTerminationDisablerType::kMainFrame;
8380 }
8381 if (NavigatedFrameHasUnload()) {
8382 expected_histogram_value |=
8383 RenderFrameHostImpl::NavigationSuddenTerminationDisablerType::kUnload;
8384 }
8385 histograms.ExpectUniqueSample(
8386 "Navigation.SuddenTerminationDisabler.AllOrigins",
8387 expected_histogram_value, 1);
8388 histograms.ExpectUniqueSample(
8389 "Navigation.SuddenTerminationDisabler.SameOrigin",
8390 expected_histogram_value, 1);
8391}
8392
Fergal Daly0bda4ab2023-04-19 05:45:038393// Test that "SameOrigin" only considers frames that have an unbroken path of
8394// same-origin frames from the frame that navigates.
8395IN_PROC_BROWSER_TEST_F(
Mingyu Lei30edf462023-11-22 13:45:298396 NavigationSuddenTerminationDisablerTypeBrowserTest,
Fergal Daly0bda4ab2023-04-19 05:45:038397 NavigationSuddenTerminationDisablerTypeRecordUmaSameOrigin) {
8398 ASSERT_TRUE(NavigateToURL(
8399 shell(), embedded_test_server()->GetURL(
8400 "a.com", "/cross_site_iframe_factory.html?a(b(a))")));
8401
8402 // Set up the unload handler in the a.com subframe.
8403 AddUnloadHandler(
8404 DescendantRenderFrameHostImplAt(current_frame_host(), {0, 0}));
8405 // Navigate the main frame and capture histograms.
8406 base::HistogramTester histograms;
8407 ASSERT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
8408
8409 histograms.ExpectUniqueSample(
8410 "Navigation.SuddenTerminationDisabler.AllOrigins",
8411 RenderFrameHostImpl::NavigationSuddenTerminationDisablerType::kMainFrame |
8412 RenderFrameHostImpl::NavigationSuddenTerminationDisablerType::kUnload,
8413 1);
8414 histograms.ExpectUniqueSample(
8415 "Navigation.SuddenTerminationDisabler.SameOrigin",
8416 RenderFrameHostImpl::NavigationSuddenTerminationDisablerType::kMainFrame,
8417 1);
8418}
8419
8420// Test that we record when the navigation involves restoring from BFCache.
8421// This is tested because the code path for a navigation involving activation
8422// is different from one involving a pageload.
8423IN_PROC_BROWSER_TEST_F(
Mingyu Lei30edf462023-11-22 13:45:298424 NavigationSuddenTerminationDisablerTypeBrowserTest,
Fergal Daly0bda4ab2023-04-19 05:45:038425 NavigationSuddenTerminationDisablerTypeRecordUmaActivation) {
8426 ASSERT_TRUE(NavigateToURL(
8427 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
8428
8429 ASSERT_TRUE(NavigateToURL(
8430 shell(), embedded_test_server()->GetURL("b.com", "/title1.html")));
8431
8432 // Set up the unload handler in the b.com page.
8433 AddUnloadHandler(current_frame_host());
8434 // Navigate the main frame and capture histograms.
8435 base::HistogramTester histograms;
8436 ASSERT_TRUE(HistoryGoBack(web_contents()));
8437
8438 histograms.ExpectUniqueSample(
8439 "Navigation.SuddenTerminationDisabler.AllOrigins",
8440 RenderFrameHostImpl::NavigationSuddenTerminationDisablerType::kMainFrame |
8441 RenderFrameHostImpl::NavigationSuddenTerminationDisablerType::kUnload,
8442 1);
8443 histograms.ExpectUniqueSample(
8444 "Navigation.SuddenTerminationDisabler.SameOrigin",
8445 RenderFrameHostImpl::NavigationSuddenTerminationDisablerType::kMainFrame |
8446 RenderFrameHostImpl::NavigationSuddenTerminationDisablerType::kUnload,
8447 1);
8448}
8449
Fergal Daly0cfc6fe2023-05-11 07:19:258450// Ensure that the first navigation of a subframe away from the initial empty
8451// document is recorded correctly. This does not test all possibilities of
8452// histogram value, just that the scenario is counted under the correct
8453// histogram.
8454IN_PROC_BROWSER_TEST_F(
Mingyu Lei30edf462023-11-22 13:45:298455 NavigationSuddenTerminationDisablerTypeBrowserTest,
Fergal Daly0cfc6fe2023-05-11 07:19:258456 NavigationSuddenTerminationDisablerTypeRecordUmaInitialEmptyDocument) {
8457 GURL url = embedded_test_server()->GetURL("a.com", "/title1.html");
8458 ASSERT_TRUE(NavigateToURL(shell(), url));
8459
8460 // Create a subframe with an unload handler.
8461 ASSERT_TRUE(ExecJs(web_contents(), R"(
8462 var i = document.createElement("iframe");
8463 document.body.appendChild(i);
8464 )"));
8465
8466 AddUnloadHandler(DescendantRenderFrameHostImplAt(current_frame_host(), {0}));
8467
8468 // Navigate the subframe and capture histograms.
8469 base::HistogramTester histograms;
8470 uint32_t expected_histogram_value =
8471 RenderFrameHostImpl::NavigationSuddenTerminationDisablerType::kUnload |
8472 RenderFrameHostImpl::NavigationSuddenTerminationDisablerType::
8473 kInitialEmptyDocument |
8474 RenderFrameHostImpl::NavigationSuddenTerminationDisablerType::kNotHttp;
8475 ASSERT_TRUE(NavigateToURLFromRenderer(
8476 DescendantRenderFrameHostImplAt(current_frame_host(), {0}), url));
8477 histograms.ExpectUniqueSample(
8478 "Navigation.SuddenTerminationDisabler.AllOrigins",
8479 expected_histogram_value, 1);
8480 histograms.ExpectUniqueSample(
8481 "Navigation.SuddenTerminationDisabler.SameOrigin",
8482 expected_histogram_value, 1);
8483}
8484
8485// Ensure that navigations from non-HTTP(S) pages are recorded correctly.
8486IN_PROC_BROWSER_TEST_F(
Mingyu Lei30edf462023-11-22 13:45:298487 NavigationSuddenTerminationDisablerTypeBrowserTest,
Fergal Daly0cfc6fe2023-05-11 07:19:258488 NavigationSuddenTerminationDisablerTypeRecordUmaNotHttp) {
8489 GURL blank_url("about:blank");
8490 GURL url = embedded_test_server()->GetURL("a.com", "/title1.html");
8491 ASSERT_TRUE(NavigateToURL(shell(), blank_url));
8492
8493 AddUnloadHandler(current_frame_host());
8494
8495 // Navigate the subframe and capture histograms.
8496 base::HistogramTester histograms;
8497 uint32_t expected_histogram_value =
8498 RenderFrameHostImpl::NavigationSuddenTerminationDisablerType::kMainFrame |
8499 RenderFrameHostImpl::NavigationSuddenTerminationDisablerType::kUnload |
8500 RenderFrameHostImpl::NavigationSuddenTerminationDisablerType::kNotHttp;
8501 ASSERT_TRUE(NavigateToURL(web_contents(), url));
8502 histograms.ExpectUniqueSample(
8503 "Navigation.SuddenTerminationDisabler.AllOrigins",
8504 expected_histogram_value, 1);
8505 histograms.ExpectUniqueSample(
8506 "Navigation.SuddenTerminationDisabler.SameOrigin",
8507 expected_histogram_value, 1);
8508}
8509
Lukasz Anforowiczd1939012023-05-22 17:09:118510// This is a regression test against https://crbug.com/1145717 - navigating to
8511// invalid/weird URLs (e.g. `about:mumble` or `about://mumble`) shouldn't crash.
8512IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, AboutMumble) {
8513 // First navigate to an arbitrary http site to lock the renderer process.
8514 GURL http_url = embedded_test_server()->GetURL("a.com", "/title1.html");
8515 ASSERT_TRUE(NavigateToURL(shell(), http_url));
8516
8517 // Verify that browser-initiated navigation to `about:mumble` doesn't crash.
8518 //
Charlie Reis45da42612024-02-07 03:41:168519 // The renderer will try to commit the original "about:mumble" URL, which will
8520 // still show up via `window.location.href`, but the URL passed back in the
8521 // DidCommit IPC will be rewritten to `about:blank#blocked` due to
8522 // `RenderFrameImpl`'s `IsValidCommitUrl` and therefore this is what we expect
8523 // in `GetLastCommittedURL`.
Lukasz Anforowiczd1939012023-05-22 17:09:118524 ASSERT_FALSE(NavigateToURL(shell(), GURL("about:mumble")));
8525 EXPECT_EQ(EvalJs(shell(), "window.location.href"), "about:mumble");
8526 EXPECT_EQ(
8527 shell()->web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL(),
8528 GURL("about:blank#blocked"));
8529
8530 // Verify that browser-initiated navigation to `about://mumble` doesn't crash.
8531 ASSERT_FALSE(NavigateToURL(shell(), GURL("about://mumble")));
8532 EXPECT_EQ(EvalJs(shell(), "window.location.href"), "about://mumble");
8533 EXPECT_EQ(
8534 shell()->web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL(),
8535 GURL("about:blank#blocked"));
8536}
8537
Charlie Reisee80f9f2023-07-12 21:56:248538// Ensure that the browser process doesn't see a javascript: URL when opening a
8539// new window to a javascript: URL. These URLs are typically handled on the
8540// renderer side, and the renderer should not send the javascript: URL to the
8541// browser in a navigation request. Previously, this was not correctly handled
8542// for initial navigations to javascript: URLs. See https://crbug.com/1357515.
8543IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, FilterURL_JavascriptURLs) {
8544 GURL http_url = embedded_test_server()->GetURL("a.com", "/title1.html");
8545 ASSERT_TRUE(NavigateToURL(shell(), http_url));
8546
8547 {
8548 SCOPED_TRACE(testing::Message() << "Testing opener case.");
8549 base::HistogramTester histograms;
8550 ShellAddedObserver new_shell_observer;
8551 EXPECT_TRUE(
8552 ExecJs(shell(), "window.open('javascript:window.foo=\"bar\"');"));
8553 WebContents* popup_contents = new_shell_observer.GetShell()->web_contents();
8554 EXPECT_EQ(url::kAboutBlankURL, EvalJs(popup_contents, "location.href"));
8555 // No commit message is sent to the browser in this case, so the last
8556 // committed URL is still empty.
8557 EXPECT_EQ(GURL(), popup_contents->GetLastCommittedURL());
8558 histograms.ExpectTotalCount("BrowserRenderProcessHost.BlockedByFilterURL",
8559 0);
8560
8561 // The javascript: URL should have run.
8562 EXPECT_EQ("bar", EvalJs(popup_contents, "window.foo"));
8563 }
8564
8565 {
8566 SCOPED_TRACE(testing::Message() << "Testing noopener case.");
8567 base::HistogramTester histograms;
8568 ShellAddedObserver new_shell_observer;
8569 EXPECT_TRUE(ExecJs(
8570 shell(),
8571 "window.open('javascript:window.foo=\"bar\"', '', 'noopener');"));
8572 WebContents* popup_contents = new_shell_observer.GetShell()->web_contents();
8573 EXPECT_EQ(url::kAboutBlankURL, EvalJs(popup_contents, "location.href"));
8574 EXPECT_EQ(url::kAboutBlankURL, popup_contents->GetLastCommittedURL());
8575 histograms.ExpectTotalCount("BrowserRenderProcessHost.BlockedByFilterURL",
8576 0);
8577
8578 // The Javascript URL should not have run in the noopener case, because the
8579 // origin should not be inherited according to spec. See:
8580 // https://html.spec.whatwg.org/multipage/document-sequences.html#navigable-target-names%3Acreating-a-new-top-level-traversable
8581 // https://html.spec.whatwg.org/multipage/document-sequences.html#creating-a-new-browsing-context
Alison Gale81f4f2c72024-04-22 19:33:318582 // TODO(crbug.com/40236679): Also prevent the origin from being
Charlie Reisee80f9f2023-07-12 21:56:248583 // inherited.
Chris Fredricksond72b1892025-07-10 22:04:428584 EXPECT_EQ(base::Value(), EvalJs(popup_contents, "window.foo"));
Charlie Reisee80f9f2023-07-12 21:56:248585 }
8586}
8587
8588// Ensure that opening popups to empty URLs does not fail FilterURL. The
8589// renderer process treats empty URLs as about:blank, but the browser process
8590// does not consider them valid and may treat them as attempts to go to the NTP
8591// in some cases. As a result, the renderer should map empty URLs to about:blank
8592// before making navigation requests. See https://crbug.com/1357515.
8593IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, FilterURL_EmptyURL) {
8594 GURL http_url = embedded_test_server()->GetURL("a.com", "/title1.html");
8595 ASSERT_TRUE(NavigateToURL(shell(), http_url));
8596
8597 {
8598 SCOPED_TRACE(testing::Message() << "Testing opener case.");
8599 base::HistogramTester histograms;
8600 ShellAddedObserver new_shell_observer;
8601 EXPECT_TRUE(ExecJs(shell(), "window.open('');"));
8602 WebContents* popup_contents = new_shell_observer.GetShell()->web_contents();
8603 EXPECT_EQ(url::kAboutBlankURL, EvalJs(popup_contents, "location.href"));
8604 EXPECT_EQ(url::kAboutBlankURL, popup_contents->GetLastCommittedURL());
8605 histograms.ExpectTotalCount("BrowserRenderProcessHost.BlockedByFilterURL",
8606 0);
8607 }
8608
8609 {
8610 SCOPED_TRACE(testing::Message() << "Testing noopener case.");
8611 base::HistogramTester histograms;
8612 ShellAddedObserver new_shell_observer;
8613 EXPECT_TRUE(ExecJs(shell(), "window.open('', '', 'noopener');"));
8614 WebContents* popup_contents = new_shell_observer.GetShell()->web_contents();
8615 EXPECT_EQ(url::kAboutBlankURL, EvalJs(popup_contents, "location.href"));
8616 EXPECT_EQ(url::kAboutBlankURL, popup_contents->GetLastCommittedURL());
8617 histograms.ExpectTotalCount("BrowserRenderProcessHost.BlockedByFilterURL",
8618 0);
8619 }
8620}
8621
Alex Moshchuk4bf11e932023-09-12 17:59:558622// Check that an about:blank popup opened from a WebUI page is not allowed to
8623// execute Javascript URLs. chrome:// pages don't allow executing javascript:
8624// URLs, so an about:blank popup opened by one should not be allowed to either,
8625// despite its URL not having the chrome: scheme. See
8626// https://crbug.com/1471305.
8627IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
8628 JavascriptURLBlockedInAboutBlankWebUiPopup) {
8629 GURL webui_url = GetWebUIURL(kChromeUIGpuHost);
8630 ASSERT_TRUE(NavigateToURL(shell(), webui_url));
8631
8632 // Open an about:blank popup which should inherit the WebUI origin, and set
8633 // some state in window.foo.
8634 Shell* new_shell = OpenPopup(shell(), GURL(url::kAboutBlankURL), "popup");
8635 EXPECT_TRUE(new_shell);
8636 EXPECT_TRUE(ExecJs(new_shell, "window.foo = 123;"));
8637 EXPECT_EQ(123, EvalJs(new_shell, "window.foo"));
8638
8639 // Try to execute a Javascript URL that modifies window.foo in the popup via
8640 // a browser-initiated navigation. This should be blocked, and the value of
8641 // window.foo should stay unchanged.
8642 new_shell->LoadURL(GURL("javascript:window.foo=456"));
8643 EXPECT_EQ(123, EvalJs(new_shell, "window.foo"));
8644}
8645
8646// Same test as above, but with a sandboxed about:blank WebUI popup, which
8647// should still not be allowed to execute Javascript URLs.
8648IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
8649 JavascriptURLBlockedInSandboxedWebUiPopup) {
8650 GURL webui_url = GetWebUIURL(kChromeUIGpuHost);
8651 ASSERT_TRUE(NavigateToURL(shell(), webui_url));
8652
8653 // Add a sandboxed about:blank iframe.
8654 {
8655 std::string script =
8656 "var frame = document.createElement('iframe');\n"
8657 "frame.sandbox = 'allow-scripts allow-popups';\n"
8658 "document.body.appendChild(frame);\n";
8659 EXPECT_TRUE(ExecJs(shell(), script));
8660 }
8661
8662 // Open an about:blank popup from that sandboxed iframe, which should have an
8663 // opaque origin with the WebUI origin as the precursor. Set some state in
8664 // window.foo in that popup.
8665 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
8666 ->GetPrimaryFrameTree()
8667 .root();
8668 Shell* new_shell =
8669 OpenPopup(root->child_at(0), GURL(url::kAboutBlankURL), "popup");
8670 EXPECT_TRUE(new_shell);
8671 EXPECT_TRUE(ExecJs(new_shell, "window.foo = 123;"));
8672 EXPECT_EQ(123, EvalJs(new_shell, "window.foo"));
8673
8674 // Try to execute a Javascript URL that modifies window.foo in the popup via
8675 // a browser-initiated navigation. This should be blocked, and the value of
8676 // window.foo should stay unchanged.
8677 new_shell->LoadURL(GURL("javascript:window.foo=456"));
8678 EXPECT_EQ(123, EvalJs(new_shell, "window.foo"));
8679}
8680
Jason Lin218737052023-11-21 08:11:398681// Test navigation with site instances whose storage partitions are fixed.
8682IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, FixedStoragePartition) {
8683 auto* browser_context = shell()->web_contents()->GetBrowserContext();
8684 auto storage_partition_config = StoragePartitionConfig::Create(
8685 browser_context, "NavigationBrowserTest", "FixedStoragePartition", true);
8686 auto url = embedded_test_server()->GetURL("/");
8687 auto* shell = Shell::CreateNewWindow(
8688 browser_context, url,
8689 SiteInstanceImpl::CreateForFixedStoragePartition(
8690 browser_context, url, storage_partition_config),
8691 gfx::Size());
8692
8693 auto GetSiteInstance = [](Shell* shell) {
8694 return static_cast<SiteInstanceImpl*>(
8695 shell->web_contents()->GetSiteInstance());
8696 };
8697
8698 EXPECT_EQ(GetSiteInstance(shell)->GetStoragePartitionConfig(),
8699 storage_partition_config);
8700 EXPECT_TRUE(GetSiteInstance(shell)->IsFixedStoragePartition());
8701
8702 // Check navigation.
8703 ASSERT_TRUE(
8704 NavigateToURL(shell, embedded_test_server()->GetURL("/title1.html")));
8705 EXPECT_EQ(GetSiteInstance(shell)->GetStoragePartitionConfig(),
8706 storage_partition_config);
8707 EXPECT_TRUE(GetSiteInstance(shell)->IsFixedStoragePartition());
8708
8709 // Check opening a window. The new window should stay in the same
8710 // BrowsingInstance and StoragePartition.
8711 {
8712 ShellAddedObserver observer;
8713 auto destination = embedded_test_server()->GetURL("a.com", "/title1.html");
8714 EXPECT_TRUE(ExecJs(shell, JsReplace("window.open($1)", destination),
8715 EXECUTE_SCRIPT_NO_USER_GESTURE));
8716 auto* popup = observer.GetShell();
8717 EXPECT_TRUE(WaitForLoadStop(popup->web_contents()));
8718 EXPECT_EQ(popup->web_contents()->GetLastCommittedURL(), destination);
8719 EXPECT_EQ(GetSiteInstance(popup)->GetBrowsingInstanceId(),
8720 GetSiteInstance(shell)->GetBrowsingInstanceId());
8721 EXPECT_EQ(GetSiteInstance(popup)->GetStoragePartitionConfig(),
8722 storage_partition_config);
8723 EXPECT_TRUE(GetSiteInstance(popup)->IsFixedStoragePartition());
8724 }
8725
8726 // Check opening a window with about:blank at the beginning, and then navigate
8727 // it. It should stay in the same BrowsingInstance and StoragePartition.
8728 {
8729 ShellAddedObserver observer;
8730 EXPECT_TRUE(ExecJs(shell, "newWindow = window.open()",
8731 EXECUTE_SCRIPT_NO_USER_GESTURE));
8732 auto* popup = observer.GetShell();
8733 EXPECT_EQ(GetSiteInstance(popup)->GetStoragePartitionConfig(),
8734 storage_partition_config);
8735 EXPECT_TRUE(GetSiteInstance(popup)->IsFixedStoragePartition());
8736
8737 auto destination = embedded_test_server()->GetURL("a.com", "/title1.html");
8738 EXPECT_TRUE(ExecJs(shell,
8739 JsReplace("newWindow.location.href = $1", destination),
8740 EXECUTE_SCRIPT_NO_USER_GESTURE));
8741 EXPECT_TRUE(WaitForLoadStop(popup->web_contents()));
8742 EXPECT_EQ(popup->web_contents()->GetLastCommittedURL(), destination);
8743 EXPECT_EQ(GetSiteInstance(popup)->GetBrowsingInstanceId(),
8744 GetSiteInstance(shell)->GetBrowsingInstanceId());
8745 EXPECT_EQ(GetSiteInstance(popup)->GetStoragePartitionConfig(),
8746 storage_partition_config);
8747 EXPECT_TRUE(GetSiteInstance(popup)->IsFixedStoragePartition());
8748 }
8749
8750 // Check navigation again.
8751 ASSERT_TRUE(
8752 NavigateToURL(shell, embedded_test_server()->GetURL("/title2.html")));
8753 EXPECT_EQ(GetSiteInstance(shell)->GetStoragePartitionConfig(),
8754 storage_partition_config);
8755 EXPECT_TRUE(GetSiteInstance(shell)->IsFixedStoragePartition());
8756
8757 // Check navigation that triggers a BrowsingInstance swap, and the storage
8758 // partition config should also be preserved.
8759 auto browsing_instance_id = GetSiteInstance(shell)->GetBrowsingInstanceId();
8760 ASSERT_TRUE(NavigateToURL(
8761 shell, embedded_test_server()->GetURL("c.com", "/title2.html")));
8762 EXPECT_NE(GetSiteInstance(shell)->GetBrowsingInstanceId(),
8763 browsing_instance_id);
8764 EXPECT_EQ(GetSiteInstance(shell)->GetStoragePartitionConfig(),
8765 storage_partition_config);
8766 EXPECT_TRUE(GetSiteInstance(shell)->IsFixedStoragePartition());
8767}
8768
David Bokan3592c7d2024-03-27 21:34:198769// Exercises the restored session history traversal code path which uses
8770// RESTORE navigation types, rather than HISTORY_{SAME|DIFFERENT}_DOCUMENT,
8771// which code might erroneously expect. See https://crbug.com/40068335.
8772IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
8773 TraversingToRestoredEntryUsesRestoreType) {
8774 ASSERT_TRUE(
8775 web_contents()->GetController().GetActiveEntry()->IsInitialEntry());
8776
8777 const GURL url1(embedded_test_server()->GetURL("/title1.html"));
8778 const GURL url2(embedded_test_server()->GetURL("/title2.html"));
8779 const GURL url3(embedded_test_server()->GetURL("/title2.html#samedoc"));
8780
8781 EXPECT_TRUE(NavigateToURL(shell(), url1));
8782 EXPECT_TRUE(NavigateToURL(shell(), url2));
8783 EXPECT_TRUE(NavigateToURL(shell(), url3));
8784
8785 // Clone the tab and load the page.
8786 std::unique_ptr<WebContents> new_tab = shell()->web_contents()->Clone();
8787 WebContentsImpl* new_tab_impl = static_cast<WebContentsImpl*>(new_tab.get());
8788 NavigationController& new_controller = new_tab_impl->GetController();
8789
8790 {
8791 TestNavigationObserver clone_observer(new_tab.get());
8792 new_controller.LoadIfNecessary();
8793 clone_observer.Wait();
8794 }
8795
8796 // Back to url2 which is a same document navigation but uses RESTORE.
8797 {
8798 NavigationHandleCommitObserver observer(new_tab.get(), url2);
8799 ASSERT_TRUE(HistoryGoBack(new_tab_impl));
8800 EXPECT_EQ(observer.navigation_type(),
8801 blink::mojom::NavigationType::RESTORE);
8802 }
8803
8804 // Back to url1 which is a cross document navigation but uses RESTORE.
8805 {
8806 NavigationHandleCommitObserver observer(new_tab.get(), url1);
8807 ASSERT_TRUE(HistoryGoBack(new_tab_impl));
8808 EXPECT_EQ(observer.navigation_type(),
8809 blink::mojom::NavigationType::RESTORE);
8810 }
8811}
8812
Fergal Daly0adc3b22024-01-25 03:56:208813class NavigationBrowserTestDeprecateUnloadOptOut
8814 : public NavigationBrowserTest,
8815 public ::testing::WithParamInterface<bool> {
8816 void SetUpCommandLine(base::CommandLine* command_line) override {
8817 NavigationBrowserTest::SetUpCommandLine(command_line);
8818 if (IsOptOutEnabled()) {
8819 scoped_feature_list_.InitWithFeatures(
Sandor Majora9a29ad52025-02-20 16:00:148820 {network::features::kDeprecateUnload,
Fergal Daly0adc3b22024-01-25 03:56:208821 blink::features::kDeprecateUnloadOptOut},
8822 {});
8823 } else {
8824 scoped_feature_list_.InitWithFeatures(
Sandor Majora9a29ad52025-02-20 16:00:148825 {network::features::kDeprecateUnload},
Fergal Daly0adc3b22024-01-25 03:56:208826 {blink::features::kDeprecateUnloadOptOut});
8827 }
8828 }
8829
8830 protected:
8831 bool IsOptOutEnabled() const { return GetParam(); }
8832
8833 private:
8834 base::test::ScopedFeatureList scoped_feature_list_;
8835};
8836
8837INSTANTIATE_TEST_SUITE_P(All,
8838 NavigationBrowserTestDeprecateUnloadOptOut,
8839 ::testing::Bool());
8840
8841// Test that enabled/disabled kDeprecateUnloadOptOut has the desired effect.
8842IN_PROC_BROWSER_TEST_P(NavigationBrowserTestDeprecateUnloadOptOut,
8843 DeprecateUnloadOptOutFlagRespected) {
8844 GURL url_1(embedded_test_server()->GetURL("/title1.html"));
8845 GURL url_2(embedded_test_server()->GetURL("/title2.html"));
8846
8847 // Unload will not run on Android if the page is cacheable.
8848 web_contents()->GetController().GetBackForwardCache().DisableForTesting(
8849 BackForwardCacheImpl::TEST_USES_UNLOAD_EVENT);
8850 // Navigate to a page and install an unload handler with a side-effect.
8851 ASSERT_TRUE(NavigateToURL(web_contents(), url_1));
8852 ASSERT_TRUE(ExecJs(web_contents(), R"(
8853 localStorage.setItem("unload", "not_dispatched");
8854 addEventListener("unload", () => {
8855 localStorage.setItem("unload", "dispatched");
8856 })
8857 )"));
8858
8859 // Navigate to a same-site page (to ensure that the unload handler's
8860 // side-effect is reliably visible).
8861 ASSERT_TRUE(NavigateToURL(web_contents(), url_2));
8862
8863 // Check for the side-effect.
8864 ASSERT_EQ(EvalJs(web_contents(), "localStorage.getItem('unload')"),
8865 IsOptOutEnabled() ? "dispatched" : "not_dispatched");
8866}
8867
Khushal Sagarb13c1512024-04-23 21:51:378868IN_PROC_BROWSER_TEST_F(NavigationBrowserTest, FCPMetrics) {
8869 GURL url_1(embedded_test_server()->GetURL("/title1.html"));
8870 ASSERT_TRUE(NavigateToURL(shell(), url_1));
8871
8872 GURL url_2(embedded_test_server()->GetURL("/title2.html"));
8873 NavigationController::LoadURLParams params(url_2);
8874 params.transition_type = ui::PageTransitionFromInt(
8875 ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
8876 web_contents()->GetController().LoadURLWithParams(params);
8877 WaitForHistogramRecordedInChildProcess(
8878 "Navigation.FCPFrameSubmittedBeforeSurfaceEmbed");
8879}
8880
Rakina Zata Amni14fe3012024-08-29 01:45:488881// Tests that if the main frame has focus before a same-site navigation, it's
8882// kept after navigation.
8883// Regression test for crbug.com/360705823.
8884IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
8885 FocusPreservedOnNavigation_MainFrame) {
8886 GURL url_1(embedded_test_server()->GetURL("/page_with_iframe.html"));
8887 GURL url_2(embedded_test_server()->GetURL("/title2.html"));
8888 ASSERT_TRUE(NavigateToURL(shell(), url_1));
8889
8890 // Set focus on a button in the main frame.
8891 ASSERT_TRUE(ExecJs(current_frame_host(), R"(
8892 let button = document.createElement('button');
8893 document.body.appendChild(button);
8894 button.focus();
8895 )"));
8896 // The main document should have focus.
8897 ASSERT_EQ(true, EvalJs(current_frame_host(), "document.hasFocus();"));
8898
8899 // After navigation, the main document should still have focus.
8900 ASSERT_TRUE(NavigateToURL(shell(), url_2));
8901 ASSERT_EQ(true, EvalJs(current_frame_host(), "document.hasFocus();"));
8902}
8903
8904// Same as the above test, but the focus is on the iframe.
8905IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
8906 FocusPreservedOnNavigation_Subframe) {
8907 GURL url_1(embedded_test_server()->GetURL("/page_with_iframe.html"));
8908 GURL url_2(embedded_test_server()->GetURL("/title2.html"));
8909 ASSERT_TRUE(NavigateToURL(shell(), url_1));
8910
8911 // Set focus on a button in the iframe.
8912 FrameTreeNode* child_ftn = current_frame_host()->child_at(0);
8913 ASSERT_TRUE(ExecJs(child_ftn, R"(
8914 let button = document.createElement('button');
8915 document.body.appendChild(button);
8916 button.focus();
8917 )"));
8918 // The iframe document should have focus.
8919 ASSERT_EQ(true, EvalJs(child_ftn, "document.hasFocus();"));
8920
8921 // After navigation, the child document should still have focus.
8922 ASSERT_TRUE(NavigateFrameToURL(child_ftn, url_2));
8923 ASSERT_EQ(true, EvalJs(child_ftn, "document.hasFocus();"));
8924}
8925
8926// When the navigation is cross-site, focus is not preserved.
8927IN_PROC_BROWSER_TEST_F(NavigationBrowserTest,
8928 FocusNotPreservedOnNavigation_SubframeCrossSite) {
8929 if (!AreAllSitesIsolatedForTesting()) {
8930 GTEST_SKIP() << "Test needs local -> remote swap";
8931 }
8932 GURL url_1(embedded_test_server()->GetURL("a.com", "/page_with_iframe.html"));
8933 GURL url_2(embedded_test_server()->GetURL("b.com", "/title2.html"));
8934 ASSERT_TRUE(NavigateToURL(shell(), url_1));
8935
8936 // Set focus on a button in the iframe.
8937 FrameTreeNode* child_ftn = current_frame_host()->child_at(0);
8938 ASSERT_TRUE(ExecJs(child_ftn, R"(
8939 let button = document.createElement('button');
8940 document.body.appendChild(button);
8941 button.focus();
8942 )"));
8943 // The iframe document should have focus.
8944 ASSERT_EQ(true, EvalJs(child_ftn, "document.hasFocus();"));
8945
8946 // After navigation, the child document should no longer have focus.
8947 ASSERT_TRUE(NavigateFrameToURL(child_ftn, url_2));
8948 ASSERT_EQ(false, EvalJs(child_ftn, "document.hasFocus();"));
8949}
8950
Khushal Sagar3b39a6262024-03-08 15:36:408951class NavigationWithPageSwapBrowserTest : public NavigationBrowserTest {
8952 public:
8953 NavigationWithPageSwapBrowserTest() {
8954 feature_list_.InitAndEnableFeature(blink::features::kPageSwapEvent);
8955 }
8956
8957 bool NavigateBack(WebContentsImpl* contents) {
8958 auto result = EvalJs(contents, JsReplace(
8959 R"(
8960 (async () => {
8961 let pageswapfired = new Promise((resolve) => {
8962 onpageswap = (e) => {
8963 activation = e.activation;
8964 resolve(activation);
8965 };
8966 });
8967 history.back();
8968 let result = await pageswapfired;
8969 return result != null;
8970 })();
8971 )"));
8972 return result.ExtractBool();
8973 }
8974
8975 private:
8976 base::test::ScopedFeatureList feature_list_;
8977};
8978
8979IN_PROC_BROWSER_TEST_F(NavigationWithPageSwapBrowserTest,
8980 PageSwapForInitialEntry) {
8981 ASSERT_TRUE(
8982 web_contents()->GetController().GetActiveEntry()->IsInitialEntry());
8983
8984 // TODO(khushalsagar): Assert that pageswap is fired without activation. The
8985 // test script is hitting an issue for the initial Document.
8986 ASSERT_TRUE(NavigateToURL(web_contents(),
8987 embedded_test_server()->GetURL("/title1.html")));
8988}
8989
8990IN_PROC_BROWSER_TEST_F(NavigationWithPageSwapBrowserTest,
8991 PageSwapWhenTraversingToRestoredEntry) {
8992 ASSERT_TRUE(
8993 web_contents()->GetController().GetActiveEntry()->IsInitialEntry());
8994
8995 const GURL url1(embedded_test_server()->GetURL("/title1.html"));
8996 const GURL url2(embedded_test_server()->GetURL("/title2.html"));
8997
8998 EXPECT_TRUE(NavigateToURL(shell(), url1));
8999 EXPECT_TRUE(NavigateToURL(shell(), url2));
9000
9001 // Clone the tab and load the page.
9002 std::unique_ptr<WebContents> new_tab = shell()->web_contents()->Clone();
9003 WebContentsImpl* new_tab_impl = static_cast<WebContentsImpl*>(new_tab.get());
9004 NavigationController& new_controller = new_tab_impl->GetController();
9005
9006 {
9007 TestNavigationObserver clone_observer(new_tab.get());
9008 new_controller.LoadIfNecessary();
9009 clone_observer.Wait();
9010 }
9011
9012 ASSERT_TRUE(NavigateBack(new_tab_impl));
9013}
9014
Khushal Sagar4a44b722024-04-15 14:40:099015class NavigationBrowserTestPaintHoldingSubframe
9016 : public NavigationBrowserTest,
9017 public ::testing::WithParamInterface<bool> {
9018 public:
9019 NavigationBrowserTestPaintHoldingSubframe() {
Khushal Sagar2f6bc792024-05-23 23:43:579020 // Paint holding for in-process iframes is only enabled when there is a
9021 // ViewTransition.
9022 paint_holding_feature_.InitWithFeatures(
Vladimir Levin5922b4ae2025-05-15 21:14:529023 {blink::features::kPaintHoldingForIframes}, {});
Khushal Sagarc157ae22024-04-25 17:17:259024
Khushal Sagar4a44b722024-04-15 14:40:099025 const bool enable_render_document = GetParam();
9026 if (enable_render_document) {
9027 InitAndEnableRenderDocumentFeature(
9028 &render_document_feature_,
9029 GetRenderDocumentLevelName(RenderDocumentLevel::kSubframe));
9030 } else {
9031 InitAndEnableRenderDocumentFeature(
9032 &render_document_feature_,
9033 GetRenderDocumentLevelName(RenderDocumentLevel::kCrashedFrame));
9034 }
9035
9036 auto* command_line = base::CommandLine::ForCurrentProcess();
9037
9038 // This test requires cross-process iframes.
9039 command_line->AppendSwitch(switches::kSitePerProcess);
9040 }
9041
9042 void SetUp() override {
9043 EnablePixelOutput();
9044 NavigationBrowserTest::SetUp();
9045 }
9046
9047 void SetUpOnMainThread() override {
9048 embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
9049 &NavigationBrowserTestPaintHoldingSubframe::HandleSlowStyleSheet,
9050 base::Unretained(this)));
9051 NavigationBrowserTest::SetUpOnMainThread();
9052 }
9053
9054 protected:
9055 void WaitForStylesheetRequest() {
9056 if (start_response_) {
9057 return;
9058 }
9059
9060 base::RunLoop run_loop;
9061 run_loop_ = &run_loop;
9062 run_loop.Run();
9063 run_loop_ = nullptr;
9064 }
9065
Khushal Sagarc157ae22024-04-25 17:17:259066 void FinishStylesheetRequest(RenderFrameHost* rfh) {
Khushal Sagar4a44b722024-04-15 14:40:099067 std::move(start_response_).Run();
9068 std::move(finish_response_).Run();
Khushal Sagarc157ae22024-04-25 17:17:259069
9070 // Ensure that the stylesheet response unblocks rendering for this Document.
9071 ASSERT_TRUE(ExecJs(rfh, JsReplace(
9072 R"(
9073 (async () => {
9074 let rafFired = new Promise((resolve) => {
9075 requestAnimationFrame(resolve);
9076 });
9077 await rafFired;
9078 })();
9079 )")));
Khushal Sagar4a44b722024-04-15 14:40:099080 }
9081
9082 SkBitmap CopyView(RenderWidgetHostView* view) {
9083 base::RunLoop run_loop;
9084 run_loop_ = &run_loop;
9085
9086 constexpr gfx::Size kOutputSize(10, 10);
9087 view->CopyFromSurface(
9088 gfx::Rect(), kOutputSize,
9089 base::BindOnce(&NavigationBrowserTestPaintHoldingSubframe::OnCopyDone,
9090 base::Unretained(this)));
9091
9092 run_loop.Run();
9093 run_loop_ = nullptr;
9094 return bitmap_;
9095 }
9096
9097 private:
Khushal Sagar2f6bc792024-05-23 23:43:579098 class SlowHttpResponseNoCaching : public SlowHttpResponse {
9099 public:
9100 explicit SlowHttpResponseNoCaching(GotRequestCallback got_request)
9101 : SlowHttpResponse(std::move(got_request)) {}
9102
9103 base::StringPairs ResponseHeaders() override {
9104 auto response = SlowHttpResponse::ResponseHeaders();
9105 // Disable response caching.
9106 response.emplace_back("Cache-Control", "max-age=0");
9107 return response;
9108 }
9109 };
9110
Khushal Sagar4a44b722024-04-15 14:40:099111 std::unique_ptr<net::test_server::HttpResponse> HandleSlowStyleSheet(
9112 const net::test_server::HttpRequest& request) {
9113 if (request.relative_url != "/slow-response") {
9114 return nullptr;
9115 }
Khushal Sagar2f6bc792024-05-23 23:43:579116 return std::make_unique<SlowHttpResponseNoCaching>(base::BindOnce(
Khushal Sagar4a44b722024-04-15 14:40:099117 &NavigationBrowserTestPaintHoldingSubframe::OnStylesheetRequest,
9118 base::Unretained(this)));
9119 }
9120
9121 void OnStylesheetRequest(base::OnceClosure start_response,
9122 base::OnceClosure finish_response) {
9123 start_response_ = std::move(start_response);
9124 finish_response_ = std::move(finish_response);
9125
9126 if (run_loop_) {
9127 run_loop_->Quit();
9128 }
9129 }
9130
9131 void OnCopyDone(const SkBitmap& bitmap) {
9132 bitmap_ = bitmap;
9133 run_loop_->Quit();
9134 }
9135
9136 SkBitmap bitmap_;
9137 raw_ptr<base::RunLoop> run_loop_;
9138 base::OnceClosure start_response_;
9139 base::OnceClosure finish_response_;
Khushal Sagarc157ae22024-04-25 17:17:259140 base::test::ScopedFeatureList paint_holding_feature_;
Khushal Sagar4a44b722024-04-15 14:40:099141 base::test::ScopedFeatureList render_document_feature_;
9142};
9143
9144IN_PROC_BROWSER_TEST_P(NavigationBrowserTestPaintHoldingSubframe, Basic) {
Khushal Sagar4a44b722024-04-15 14:40:099145 auto* web_contents = shell()->web_contents();
9146
9147 GURL main_url(
9148 embedded_test_server()->GetURL("/render-blocking-mainframe.html"));
9149 ASSERT_TRUE(NavigateToURL(web_contents, main_url));
9150
9151 const std::string iframe_id = "iframe_id";
Khushal Sagarc157ae22024-04-25 17:17:259152 RenderFrameHostImpl* subframe_rfh = nullptr;
Khushal Sagar4a44b722024-04-15 14:40:099153
9154 {
Khushal Sagar4a44b722024-04-15 14:40:099155 const std::string kCreateIFrameWithID = R"(
9156 const iframe = document.createElement("iframe");
9157 iframe.id = $1;
9158 document.body.appendChild(iframe);
9159 )";
9160 ASSERT_TRUE(ExecJs(web_contents->GetPrimaryMainFrame(),
9161 JsReplace(kCreateIFrameWithID, "iframe_id")));
9162
Khushal Sagarc157ae22024-04-25 17:17:259163 GURL subframe_url(embedded_test_server()->GetURL(
9164 "a.com", "/render-blocking-subframe.html"));
Khushal Sagar4a44b722024-04-15 14:40:099165 TestNavigationObserver load_observer(web_contents);
9166 ASSERT_TRUE(
9167 BeginNavigateIframeToURL(web_contents, "iframe_id", subframe_url));
Khushal Sagarc157ae22024-04-25 17:17:259168 load_observer.WaitForNavigationFinished();
Khushal Sagar4a44b722024-04-15 14:40:099169 WaitForStylesheetRequest();
Khushal Sagar4a44b722024-04-15 14:40:099170
Khushal Sagarc157ae22024-04-25 17:17:259171 // We should have a cross-process iframe which is a local root.
9172 subframe_rfh = static_cast<RenderFrameHostImpl*>(
9173 ChildFrameAt(web_contents->GetPrimaryMainFrame(), 0));
9174 ASSERT_TRUE(subframe_rfh);
9175 ASSERT_TRUE(subframe_rfh->is_local_root());
9176
9177 FinishStylesheetRequest(subframe_rfh);
9178 WaitForCopyableViewInWebContents(web_contents);
9179 }
Khushal Sagar4a44b722024-04-15 14:40:099180
9181 // The frame is displaying blue.
9182 WaitForCopyableViewInFrame(subframe_rfh);
9183 auto bitmap = CopyView(web_contents->GetRenderWidgetHostView());
9184 EXPECT_EQ(bitmap.getColor(4, 4), SK_ColorBLUE) << cc::GetPNGDataUrl(bitmap);
9185
9186 {
9187 GURL subframe_url(embedded_test_server()->GetURL(
9188 "a.com", "/render-blocking-subframe.html?red"));
9189
9190 TestNavigationObserver load_observer(web_contents);
9191 ASSERT_TRUE(
9192 BeginNavigateIframeToURL(web_contents, "iframe_id", subframe_url));
9193 load_observer.WaitForNavigationFinished();
9194 WaitForStylesheetRequest();
9195
9196 // The subframe RFH could have changed.
9197 subframe_rfh = static_cast<RenderFrameHostImpl*>(
9198 ChildFrameAt(web_contents->GetPrimaryMainFrame(), 0));
9199 ASSERT_TRUE(subframe_rfh);
9200 ASSERT_TRUE(subframe_rfh->is_local_root());
9201 }
9202
9203 // The frame should continue to display blue from paint holding.
9204 WaitForCopyableViewInWebContents(web_contents);
9205 bitmap = CopyView(web_contents->GetRenderWidgetHostView());
9206 EXPECT_EQ(bitmap.getColor(4, 4), SK_ColorBLUE);
9207
9208 // Respond to the stylesheet request which will resume rendering in the
9209 // subframe.
Khushal Sagarc157ae22024-04-25 17:17:259210 FinishStylesheetRequest(subframe_rfh);
9211
9212 // Now the frame is displaying red.
9213 WaitForCopyableViewInFrame(subframe_rfh);
9214 bitmap = CopyView(web_contents->GetRenderWidgetHostView());
9215 EXPECT_EQ(bitmap.getColor(4, 4), SK_ColorRED) << cc::GetPNGDataUrl(bitmap);
9216}
9217
Khushal Sagar2f6bc792024-05-23 23:43:579218IN_PROC_BROWSER_TEST_P(NavigationBrowserTestPaintHoldingSubframe,
9219 BasicInProcessIframe) {
9220 auto* web_contents = shell()->web_contents();
9221
9222 GURL main_url(
9223 embedded_test_server()->GetURL("/render-blocking-mainframe.html"));
9224 ASSERT_TRUE(NavigateToURL(web_contents, main_url));
9225
9226 const std::string iframe_id = "iframe_id";
9227 RenderFrameHostImpl* subframe_rfh = nullptr;
9228
9229 {
9230 const std::string kCreateIFrameWithID = R"(
9231 const iframe = document.createElement("iframe");
9232 iframe.id = $1;
9233 document.body.appendChild(iframe);
9234 )";
9235 ASSERT_TRUE(ExecJs(web_contents->GetPrimaryMainFrame(),
9236 JsReplace(kCreateIFrameWithID, "iframe_id")));
9237
9238 GURL subframe_url(
9239 embedded_test_server()->GetURL("/render-blocking-subframe.html"));
9240 TestNavigationObserver load_observer(web_contents);
9241 ASSERT_TRUE(
9242 BeginNavigateIframeToURL(web_contents, "iframe_id", subframe_url));
9243 load_observer.WaitForNavigationFinished();
9244 WaitForStylesheetRequest();
9245
9246 // We should have a same-process iframe which is not a local root.
9247 subframe_rfh = static_cast<RenderFrameHostImpl*>(
9248 ChildFrameAt(web_contents->GetPrimaryMainFrame(), 0));
9249 ASSERT_TRUE(subframe_rfh);
9250 ASSERT_FALSE(subframe_rfh->is_local_root());
9251 ASSERT_EQ(subframe_rfh->GetProcess(),
9252 web_contents->GetPrimaryMainFrame()->GetProcess());
9253
9254 FinishStylesheetRequest(subframe_rfh);
9255 WaitForCopyableViewInWebContents(web_contents);
9256 }
9257
9258 {
9259 const std::string kInjectVTOptIn = R"(
9260 const style = document.createElement("style");
9261 style.innerHTML = "@view-transition { navigation: auto; }"
9262 document.head.appendChild(style);
9263 )";
9264 ASSERT_TRUE(ExecJs(subframe_rfh, kInjectVTOptIn));
9265 }
9266
9267 // The frame is displaying blue.
9268 WaitForCopyableViewInFrame(subframe_rfh);
9269 auto bitmap = CopyView(web_contents->GetRenderWidgetHostView());
9270 EXPECT_EQ(bitmap.getColor(4, 4), SK_ColorBLUE) << cc::GetPNGDataUrl(bitmap);
9271
9272 {
9273 GURL subframe_url(
9274 embedded_test_server()->GetURL("/render-blocking-subframe.html?red"));
9275
9276 TestNavigationObserver load_observer(web_contents);
9277 ASSERT_TRUE(
9278 BeginNavigateIframeToURL(web_contents, "iframe_id", subframe_url));
9279 load_observer.WaitForNavigationFinished();
9280 WaitForStylesheetRequest();
9281
9282 // The subframe RFH could have changed.
9283 subframe_rfh = static_cast<RenderFrameHostImpl*>(
9284 ChildFrameAt(web_contents->GetPrimaryMainFrame(), 0));
9285 ASSERT_TRUE(subframe_rfh);
9286 ASSERT_FALSE(subframe_rfh->is_local_root());
9287 ASSERT_EQ(subframe_rfh->GetProcess(),
9288 web_contents->GetPrimaryMainFrame()->GetProcess());
9289 }
9290
9291 // The frame should continue to display blue from paint holding.
9292 WaitForCopyableViewInWebContents(web_contents);
9293 bitmap = CopyView(web_contents->GetRenderWidgetHostView());
9294 EXPECT_EQ(bitmap.getColor(4, 4), SK_ColorBLUE);
9295
9296 // Respond to the stylesheet request which will resume rendering in the
9297 // subframe.
9298 FinishStylesheetRequest(subframe_rfh);
9299
9300 // Now the frame is displaying red.
9301 WaitForCopyableViewInFrame(subframe_rfh);
9302 bitmap = CopyView(web_contents->GetRenderWidgetHostView());
9303 EXPECT_EQ(bitmap.getColor(4, 4), SK_ColorRED) << cc::GetPNGDataUrl(bitmap);
9304}
9305
Khushal Sagarc157ae22024-04-25 17:17:259306IN_PROC_BROWSER_TEST_P(NavigationBrowserTestPaintHoldingSubframe, CrossOrigin) {
9307 auto* web_contents = shell()->web_contents();
9308
9309 GURL main_url(
9310 embedded_test_server()->GetURL("/render-blocking-mainframe.html"));
9311 ASSERT_TRUE(NavigateToURL(web_contents, main_url));
9312
9313 const std::string iframe_id = "iframe_id";
9314 RenderFrameHostImpl* subframe_rfh = nullptr;
9315
9316 {
9317 const std::string kCreateIFrameWithID = R"(
9318 const iframe = document.createElement("iframe");
9319 iframe.id = $1;
9320 document.body.appendChild(iframe);
9321 )";
9322 ASSERT_TRUE(ExecJs(web_contents->GetPrimaryMainFrame(),
9323 JsReplace(kCreateIFrameWithID, "iframe_id")));
9324
9325 GURL subframe_url(embedded_test_server()->GetURL(
9326 "a.com", "/render-blocking-subframe.html"));
9327 TestNavigationObserver load_observer(web_contents);
9328 ASSERT_TRUE(
9329 BeginNavigateIframeToURL(web_contents, "iframe_id", subframe_url));
9330 load_observer.WaitForNavigationFinished();
9331 WaitForStylesheetRequest();
9332
9333 // We should have a cross-process iframe which is a local root.
9334 subframe_rfh = static_cast<RenderFrameHostImpl*>(
9335 ChildFrameAt(web_contents->GetPrimaryMainFrame(), 0));
9336 ASSERT_TRUE(subframe_rfh);
9337 ASSERT_TRUE(subframe_rfh->is_local_root());
9338
9339 FinishStylesheetRequest(subframe_rfh);
9340 WaitForCopyableViewInWebContents(web_contents);
9341 }
9342
9343 // The frame is displaying blue.
9344 WaitForCopyableViewInFrame(subframe_rfh);
9345 auto bitmap = CopyView(web_contents->GetRenderWidgetHostView());
9346 EXPECT_EQ(bitmap.getColor(4, 4), SK_ColorBLUE) << cc::GetPNGDataUrl(bitmap);
9347
9348 {
9349 GURL subframe_url(embedded_test_server()->GetURL(
9350 "b.com", "/render-blocking-subframe.html?red"));
9351
9352 TestNavigationObserver load_observer(web_contents);
9353 ASSERT_TRUE(
9354 BeginNavigateIframeToURL(web_contents, "iframe_id", subframe_url));
9355 load_observer.WaitForNavigationFinished();
9356 WaitForStylesheetRequest();
9357
9358 // The subframe RFH could have changed.
9359 subframe_rfh = static_cast<RenderFrameHostImpl*>(
9360 ChildFrameAt(web_contents->GetPrimaryMainFrame(), 0));
9361 ASSERT_TRUE(subframe_rfh);
9362 ASSERT_TRUE(subframe_rfh->is_local_root());
9363 }
9364
9365 // The frame is displaying white (from the main frame) because paint holding
9366 // is disabled.
9367 WaitForCopyableViewInWebContents(web_contents);
9368 bitmap = CopyView(web_contents->GetRenderWidgetHostView());
9369 EXPECT_EQ(bitmap.getColor(4, 4), SK_ColorWHITE);
9370
9371 // Respond to the stylesheet request which will resume rendering in the
9372 // subframe.
9373 FinishStylesheetRequest(subframe_rfh);
9374
9375 // Now the frame is displaying red.
9376 WaitForCopyableViewInFrame(subframe_rfh);
9377 bitmap = CopyView(web_contents->GetRenderWidgetHostView());
9378 EXPECT_EQ(bitmap.getColor(4, 4), SK_ColorRED) << cc::GetPNGDataUrl(bitmap);
9379}
9380
9381IN_PROC_BROWSER_TEST_P(NavigationBrowserTestPaintHoldingSubframe,
9382 CrashSubframe) {
9383 auto* web_contents = shell()->web_contents();
9384
9385 GURL main_url(
9386 embedded_test_server()->GetURL("/render-blocking-mainframe.html"));
9387 ASSERT_TRUE(NavigateToURL(web_contents, main_url));
9388
9389 const std::string iframe_id = "iframe_id";
9390 RenderFrameHostImpl* subframe_rfh = nullptr;
9391
9392 {
9393 const std::string kCreateIFrameWithID = R"(
9394 const iframe = document.createElement("iframe");
9395 iframe.id = $1;
9396 document.body.appendChild(iframe);
9397 )";
9398 ASSERT_TRUE(ExecJs(web_contents->GetPrimaryMainFrame(),
9399 JsReplace(kCreateIFrameWithID, "iframe_id")));
9400
9401 GURL subframe_url(embedded_test_server()->GetURL(
9402 "a.com", "/render-blocking-subframe.html"));
9403 TestNavigationObserver load_observer(web_contents);
9404 ASSERT_TRUE(
9405 BeginNavigateIframeToURL(web_contents, "iframe_id", subframe_url));
9406 load_observer.WaitForNavigationFinished();
9407 WaitForStylesheetRequest();
9408
9409 // We should have a cross-process iframe which is a local root.
9410 subframe_rfh = static_cast<RenderFrameHostImpl*>(
9411 ChildFrameAt(web_contents->GetPrimaryMainFrame(), 0));
9412 ASSERT_TRUE(subframe_rfh);
9413 ASSERT_TRUE(subframe_rfh->is_local_root());
9414
9415 FinishStylesheetRequest(subframe_rfh);
9416 WaitForCopyableViewInWebContents(web_contents);
9417 }
9418
9419 // The frame is displaying blue.
9420 WaitForCopyableViewInFrame(subframe_rfh);
9421 auto bitmap = CopyView(web_contents->GetRenderWidgetHostView());
9422 EXPECT_EQ(bitmap.getColor(4, 4), SK_ColorBLUE) << cc::GetPNGDataUrl(bitmap);
9423
9424 // Crash the subframe.
9425 {
9426 auto* process = subframe_rfh->GetProcess();
9427 content::ScopedAllowRendererCrashes allow_renderer_crashes(process);
9428
9429 RenderProcessHostWatcher watcher(
9430 process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
9431 process->Shutdown(content::RESULT_CODE_KILLED);
9432 watcher.Wait();
9433 }
9434
9435 {
9436 GURL subframe_url(embedded_test_server()->GetURL(
9437 "a.com", "/render-blocking-subframe.html?red"));
9438
9439 TestNavigationObserver load_observer(web_contents);
9440 ASSERT_TRUE(
9441 BeginNavigateIframeToURL(web_contents, "iframe_id", subframe_url));
9442 load_observer.WaitForNavigationFinished();
9443 WaitForStylesheetRequest();
9444
9445 // The subframe RFH could have changed.
9446 subframe_rfh = static_cast<RenderFrameHostImpl*>(
9447 ChildFrameAt(web_contents->GetPrimaryMainFrame(), 0));
9448 ASSERT_TRUE(subframe_rfh);
9449 ASSERT_TRUE(subframe_rfh->is_local_root());
9450 }
9451
9452 // The frame is displaying white (from the main frame) because paint holding
9453 // is disabled.
9454 WaitForCopyableViewInWebContents(web_contents);
9455 bitmap = CopyView(web_contents->GetRenderWidgetHostView());
9456 EXPECT_EQ(bitmap.getColor(4, 4), SK_ColorWHITE) << cc::GetPNGDataUrl(bitmap);
9457
9458 // Respond to the stylesheet request which will resume rendering in the
9459 // subframe.
9460 FinishStylesheetRequest(subframe_rfh);
Khushal Sagar4a44b722024-04-15 14:40:099461
9462 // Now the frame is displaying red.
9463 WaitForCopyableViewInFrame(subframe_rfh);
9464 bitmap = CopyView(web_contents->GetRenderWidgetHostView());
9465 EXPECT_EQ(bitmap.getColor(4, 4), SK_ColorRED) << cc::GetPNGDataUrl(bitmap);
9466}
9467
9468INSTANTIATE_TEST_SUITE_P(All,
9469 NavigationBrowserTestPaintHoldingSubframe,
9470 ::testing::Bool());
9471
Jiacheng Guo0d8204a2024-06-25 06:12:279472RenderFrameHostImpl* GetMainFrameSpeculativeRFH(WebContentsImpl* web_contents) {
9473 return web_contents->GetPrimaryFrameTree()
9474 .root()
9475 ->render_manager()
9476 ->speculative_frame_host();
9477}
9478
Jiacheng Guo9c2f6f52024-06-28 02:57:459479void VerifyDeferSpeculativeRFHActionUMA(const base::HistogramTester& tester,
9480 DeferSpeculativeRFHAction action) {
9481 tester.ExpectUniqueSample("Navigation.DeferSpeculativeRFHAction",
9482 static_cast<int>(action), 1);
9483}
9484
Jiacheng Guoc1510462024-06-21 00:07:119485class DeferSpeculativeRFHCreationTest : public NavigationBrowserTest {
9486 public:
9487 DeferSpeculativeRFHCreationTest() {
9488 feature_list_.InitAndEnableFeature(features::kDeferSpeculativeRFHCreation);
Jiacheng Guo0d8204a2024-06-25 06:12:279489 // Enable render document for all frames to ensure a speculative RFH
9490 // will be created during navigation.
9491 InitAndEnableRenderDocumentFeature(
9492 &render_document_feature_,
9493 GetRenderDocumentLevelName(RenderDocumentLevel::kAllFrames));
Jiacheng Guoc1510462024-06-21 00:07:119494 }
9495
9496 private:
9497 base::test::ScopedFeatureList feature_list_;
Jiacheng Guo0d8204a2024-06-25 06:12:279498 base::test::ScopedFeatureList render_document_feature_;
Jiacheng Guoc1510462024-06-21 00:07:119499};
9500
Jiacheng Guo6ecb7ce2024-06-25 07:07:219501class DeferSpeculativeRFHCreationRenderProcessTest
9502 : public NavigationBrowserTest,
9503 public ::testing::WithParamInterface<bool> {
9504 public:
9505 DeferSpeculativeRFHCreationRenderProcessTest()
9506 : warmup_spare_render_process_(GetParam()) {
Jiacheng Guo6ecb7ce2024-06-25 07:07:219507 std::map<std::string, std::string> parameters = {
Devon Loehrc0138d8d2025-03-04 01:11:429508 {"warmup_spare_process", base::ToString(GetParam())},
Jiacheng Guo6ecb7ce2024-06-25 07:07:219509 };
9510 defer_rfh_feature_list_.InitAndEnableFeatureWithParameters(
9511 features::kDeferSpeculativeRFHCreation, parameters);
Jiacheng Guo297820872025-02-17 04:49:379512 android_spare_rederer_feature_.InitAndEnableFeatureWithParameters(
9513 features::kAndroidWarmUpSpareRendererWithTimeout,
9514 base::FieldTrialParams{{"spare_renderer_memory_threshold", "0"}});
Jiacheng Guo6ecb7ce2024-06-25 07:07:219515 InitAndEnableRenderDocumentFeature(
9516 &render_document_feature_,
9517 GetRenderDocumentLevelName(RenderDocumentLevel::kAllFrames));
9518 }
9519
Jiacheng Guo543893b02024-06-27 05:56:539520 // A new renderer process will only be created for a cross-RFH navigation if
9521 // it involves a SiteInstanceGroup change, which will happen if site isolation
9522 // or BFCache is turned on
Jiacheng Guo297820872025-02-17 04:49:379523 bool WillWarmupSpareRenderProcess() { return warmup_spare_render_process_; }
9524
9525 bool WillAllocateNewProcess() {
9526 return AreAllSitesIsolatedForTesting() || IsBackForwardCacheEnabled();
Jiacheng Guo543893b02024-06-27 05:56:539527 }
Jiacheng Guo6ecb7ce2024-06-25 07:07:219528
Jiacheng Guo6ecb7ce2024-06-25 07:07:219529 private:
Jiacheng Guo6ecb7ce2024-06-25 07:07:219530 bool warmup_spare_render_process_;
Jiacheng Guo6ecb7ce2024-06-25 07:07:219531 base::test::ScopedFeatureList defer_rfh_feature_list_;
Jiacheng Guo297820872025-02-17 04:49:379532 base::test::ScopedFeatureList android_spare_rederer_feature_;
Jiacheng Guo6ecb7ce2024-06-25 07:07:219533 base::test::ScopedFeatureList render_document_feature_;
9534};
9535
Jiacheng Guoc1510462024-06-21 00:07:119536// Verify the common flow for with DeferSpeculativeRFHCreation feature.
9537// The creation of the speculative RFH will be deferred until the network
9538// request is sent.
Jiacheng Guo6ecb7ce2024-06-25 07:07:219539IN_PROC_BROWSER_TEST_P(DeferSpeculativeRFHCreationRenderProcessTest,
Jiacheng Guo543893b02024-06-27 05:56:539540 SpeculativeRFHCreationDeferred) {
Jiacheng Guoc1510462024-06-21 00:07:119541 ASSERT_TRUE(NavigateToURL(
9542 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
Jiacheng Guo297820872025-02-17 04:49:379543 RenderProcessHost* first_navigation_process =
9544 main_frame()->render_manager()->current_frame_host()->GetProcess();
Jiacheng Guoc1510462024-06-21 00:07:119545 WebContentsImpl* web_contents =
9546 static_cast<WebContentsImpl*>(shell()->web_contents());
Patrick Monettec5201262024-10-09 18:36:579547 SpareRenderProcessHostManagerImpl::Get().CleanupSparesForTesting();
Patrick Monetted1757682024-09-24 20:59:199548 SpareRenderProcessHostStartedObserver spare_started_observer;
Jiacheng Guoc1510462024-06-21 00:07:119549
9550 GURL url = embedded_test_server()->GetURL("b.com", "/title1.html");
9551 TestNavigationManager nav_manager(web_contents, url);
Jiacheng Guo9c2f6f52024-06-28 02:57:459552 base::HistogramTester histogram_tester;
Jiacheng Guoc1510462024-06-21 00:07:119553
9554 // The speculative RFH shall not be created when the navigation request is
9555 // created.
9556 ASSERT_TRUE(BeginNavigateToURLFromRenderer(web_contents, url));
Jiacheng Guo9c2f6f52024-06-28 02:57:459557 DeferSpeculativeRFHAction expected_action =
9558 WillWarmupSpareRenderProcess()
9559 ? DeferSpeculativeRFHAction::kDeferredWithRenderProcessWarmUp
9560 : DeferSpeculativeRFHAction::kDeferredWithoutRenderProcessWarmUp;
9561 VerifyDeferSpeculativeRFHActionUMA(histogram_tester, expected_action);
Jiacheng Guoc1510462024-06-21 00:07:119562 NavigationRequest* navigation_request =
9563 NavigationRequest::From(nav_manager.GetNavigationHandle());
Patrick Monetted1757682024-09-24 20:59:199564 RenderProcessHost* created_process = nullptr;
Jiacheng Guo543893b02024-06-27 05:56:539565 if (WillWarmupSpareRenderProcess()) {
Patrick Monetted1757682024-09-24 20:59:199566 created_process = spare_started_observer.WaitForSpareRenderProcessStarted();
Jiacheng Guo6ecb7ce2024-06-25 07:07:219567 }
Jiacheng Guo543893b02024-06-27 05:56:539568 ASSERT_EQ(!!created_process, WillWarmupSpareRenderProcess());
Jiacheng Guoc1510462024-06-21 00:07:119569 ASSERT_TRUE(navigation_request);
9570 // The navigation manager pauses the navigation in the WillStartRequest
9571 // throttle. The speculative RFH will be created after the throttle completes
9572 // and the navigation request is sent.
9573 ASSERT_EQ(navigation_request->state(),
9574 NavigationRequest::NavigationState::WILL_START_REQUEST);
9575 // The loader will not be created until the WillStartRequest throttle check
9576 // completed.
9577 ASSERT_FALSE(navigation_request->HasLoader());
9578 ASSERT_FALSE(GetMainFrameSpeculativeRFH(web_contents));
9579 ASSERT_EQ(navigation_request->GetAssociatedRFHType(),
9580 NavigationRequest::AssociatedRenderFrameHostType::NONE);
9581
9582 nav_manager.WaitForSpeculativeRenderFrameHostCreation();
9583 // The speculative RFH shall be created after sending the request.
9584 ASSERT_EQ(navigation_request->state(),
9585 NavigationRequest::NavigationState::WILL_START_REQUEST);
9586 ASSERT_TRUE(navigation_request->HasLoader());
9587 RenderFrameHostImplWrapper speculative_rfh(
9588 GetMainFrameSpeculativeRFH(web_contents));
9589 ASSERT_TRUE(speculative_rfh);
9590 ASSERT_EQ(navigation_request->GetAssociatedRFHType(),
9591 NavigationRequest::AssociatedRenderFrameHostType::SPECULATIVE);
Jiacheng Guo297820872025-02-17 04:49:379592 if (!WillAllocateNewProcess()) {
9593 ASSERT_EQ(speculative_rfh->GetSiteInstance()->GetProcess(),
9594 first_navigation_process);
9595 } else if (WillWarmupSpareRenderProcess()) {
Jiacheng Guo6ecb7ce2024-06-25 07:07:219596 ASSERT_EQ(speculative_rfh->GetSiteInstance()->GetProcess(),
9597 created_process);
9598 }
Jiacheng Guoc1510462024-06-21 00:07:119599 // The speculative RFH shall become the primary RFH when the navigation is
9600 // committed.
9601 ASSERT_TRUE(nav_manager.WaitForNavigationFinished());
9602 ASSERT_FALSE(GetMainFrameSpeculativeRFH(web_contents));
9603 ASSERT_EQ(main_frame()->render_manager()->current_frame_host(),
9604 speculative_rfh.get());
9605}
9606
Jiacheng Guo6ecb7ce2024-06-25 07:07:219607INSTANTIATE_TEST_SUITE_P(All,
9608 DeferSpeculativeRFHCreationRenderProcessTest,
9609 ::testing::Bool());
9610
Jiacheng Guoc1510462024-06-21 00:07:119611// Verify that navigating from a crashed page will create a speculative
9612// RFH at once.
9613IN_PROC_BROWSER_TEST_F(DeferSpeculativeRFHCreationTest,
9614 NavigationFromCrashedFrameNotDeferred) {
9615 ASSERT_TRUE(NavigateToURL(
9616 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
9617 WebContentsImpl* web_contents =
9618 static_cast<WebContentsImpl*>(shell()->web_contents());
9619 // Crash the frame.
9620 {
9621 auto* process = main_frame()
9622 ->GetRenderFrameHostManager()
9623 .current_frame_host()
9624 ->GetProcess();
9625 content::ScopedAllowRendererCrashes allow_renderer_crashes(process);
9626
9627 RenderProcessHostWatcher watcher(
9628 process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
9629 process->Shutdown(content::RESULT_CODE_KILLED);
9630 watcher.Wait();
9631 }
9632
9633 GURL url = embedded_test_server()->GetURL("a.com", "/title2.html");
9634 TestNavigationManager nav_manager(web_contents, url);
9635 // Navigation from a crashed frame shall immediately create a speculative RFH.
Jiacheng Guo9c2f6f52024-06-28 02:57:459636 base::HistogramTester histogram_tester;
Jiacheng Guoc1510462024-06-21 00:07:119637 shell()->LoadURL(url);
Jiacheng Guo9c2f6f52024-06-28 02:57:459638 VerifyDeferSpeculativeRFHActionUMA(histogram_tester,
9639 DeferSpeculativeRFHAction::kNotDeferred);
Jiacheng Guoc1510462024-06-21 00:07:119640 NavigationRequest* navigation_request =
9641 NavigationRequest::From(nav_manager.GetNavigationHandle());
9642 ASSERT_EQ(navigation_request->state(),
9643 NavigationRequest::NavigationState::WILL_START_REQUEST);
9644 ASSERT_FALSE(navigation_request->HasLoader());
9645 ASSERT_FALSE(GetMainFrameSpeculativeRFH(web_contents));
9646 ASSERT_EQ(navigation_request->GetAssociatedRFHType(),
9647 NavigationRequest::AssociatedRenderFrameHostType::CURRENT);
9648 ASSERT_TRUE(nav_manager.WaitForNavigationFinished());
9649 ASSERT_FALSE(GetMainFrameSpeculativeRFH(web_contents));
9650}
9651
Jiacheng Guoc1510462024-06-21 00:07:119652// Verify that the creation of the speculative RFH is not deferred for the
9653// web pages.
9654IN_PROC_BROWSER_TEST_F(DeferSpeculativeRFHCreationTest,
9655 CreationNotDeferredForWebUI) {
9656 ASSERT_TRUE(NavigateToURL(
9657 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
9658 WebContentsImpl* web_contents =
9659 static_cast<WebContentsImpl*>(shell()->web_contents());
9660
9661 GURL url = GetWebUIURL(kChromeUIGpuHost);
9662 TestNavigationManager nav_manager(web_contents, url);
9663 // The speculative RFH shall be created when the navigation starts.
Jiacheng Guo9c2f6f52024-06-28 02:57:459664 base::HistogramTester histogram_tester;
Jiacheng Guoc1510462024-06-21 00:07:119665 shell()->LoadURL(url);
Jiacheng Guo9c2f6f52024-06-28 02:57:459666 VerifyDeferSpeculativeRFHActionUMA(histogram_tester,
9667 DeferSpeculativeRFHAction::kNotDeferred);
Minoru Chikamunea4a252fa2025-02-17 09:13:319668 ASSERT_TRUE(nav_manager.WaitForRequestStart());
Jiacheng Guoc1510462024-06-21 00:07:119669 NavigationRequest* navigation_request = main_frame()->navigation_request();
9670 ASSERT_EQ(navigation_request->state(),
Minoru Chikamunea4a252fa2025-02-17 09:13:319671 NavigationRequest::NavigationState::WILL_START_REQUEST);
Jiacheng Guoc1510462024-06-21 00:07:119672 ASSERT_FALSE(navigation_request->HasLoader());
9673 ASSERT_TRUE(GetMainFrameSpeculativeRFH(web_contents));
9674 ASSERT_EQ(navigation_request->GetAssociatedRFHType(),
9675 NavigationRequest::AssociatedRenderFrameHostType::SPECULATIVE);
9676 ASSERT_TRUE(nav_manager.WaitForNavigationFinished());
9677 ASSERT_FALSE(GetMainFrameSpeculativeRFH(web_contents));
9678}
9679
9680// Verify that the creation of the speculative RFH is not deferred for the
9681// pages without a URL loader.
9682IN_PROC_BROWSER_TEST_F(DeferSpeculativeRFHCreationTest,
Jiacheng Guo0d8204a2024-06-25 06:12:279683 CreationNotDeferredWithoutURLLoader) {
Jiacheng Guoc1510462024-06-21 00:07:119684 ASSERT_TRUE(NavigateToURL(
9685 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
9686 WebContentsImpl* web_contents =
9687 static_cast<WebContentsImpl*>(shell()->web_contents());
9688
9689 GURL url("about:blank");
Jiacheng Guoc1510462024-06-21 00:07:119690 // The speculative RFH shall be created when the navigation starts.
Jiacheng Guo9c2f6f52024-06-28 02:57:459691 base::HistogramTester histogram_tester;
Jiacheng Guoc1510462024-06-21 00:07:119692 ASSERT_TRUE(BeginNavigateToURLFromRenderer(web_contents, url));
Jiacheng Guo9c2f6f52024-06-28 02:57:459693 VerifyDeferSpeculativeRFHActionUMA(histogram_tester,
9694 DeferSpeculativeRFHAction::kNotDeferred);
Rakina Zata Amnidf8d1b42024-07-26 09:21:259695 ASSERT_TRUE(WaitForLoadStop(web_contents));
Jiacheng Guoc1510462024-06-21 00:07:119696 ASSERT_FALSE(GetMainFrameSpeculativeRFH(web_contents));
9697}
9698
9699// Verify that the created speculative RFH after the network request will
9700// be correctly replaced if the redirection points to a different site.
9701IN_PROC_BROWSER_TEST_F(DeferSpeculativeRFHCreationTest,
Jiacheng Guo0d8204a2024-06-25 06:12:279702 SpeculativeRFHWithRedirect) {
Jiacheng Guoc1510462024-06-21 00:07:119703 ASSERT_TRUE(NavigateToURL(
9704 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
9705 WebContentsImpl* web_contents =
9706 static_cast<WebContentsImpl*>(shell()->web_contents());
9707
9708 GURL redirect_url = embedded_test_server()->GetURL("b.com", "/title1.html");
9709 GURL url = embedded_test_server()->GetURL(
Jiacheng Guo0d8204a2024-06-25 06:12:279710 "c.com", "/server-redirect?" + redirect_url.spec());
Jiacheng Guoc1510462024-06-21 00:07:119711 TestNavigationManager nav_manager(web_contents, url);
9712
9713 // The speculative RFH shall not be created when the navigation request is
9714 // created.
Jiacheng Guo9c2f6f52024-06-28 02:57:459715 base::HistogramTester histogram_tester;
Jiacheng Guoc1510462024-06-21 00:07:119716 ASSERT_TRUE(BeginNavigateToURLFromRenderer(web_contents, url));
Jiacheng Guo9c2f6f52024-06-28 02:57:459717 VerifyDeferSpeculativeRFHActionUMA(
9718 histogram_tester,
9719 DeferSpeculativeRFHAction::kDeferredWithoutRenderProcessWarmUp);
Jiacheng Guoc1510462024-06-21 00:07:119720 NavigationRequest* navigation_request =
9721 NavigationRequest::From(nav_manager.GetNavigationHandle());
9722 ASSERT_TRUE(navigation_request);
9723 // The navigation manager pauses the navigation in the WillStartRequest
9724 // throttle. The speculative RFH will be created after the throttle completes
9725 // and the navigation request is sent.
9726 ASSERT_EQ(navigation_request->state(),
9727 NavigationRequest::NavigationState::WILL_START_REQUEST);
9728 // The loader will not be created until the WillStartRequest throttle check
9729 // completed.
9730 ASSERT_FALSE(navigation_request->HasLoader());
9731 ASSERT_FALSE(GetMainFrameSpeculativeRFH(web_contents));
9732 ASSERT_EQ(navigation_request->GetAssociatedRFHType(),
9733 NavigationRequest::AssociatedRenderFrameHostType::NONE);
9734
9735 nav_manager.WaitForSpeculativeRenderFrameHostCreation();
9736 // The speculative RFH shall be created after sending the request.
9737 ASSERT_EQ(navigation_request->state(),
9738 NavigationRequest::NavigationState::WILL_START_REQUEST);
9739 ASSERT_TRUE(navigation_request->HasLoader());
9740 ASSERT_TRUE(GetMainFrameSpeculativeRFH(web_contents));
9741 RenderFrameHostImplWrapper speculative_rfh(
9742 GetMainFrameSpeculativeRFH(web_contents));
9743 ASSERT_TRUE(speculative_rfh);
9744 ASSERT_EQ(navigation_request->GetAssociatedRFHType(),
9745 NavigationRequest::AssociatedRenderFrameHostType::SPECULATIVE);
9746
9747 // After receiving the redirect, a new speculative RFH shall be created for
9748 // the new site if site isolation is enabled.
9749 ASSERT_TRUE(nav_manager.WaitForResponse());
9750 if (AreAllSitesIsolatedForTesting()) {
9751 ASSERT_TRUE(speculative_rfh.IsDestroyed());
9752 }
9753 RenderFrameHostImplWrapper new_speculative_rfh(
9754 GetMainFrameSpeculativeRFH(web_contents));
9755 ASSERT_TRUE(new_speculative_rfh);
9756 ASSERT_EQ(navigation_request->GetAssociatedRFHType(),
9757 NavigationRequest::AssociatedRenderFrameHostType::SPECULATIVE);
9758 // The speculative RFH shall become the primary RFH when the navigation is
9759 // committed.
9760 ASSERT_TRUE(nav_manager.WaitForNavigationFinished());
9761 ASSERT_FALSE(GetMainFrameSpeculativeRFH(web_contents));
9762 ASSERT_EQ(main_frame()->render_manager()->current_frame_host(),
9763 new_speculative_rfh.get());
9764}
9765
9766// Test that if there is a navigation pending for commit, the deferred
9767// speculative RFH will not be created event after the request is sent. The new
9768// navigation will be queued until the pending navigation commits.
9769IN_PROC_BROWSER_TEST_F(DeferSpeculativeRFHCreationTest,
Jiacheng Guo543893b02024-06-27 05:56:539770 NavigateWithPendingCommit) {
9771 // TODO(crbug.com/349487596): Enable the test after fixing the unrepsonive
9772 // renderer issue.
9773 if (!AreAllSitesIsolatedForTesting() && !IsBackForwardCacheEnabled()) {
9774 return;
9775 }
Jiacheng Guoc1510462024-06-21 00:07:119776 ASSERT_TRUE(NavigateToURL(
9777 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
9778 WebContentsImpl* web_contents =
9779 static_cast<WebContentsImpl*>(shell()->web_contents());
9780 NavigationLogger logger(web_contents);
9781
9782 // Create first navigation and pause before commit.
9783 GURL url_b = embedded_test_server()->GetURL("b.com", "/title1.html");
9784 TestNavigationManager nav_manager_b(web_contents, url_b);
9785 ASSERT_TRUE(BeginNavigateToURLFromRenderer(web_contents, url_b));
9786 nav_manager_b.WaitForSpeculativeRenderFrameHostCreation();
9787 RenderFrameHostImplWrapper speculative_rfh_b(
9788 GetMainFrameSpeculativeRFH(web_contents));
9789 ASSERT_TRUE(speculative_rfh_b);
9790 ASSERT_TRUE(nav_manager_b.WaitForResponse());
9791 nav_manager_b.ResumeNavigation();
9792 CommitNavigationPauser commit_pauser(speculative_rfh_b.get());
9793 commit_pauser.WaitForCommitAndPause();
9794
9795 // Navigate to a new site, a new speculative RFH will not be created because
9796 // of the pending navigation.
9797 GURL url_c = embedded_test_server()->GetURL("c.com", "/title1.html");
9798 TestNavigationManager nav_manager_c(web_contents, url_c);
9799 ASSERT_TRUE(BeginNavigateToURLFromRenderer(web_contents, url_c));
9800 NavigationRequest* navigation_request =
9801 NavigationRequest::From(nav_manager_c.GetNavigationHandle());
9802 ASSERT_TRUE(nav_manager_c.WaitForRequestStart());
9803 // Normally, the new navigation will create a speculative RFH after the
9804 // network request is sent, but since there is a pre-existing speculative RFH
9805 // for a pending commit navigation, the new navigation won't create a
9806 // speculative RFH at this point.
9807 nav_manager_c.ResumeNavigation();
9808 ASSERT_EQ(navigation_request->state(),
9809 NavigationRequest::NavigationState::WILL_START_REQUEST);
9810 ASSERT_TRUE(navigation_request->HasLoader());
9811 // Verify that the speculative RFH is not replaced by the new navigation.
9812 ASSERT_EQ(speculative_rfh_b.get(), GetMainFrameSpeculativeRFH(web_contents));
9813 ASSERT_FALSE(speculative_rfh_b.IsDestroyed());
9814 ASSERT_EQ(navigation_request->GetAssociatedRFHType(),
9815 NavigationRequest::AssociatedRenderFrameHostType::NONE);
9816
9817 commit_pauser.ResumePausedCommit();
9818 ASSERT_TRUE(nav_manager_b.WaitForNavigationFinished());
9819 // Verify that a new speculative RFH will be created after the pending
9820 // navigation is committed.
9821 ASSERT_TRUE(nav_manager_c.WaitForResponse());
9822 RenderFrameHostImplWrapper new_speculative_rfh(
9823 GetMainFrameSpeculativeRFH(web_contents));
9824 ASSERT_TRUE(new_speculative_rfh);
9825 ASSERT_EQ(navigation_request->state(),
9826 NavigationRequest::NavigationState::WILL_PROCESS_RESPONSE);
9827 ASSERT_EQ(navigation_request->GetAssociatedRFHType(),
9828 NavigationRequest::AssociatedRenderFrameHostType::SPECULATIVE);
9829 ASSERT_TRUE(nav_manager_c.WaitForNavigationFinished());
9830 ASSERT_FALSE(GetMainFrameSpeculativeRFH(web_contents));
9831 ASSERT_EQ(main_frame()->render_manager()->current_frame_host(),
9832 new_speculative_rfh.get());
9833
9834 // Check that all the navigations has been committed.
9835 auto results = logger.results();
9836 ASSERT_EQ(2u, results.size());
9837 EXPECT_TRUE(results[0].committed);
9838 EXPECT_EQ(url_b, results[0].url);
9839 EXPECT_TRUE(results[1].committed);
9840 EXPECT_EQ(url_c, results[1].url);
9841}
9842
Liam Bradyf93e81f2025-08-05 16:26:319843// Verify that navigating from about:blank will defer the creation of the
9844// speculative RFH until the network request is sent.
9845IN_PROC_BROWSER_TEST_F(DeferSpeculativeRFHCreationTest,
9846 NavigateFromAboutBlankDeferred) {
9847 ASSERT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
9848 WebContentsImpl* web_contents =
9849 static_cast<WebContentsImpl*>(shell()->web_contents());
9850
9851 GURL url = embedded_test_server()->GetURL("a.com", "/title1.html");
9852 TestNavigationManager nav_manager(web_contents, url);
9853 // Navigation from about:blank creates a new render frame host.
9854 ASSERT_TRUE(BeginNavigateToURLFromRenderer(web_contents, url));
9855 ASSERT_TRUE(nav_manager.WaitForRequestStart());
9856 NavigationRequest* navigation_request =
9857 NavigationRequest::From(nav_manager.GetNavigationHandle());
9858
9859 nav_manager.WaitForSpeculativeRenderFrameHostCreation();
9860 // The speculative RFH shall be created after sending the request.
9861 ASSERT_EQ(navigation_request->state(),
9862 NavigationRequest::NavigationState::WILL_START_REQUEST);
9863 ASSERT_TRUE(navigation_request->HasLoader());
9864 RenderFrameHostImplWrapper speculative_rfh(
9865 GetMainFrameSpeculativeRFH(web_contents));
9866 ASSERT_TRUE(speculative_rfh);
9867 ASSERT_EQ(navigation_request->GetAssociatedRFHType(),
9868 NavigationRequest::AssociatedRenderFrameHostType::SPECULATIVE);
9869
9870 ASSERT_TRUE(nav_manager.WaitForResponse());
9871 ASSERT_TRUE(GetMainFrameSpeculativeRFH(web_contents));
9872 ASSERT_EQ(navigation_request->GetAssociatedRFHType(),
9873 NavigationRequest::AssociatedRenderFrameHostType::SPECULATIVE);
9874 ASSERT_TRUE(nav_manager.WaitForNavigationFinished());
9875 ASSERT_FALSE(GetMainFrameSpeculativeRFH(web_contents));
9876}
9877
Jiacheng Guo0d8204a2024-06-25 06:12:279878class DeferSpeculativeRFHCreationReuseRFHTest : public NavigationBrowserTest {
9879 public:
9880 DeferSpeculativeRFHCreationReuseRFHTest() {
9881 feature_list_.InitAndEnableFeature(features::kDeferSpeculativeRFHCreation);
9882 render_document_feature_.InitAndDisableFeature(features::kRenderDocument);
9883 }
9884
9885 private:
9886 base::test::ScopedFeatureList feature_list_;
9887 base::test::ScopedFeatureList render_document_feature_;
9888};
9889
9890// Verify that navigating with the same RFH will reuse the RFH at once.
9891IN_PROC_BROWSER_TEST_F(DeferSpeculativeRFHCreationReuseRFHTest,
9892 ReuseSameRFHNotDeferred) {
9893 ASSERT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
9894 WebContentsImpl* web_contents =
9895 static_cast<WebContentsImpl*>(shell()->web_contents());
9896
9897 GURL url = embedded_test_server()->GetURL("a.com", "/title1.html");
9898 TestNavigationManager nav_manager(web_contents, url);
9899 // Navigation from about:blank will reuse the render frame host.
9900 ASSERT_TRUE(BeginNavigateToURLFromRenderer(web_contents, url));
9901 NavigationRequest* navigation_request =
9902 NavigationRequest::From(nav_manager.GetNavigationHandle());
9903 ASSERT_EQ(navigation_request->state(),
9904 NavigationRequest::NavigationState::WILL_START_REQUEST);
Jiacheng Guo297820872025-02-17 04:49:379905 ASSERT_FALSE(GetMainFrameSpeculativeRFH(web_contents));
Jiacheng Guo0d8204a2024-06-25 06:12:279906 ASSERT_FALSE(navigation_request->HasLoader());
Jiacheng Guo297820872025-02-17 04:49:379907 ASSERT_TRUE(nav_manager.WaitForResponse());
Jiacheng Guo0d8204a2024-06-25 06:12:279908 ASSERT_FALSE(GetMainFrameSpeculativeRFH(web_contents));
9909 ASSERT_EQ(navigation_request->GetAssociatedRFHType(),
9910 NavigationRequest::AssociatedRenderFrameHostType::CURRENT);
9911 ASSERT_TRUE(nav_manager.WaitForNavigationFinished());
9912 ASSERT_FALSE(GetMainFrameSpeculativeRFH(web_contents));
9913}
9914
Erik Anderson5d151f132024-08-14 06:18:059915class VisualPropertiesSynchronization : public NavigationBrowserTest {
9916 public:
9917 VisualPropertiesSynchronization() {
9918 // The deferral of the RFH prevents the potential race condition that this
9919 // regression test is attempting to check.
9920 feature_list_.InitAndDisableFeature(features::kDeferSpeculativeRFHCreation);
9921
9922 auto* command_line = base::CommandLine::ForCurrentProcess();
9923
9924 // This test requires cross-process iframes.
9925 command_line->AppendSwitch(switches::kSitePerProcess);
9926 }
9927
9928 private:
9929 base::test::ScopedFeatureList feature_list_;
9930};
9931
9932// Regression test for https://crbug.com/352093463.
9933// Verify that when a cross-origin subframe initiates a top-level navigation to
9934// a same-origin (with respect to itself) URL, that the visual properties
9935// are invalidated correctly.
9936IN_PROC_BROWSER_TEST_F(VisualPropertiesSynchronization,
Erik Andersonedf445002024-09-13 17:18:339937 RemoteToLocalTransition) {
Erik Anderson5d151f132024-08-14 06:18:059938 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
9939 GURL url_b_top_level(embedded_test_server()->GetURL("b.com", "/title1.html"));
9940 GURL url_b_iframe(embedded_test_server()->GetURL("b.com", "/title2.html"));
9941
9942 WebContentsImpl* web_contents =
9943 static_cast<WebContentsImpl*>(shell()->web_contents());
9944
9945 EXPECT_TRUE(NavigateToURL(shell(), url_a));
9946 EXPECT_TRUE(ExecJs(shell(),
9947 "let iframe = document.createElement('iframe');"
9948 "iframe.id = 'iframe_id';"
9949 "iframe.src = 'about:blank';"
9950 "iframe.style = 'width: 0px; height: 0px;';"
9951 "document.body.appendChild(iframe);"));
9952 EXPECT_TRUE(WaitForLoadStop(web_contents));
9953
9954 // Start a navigation of the top-level document to b.com. Before we leave
9955 // the original a.com, load an iframe to b.com which will be hosted in the
9956 // same b.com process.
9957 content::TestNavigationManager top_level_navigation(web_contents,
9958 url_b_top_level);
9959 EXPECT_TRUE(ExecJs(
9960 shell(), JsReplace("window.location.replace($1);", url_b_top_level)));
9961
9962 // Don't proceed with the top-level navigation (wait while we complete our
9963 // iframe nav to the same origin to commit).
9964 EXPECT_TRUE(top_level_navigation.WaitForLoaderStart());
9965 EXPECT_FALSE(top_level_navigation.was_committed());
9966
9967 // Navigate the iframe to a 'b.com' URL, making a remote frame within the
9968 // a.com page.
9969 FrameTreeNode* root =
9970 FrameTreeNode::From(web_contents->GetPrimaryMainFrame());
9971 CHECK(root->child_count() > 0u);
9972 FrameTreeNode* iframe = root->child_at(0);
9973 TestFrameNavigationObserver iframe_load_observer_first(
9974 iframe->current_frame_host());
9975 ASSERT_TRUE(
9976 BeginNavigateIframeToURL(web_contents, "iframe_id", url_b_iframe));
9977 iframe_load_observer_first.WaitForCommit();
9978 EXPECT_EQ(url_b_iframe, iframe_load_observer_first.last_committed_url());
9979 EXPECT_TRUE(iframe_load_observer_first.last_navigation_succeeded());
9980
9981 // Confirm the cross-process iframe process is not (yet) the main frame's
9982 // process.
9983 RenderFrameHostImpl* subframe_rfh = nullptr;
9984 subframe_rfh = static_cast<RenderFrameHostImpl*>(
9985 ChildFrameAt(web_contents->GetPrimaryMainFrame(), 0));
9986 ASSERT_TRUE(subframe_rfh);
9987 RenderProcessHost* cross_origin_iframe_process = subframe_rfh->GetProcess();
9988 ASSERT_NE(cross_origin_iframe_process,
9989 web_contents->GetPrimaryMainFrame()->GetProcess());
9990
9991 // Allow the top-level navigation to proceed.
9992 EXPECT_FALSE(top_level_navigation.was_committed());
9993 EXPECT_TRUE(top_level_navigation.WaitForNavigationFinished());
9994 EXPECT_TRUE(top_level_navigation.was_committed());
9995
9996 // The main frame should now be using the same 'b.com' renderer.
9997 ASSERT_EQ(cross_origin_iframe_process,
9998 web_contents->GetPrimaryMainFrame()->GetProcess());
9999
10000 // Verify that the browser side's VisualProperties' visible viewport size is
10001 // non-zero.
10002 root = FrameTreeNode::From(web_contents->GetPrimaryMainFrame());
10003 auto* root_rwh = root->current_frame_host()->GetRenderWidgetHost();
10004 std::optional<blink::VisualProperties> visual_properties =
10005 root_rwh->LastComputedVisualProperties();
10006 EXPECT_TRUE(visual_properties);
Chris Harrelson8af71b92025-02-14 01:05:4710007 EXPECT_NE(gfx::Size(0, 0),
10008 visual_properties->visible_viewport_size_device_px);
Erik Anderson5d151f132024-08-14 06:18:0510009
Erik Andersonedf445002024-09-13 17:18:3310010 // Ensure a frame has been produced.
10011 ASSERT_TRUE(
10012 EvalJsAfterLifecycleUpdate(web_contents->GetPrimaryMainFrame(), "", "")
Chris Fredricksonbc209522025-07-25 17:05:5210013 .is_ok());
Erik Andersonedf445002024-09-13 17:18:3310014
Erik Anderson5d151f132024-08-14 06:18:0510015 // Verify the renderer received the correct size for the viewport.
10016 EXPECT_GT(EvalJs(web_contents->GetPrimaryMainFrame(), "window.innerWidth;")
10017 .ExtractDouble(),
10018 0);
10019 EXPECT_GT(EvalJs(web_contents->GetPrimaryMainFrame(), "window.innerHeight;")
10020 .ExtractDouble(),
10021 0);
10022}
10023
Jiacheng Guo6dcc29c2024-07-26 00:28:4010024#if BUILDFLAG(IS_ANDROID)
10025class AndroidPrewarmSpareRendererTest
10026 : public NavigationBrowserTest,
10027 public ::testing::WithParamInterface<std::tuple<std::string, bool>> {
10028 public:
10029 AndroidPrewarmSpareRendererTest() {
10030 std::map<std::string, std::string> parameters = {
10031 {"spare_renderer_creation_timing", std::get<0>(GetParam())},
10032 {"spare_renderer_timeout_seconds",
10033 std::get<1>(GetParam()) ? "10" : "-1"},
10034 };
10035 feature_list_.InitWithFeaturesAndParameters(
10036 /*enabled_features=*/{{features::kAndroidWarmUpSpareRendererWithTimeout,
10037 parameters}},
10038 /*disabled_features=*/{{features::kSpareRendererForSitePerProcess}});
10039 }
10040
10041 void SetUpCommandLine(base::CommandLine* command_line) override {
10042 // Enable site per process so that the navigation will take
10043 // the spare process.
10044 command_line->AppendSwitch(switches::kSitePerProcess);
10045 }
10046
10047 bool SpareRendererHasTimeout() { return std::get<1>(GetParam()); }
10048
10049 private:
10050 base::test::ScopedFeatureList feature_list_;
10051};
10052
10053INSTANTIATE_TEST_SUITE_P(
10054 All,
10055 AndroidPrewarmSpareRendererTest,
10056 testing::Combine(
10057 testing::Values(
10058 features::kAndroidSpareRendererCreationAfterLoading,
10059 features::kAndroidSpareRendererCreationAfterFirstPaint,
10060 features::kAndroidSpareRendererCreationDelayedDuringLoading),
10061 testing::Bool()));
10062
10063IN_PROC_BROWSER_TEST_P(AndroidPrewarmSpareRendererTest, ReuseSpareRenderer) {
Patrick Monettea45111342024-09-24 00:33:2910064 auto& spare_manager = SpareRenderProcessHostManagerImpl::Get();
Patrick Monettec5201262024-10-09 18:36:5710065 spare_manager.CleanupSparesForTesting();
Patrick Monetted1757682024-09-24 20:59:1910066 SpareRenderProcessHostStartedObserver spare_started_observer;
Jiacheng Guo6dcc29c2024-07-26 00:28:4010067 ASSERT_TRUE(NavigateToURL(
10068 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
Jiacheng Guo6dcc29c2024-07-26 00:28:4010069 RenderProcessHost* created_process =
Patrick Monetted1757682024-09-24 20:59:1910070 spare_started_observer.WaitForSpareRenderProcessStarted();
Jiacheng Guo6dcc29c2024-07-26 00:28:4010071 ASSERT_TRUE(!!created_process);
Patrick Monettec5201262024-10-09 18:36:5710072 ASSERT_THAT(spare_manager.GetSpares(), testing::ElementsAre(created_process));
Jiacheng Guo6dcc29c2024-07-26 00:28:4010073 WebContentsImpl* web_contents =
10074 static_cast<WebContentsImpl*>(shell()->web_contents());
10075 ASSERT_TRUE(NavigateToURL(
10076 shell(), embedded_test_server()->GetURL("b.com", "/title1.html")));
10077 ASSERT_EQ(web_contents->GetSiteInstance()->GetProcess(), created_process);
10078}
10079
10080IN_PROC_BROWSER_TEST_P(AndroidPrewarmSpareRendererTest, RendererTimeout) {
10081 scoped_refptr<base::TestMockTimeTaskRunner> task_runner =
10082 new base::TestMockTimeTaskRunner();
Patrick Monettea45111342024-09-24 00:33:2910083 auto& spare_manager = SpareRenderProcessHostManagerImpl::Get();
10084 spare_manager.SetDeferTimerTaskRunnerForTesting(task_runner);
Jiacheng Guo6dcc29c2024-07-26 00:28:4010085 const base::TimeDelta kTimeout = base::Seconds(10);
10086
Patrick Monettec5201262024-10-09 18:36:5710087 spare_manager.CleanupSparesForTesting();
Patrick Monetted1757682024-09-24 20:59:1910088 SpareRenderProcessHostStartedObserver spare_started_observer;
Jiacheng Guo6dcc29c2024-07-26 00:28:4010089 ASSERT_TRUE(NavigateToURL(
10090 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
Jiacheng Guo6dcc29c2024-07-26 00:28:4010091 RenderProcessHost* created_process =
Patrick Monetted1757682024-09-24 20:59:1910092 spare_started_observer.WaitForSpareRenderProcessStarted();
Jiacheng Guo6dcc29c2024-07-26 00:28:4010093 ASSERT_TRUE(!!created_process);
Patrick Monettec5201262024-10-09 18:36:5710094 ASSERT_THAT(spare_manager.GetSpares(), testing::ElementsAre(created_process));
Jiacheng Guo6dcc29c2024-07-26 00:28:4010095
10096 if (!SpareRendererHasTimeout()) {
10097 // Warming up a spare renderer with a timeout shall not override
10098 // a spare renderer without a timeout.
Patrick Monettea45111342024-09-24 00:33:2910099 spare_manager.WarmupSpare(shell()->web_contents()->GetBrowserContext(),
10100 kTimeout);
Jiacheng Guo6dcc29c2024-07-26 00:28:4010101 }
10102 task_runner->FastForwardBy(kTimeout);
10103 base::RunLoop().RunUntilIdle();
10104 if (SpareRendererHasTimeout()) {
Patrick Monettec5201262024-10-09 18:36:5710105 EXPECT_TRUE(spare_manager.GetSpares().empty());
Jiacheng Guo6dcc29c2024-07-26 00:28:4010106 } else {
Patrick Monettec5201262024-10-09 18:36:5710107 ASSERT_THAT(spare_manager.GetSpares(),
10108 testing::ElementsAre(created_process));
Jiacheng Guo6dcc29c2024-07-26 00:28:4010109 }
10110}
Patrick Monettea45111342024-09-24 00:33:2910111#endif // BUILDFLAG(IS_ANDROID)
Jiacheng Guo6dcc29c2024-07-26 00:28:4010112
sbinglere0bc0952024-12-13 18:09:1210113class HstsUpgradeBrowserTest : public NavigationBrowserTest {
10114 public:
10115 HstsUpgradeBrowserTest() {
10116 feature_list_.InitAndEnableFeature(
10117 net::features::kHstsTopLevelNavigationsOnly);
10118 }
10119
10120 void SetUpOnMainThread() override {
10121 NavigationBrowserTest::SetUpOnMainThread();
10122 ASSERT_TRUE(embedded_https_test_server().Start());
10123 }
10124
sbinglerff91a352025-02-06 19:34:1810125 content::test::FencedFrameTestHelper& fenced_frame_test_helper() {
10126 return fenced_frame_test_helper_;
10127 }
10128
sbinglere0bc0952024-12-13 18:09:1210129 private:
sbinglerff91a352025-02-06 19:34:1810130 content::test::FencedFrameTestHelper fenced_frame_test_helper_;
sbinglere0bc0952024-12-13 18:09:1210131 base::test::ScopedFeatureList feature_list_;
10132};
10133
10134// Tests that when HstsTopLevelNavigationsOnly is enabled only top-level
10135// navigations will be upgraded by HSTS.
10136IN_PROC_BROWSER_TEST_F(HstsUpgradeBrowserTest, UpgradeTopLevelOnly) {
10137 // Url that loads a page with the HSTS url, http://b.com, as an iframe under
10138 // an http://a.com main frame.
10139 GURL hsts_url_in_iframe_http = embedded_test_server()->GetURL(
10140 "a.com", "/cross_site_iframe_factory.html?a(b)");
10141 // The expected url of the HSTS url, http://b.com, iframe.
10142 GURL url_of_hsts_frame_http = embedded_test_server()->GetURL(
10143 "b.com", "/cross_site_iframe_factory.html?b()");
10144
10145 {
10146 // Add hostname to the TransportSecurityState.
10147 base::Time expiry = base::Time::Now() + base::Days(100);
10148 bool include_subdomains = false;
10149 auto* network_context = web_contents()
10150 ->GetBrowserContext()
10151 ->GetDefaultStoragePartition()
10152 ->GetNetworkContext();
10153 base::RunLoop run_loop;
10154 network_context->AddHSTS(url_of_hsts_frame_http.host(), expiry,
10155 include_subdomains, run_loop.QuitClosure());
10156 run_loop.Run();
10157 }
10158
10159 // Navigate the main frame to the HSTS url, http://b.com.
10160
10161 // Note: Because the http and https embedded test servers run on different
10162 // (non-default) ports the test will fail if we try to navigate to
10163 // `url_of_hsts_frame_http` because HSTS will simply change the scheme to
10164 // https, but the port will remain the http server's port. To work around this
10165 // we can take an https url, `hsts_url_main_frame_https`, and change its
10166 // scheme to http which will then be upgraded by HSTS back to https and will
10167 // load correctly.
10168
10169 // Url of an https://b.com page.
10170 GURL hsts_url_main_frame_https =
10171 embedded_https_test_server().GetURL("b.com", "/title1.html");
10172
10173 GURL::Replacements scheme_replacement;
10174 scheme_replacement.SetSchemeStr("http");
10175
10176 // The navigation should get upgraded to https://b.com.
10177 EXPECT_TRUE(NavigateToURL(
10178 web_contents(),
10179 /*url=*/hsts_url_main_frame_https.ReplaceComponents(scheme_replacement),
10180 /*expected_commit_url=*/hsts_url_main_frame_https));
10181
10182 // Now navigate to an http://a.com page that embeds an http://b.com iframe.
10183 EXPECT_TRUE(NavigateToURL(web_contents(), hsts_url_in_iframe_http));
10184 auto* sub_frame = main_frame()->child_at(0);
10185 // The http://b.com iframe should not have been upgraded.
10186 EXPECT_EQ(url_of_hsts_frame_http,
10187 sub_frame->current_frame_host()->GetLastCommittedURL());
sbinglerff91a352025-02-06 19:34:1810188
10189 // Fenced Frames are treated as top-level frames in many cases, but not for
10190 // HSTS upgrades. Requests for fenced frames should not be upgraded.
10191 content::RenderFrameHost* fenced_frame =
10192 fenced_frame_test_helper().CreateFencedFrame(
10193 main_frame()->current_frame_host(), url_of_hsts_frame_http);
10194
10195 ASSERT_TRUE(fenced_frame);
10196 EXPECT_EQ(url_of_hsts_frame_http, fenced_frame->GetLastCommittedURL());
sbinglere0bc0952024-12-13 18:09:1210197}
10198
clamyf1ccb4d2015-01-28 17:40:3810199} // namespace content