blob: d5e503b49a429a10d6d2d6a28e88183b9d6c053b [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2012 The Chromium Authors
[email protected]bbdd1b20b2012-12-11 21:24:132// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
dmazzoni0b5d2482014-09-10 19:45:575#include "content/browser/site_per_process_browsertest.h"
6
avib7348942015-12-25 20:57:107#include <stddef.h>
8#include <stdint.h>
Dave Tapuskae15164c2024-08-02 23:53:359
nick59dcb162015-04-09 20:29:0110#include <algorithm>
Peter Kastingb6dc73f52020-06-30 16:49:3211#include <cmath>
Lukasz Anforowicz9e0ce4e2017-09-28 19:09:1512#include <list>
csharrisond86c35bc2017-02-02 17:41:2613#include <map>
Lukasz Anforowicz9e0ce4e2017-09-28 19:09:1514#include <memory>
Daniel Cheng999698bd2017-03-22 04:56:3715#include <set>
Ehsan Karamad192a8da2018-10-21 03:48:0816#include <string>
17#include <tuple>
Lukasz Anforowicz9e0ce4e2017-09-28 19:09:1518#include <utility>
nick59dcb162015-04-09 20:29:0119#include <vector>
20
[email protected]bbdd1b20b2012-12-11 21:24:1321#include "base/command_line.h"
Lei Zhangd4f2c7ad2021-05-13 20:10:1222#include "base/containers/contains.h"
chaopengd3ca34feb2017-04-20 17:11:2223#include "base/feature_list.h"
Avi Drissmanadac21992023-01-11 23:46:3924#include "base/functional/bind.h"
25#include "base/functional/callback.h"
26#include "base/functional/callback_helpers.h"
W. James MacLean99c4ba42017-12-01 14:44:4327#include "base/json/json_reader.h"
skyostil95082a62015-06-05 19:53:0728#include "base/location.h"
csharrisond86c35bc2017-02-02 17:41:2629#include "base/memory/ptr_util.h"
Keishi Hattori0e45c022021-11-27 09:25:5230#include "base/memory/raw_ptr.h"
Ali Hijazid87307d2022-11-07 20:15:0331#include "base/memory/raw_ref.h"
Pavol Marko9a5e01d2020-04-28 13:29:0532#include "base/memory/scoped_refptr.h"
nasko39e3eb72016-06-24 23:15:4433#include "base/path_service.h"
Gabriel Charette078e3662017-08-28 22:59:0434#include "base/run_loop.h"
Sigurdur Asgeirssond2d57e72020-11-10 14:45:1535#include "base/scoped_observation.h"
brettwd97eede02015-07-06 22:09:0036#include "base/strings/pattern.h"
Ehsan Karamadf152db882017-10-23 17:41:2537#include "base/strings/string_number_conversions.h"
38#include "base/strings/string_split.h"
Arthur Sonzognif8840b92018-11-07 14:10:3539#include "base/strings/string_util.h"
[email protected]348fbaac2013-06-11 06:31:5140#include "base/strings/stringprintf.h"
Devon Loehrc0138d8d2025-03-04 01:11:4241#include "base/strings/to_string.h"
lfg9ef7d2d2014-12-15 22:32:3042#include "base/strings/utf_string_conversions.h"
Patrick Monette643cdf62021-10-15 19:13:4243#include "base/task/sequenced_task_runner.h"
44#include "base/task/single_thread_task_runner.h"
Guido Urdanetaef4e91942020-11-09 15:06:2445#include "base/test/bind.h"
Devlin Cronin513398f2018-06-05 15:33:4946#include "base/test/metrics/histogram_tester.h"
Sonjaae009ec2024-02-01 13:33:2247#include "base/test/run_until.h"
Andrew Rayskiy2646d4142023-08-07 10:32:0348#include "base/test/test_future.h"
kenrb162e42c2015-11-24 21:10:0049#include "base/test/test_timeouts.h"
Aman Verma44335c72025-02-19 18:43:5750#include "base/test/test_trace_processor.h"
Arthur Sonzognif8840b92018-11-07 14:10:3551#include "base/time/time.h"
Lukasz Anforowicz4f0593d2018-09-21 18:33:5452#include "base/timer/timer.h"
avib7348942015-12-25 20:57:1053#include "build/build_config.h"
Sunny Sachanandanie76e8b12020-12-10 01:13:1854#include "cc/base/math_util.h"
xidachenfa0199e72017-05-11 11:34:2655#include "cc/input/touch_action.h"
Aman Verma2e897242025-01-14 12:08:2656#include "components/input/features.h"
Aman Vermad88d4de2025-05-06 12:00:3357#include "components/input/input_constants.h"
Kartar Singhd084e582024-06-12 19:22:2458#include "components/input/input_router.h"
Kartar Singhb1bfa1a2024-06-24 13:14:5759#include "components/input/render_widget_host_input_event_router.h"
Kartar Singhd084e582024-06-12 19:22:2460#include "components/input/switches.h"
Aman Verma2e897242025-01-14 12:08:2661#include "components/input/utils.h"
62#include "components/viz/host/host_frame_sink_manager.h"
clamyaf4bf2d92018-02-06 10:54:3663#include "content/browser/child_process_security_policy_impl.h"
Aman Verma2e897242025-01-14 12:08:2664#include "content/browser/compositor/surface_utils.h"
kenrb2a565f82015-09-02 20:24:5965#include "content/browser/gpu/compositor_util.h"
Aman Verma44335c72025-02-19 18:43:5766#include "content/browser/gpu/gpu_data_manager_impl.h"
Sharon Yanga005ca12021-11-16 20:09:4267#include "content/browser/process_lock.h"
Charlie Reis9ce0ed222024-09-05 22:05:2668#include "content/browser/process_reuse_policy.h"
Tal Pressman31a07372020-09-29 14:07:5269#include "content/browser/renderer_host/agent_scheduling_group_host.h"
danakje34636e2020-09-15 22:15:0070#include "content/browser/renderer_host/cross_process_frame_connector.h"
71#include "content/browser/renderer_host/frame_navigation_entry.h"
72#include "content/browser/renderer_host/frame_tree.h"
danakje34636e2020-09-15 22:15:0073#include "content/browser/renderer_host/navigation_controller_impl.h"
74#include "content/browser/renderer_host/navigation_entry_impl.h"
Nate Chapin214a86a2021-06-21 20:35:5775#include "content/browser/renderer_host/navigation_entry_restore_context_impl.h"
danakje34636e2020-09-15 22:15:0076#include "content/browser/renderer_host/navigation_request.h"
77#include "content/browser/renderer_host/navigator.h"
78#include "content/browser/renderer_host/render_frame_host_impl.h"
79#include "content/browser/renderer_host/render_frame_proxy_host.h"
Lei Zhang589ef702022-08-26 19:38:0480#include "content/browser/renderer_host/render_process_host_impl.h"
[email protected]9b159a52013-10-03 17:24:5581#include "content/browser/renderer_host/render_view_host_impl.h"
Ken Buchanandaef006b2017-08-17 18:32:1582#include "content/browser/renderer_host/render_widget_host_view_child_frame.h"
Sharon Yangd70a5392021-10-26 23:06:3283#include "content/browser/site_info.h"
jamc1905862017-05-16 14:45:3084#include "content/browser/storage_partition_impl.h"
estarkcd2e30c2016-08-12 06:51:1585#include "content/browser/web_contents/web_contents_impl.h"
[email protected]ee8ae332020-01-29 03:49:4586#include "content/common/content_navigation_policy.h"
Lowell Manners88da5ec2019-06-18 09:46:1787#include "content/common/frame.mojom-test-utils.h"
W. James MacLeand973a55b2018-11-29 21:39:1388#include "content/common/input/actions_parser.h"
Aman Verma8378f3f2023-11-29 18:08:4689#include "content/common/input/synthetic_gesture.h"
90#include "content/common/input/synthetic_gesture_target.h"
W. James MacLeand973a55b2018-11-29 21:39:1391#include "content/common/input/synthetic_pinch_gesture_params.h"
Aman Verma8378f3f2023-11-29 18:08:4692#include "content/common/input/synthetic_pointer_action.h"
93#include "content/common/input/synthetic_tap_gesture.h"
94#include "content/common/input/synthetic_touchscreen_pinch_gesture.h"
rockot53be7caf2016-10-04 20:17:0895#include "content/common/renderer.mojom.h"
Dave Tapuskae15164c2024-08-02 23:53:3596#include "content/common/renderer_host.mojom-test-utils.h"
jamc1905862017-05-16 14:45:3097#include "content/public/browser/browser_context.h"
Eric Seckler8652dcd52018-09-20 10:42:2898#include "content/public/browser/browser_task_traits.h"
csharrisond86c35bc2017-02-02 17:41:2699#include "content/public/browser/browser_thread.h"
Lukasz Anforowicz8c4446dc2020-03-11 22:16:42100#include "content/public/browser/context_menu_params.h"
W. James MacLean5a57e302021-09-13 20:59:28101#include "content/public/browser/global_routing_id.h"
Aman Verma44335c72025-02-19 18:43:57102#include "content/public/browser/gpu_data_manager_observer.h"
103#include "content/public/browser/gpu_utils.h"
Avi Drissmanc3736ed62018-10-31 22:07:05104#include "content/public/browser/javascript_dialog_manager.h"
jam419c0f12016-10-13 02:09:16105#include "content/public/browser/navigation_handle.h"
Lei Zhangc8d18022022-08-24 17:58:02106#include "content/public/browser/render_process_host_priority_client.h"
Nasko Oskov0401d812021-02-05 22:20:08107#include "content/public/browser/site_isolation_policy.h"
Rakina Zata Amni347b70902020-07-22 10:49:04108#include "content/public/common/content_client.h"
jama86c1ba2017-07-05 17:12:38109#include "content/public/common/content_features.h"
[email protected]bbdd1b20b2012-12-11 21:24:13110#include "content/public/common/content_switches.h"
alexmos5b50b6742016-03-17 20:38:05111#include "content/public/common/url_constants.h"
Sreeja Kamishettyce8d5942020-08-19 11:25:51112#include "content/public/test/back_forward_cache_util.h"
Peter Kasting919ce652020-05-07 10:22:36113#include "content/public/test/browser_test.h"
[email protected]bbdd1b20b2012-12-11 21:24:13114#include "content/public/test/browser_test_utils.h"
Scott Violet99861992023-02-08 01:20:12115#include "content/public/test/content_browser_test_content_browser_client.h"
[email protected]6e9def12014-03-27 20:23:28116#include "content/public/test/content_browser_test_utils.h"
Arthur Sonzogni33b19682021-08-06 12:43:31117#include "content/public/test/content_mock_cert_verifier.h"
Hyowon Kim2f3bb5c2022-03-18 08:15:45118#include "content/public/test/fenced_frame_test_util.h"
jonross09d21de2018-06-13 12:31:36119#include "content/public/test/hit_test_region_observer.h"
jamcb4ae152017-05-19 01:35:51120#include "content/public/test/navigation_handle_observer.h"
Antonio Sartori7f809cc72020-10-30 07:47:20121#include "content/public/test/policy_container_utils.h"
Sreeja Kamishettydce0fc62020-05-18 11:29:06122#include "content/public/test/render_frame_host_test_support.h"
Kenichi Ishibashi8e087a62023-06-06 06:17:41123#include "content/public/test/test_devtools_protocol_client.h"
lukasza67fdcaf2016-11-16 00:27:52124#include "content/public/test/test_frame_navigation_observer.h"
nasko3e8c20e2014-12-18 06:54:56125#include "content/public/test/test_navigation_observer.h"
Daniel Chenge8b69402021-05-25 20:44:44126#include "content/public/test/test_navigation_throttle.h"
127#include "content/public/test/test_navigation_throttle_inserter.h"
[email protected]bbdd1b20b2012-12-11 21:24:13128#include "content/public/test/test_utils.h"
John Abd-El-Maleka1e25a3a2017-12-04 23:21:08129#include "content/public/test/url_loader_interceptor.h"
csharrisonf2466b802016-11-19 23:57:12130#include "content/shell/browser/shell.h"
Thiabaud Engelbrecht89f0d3a2022-08-10 01:45:15131#include "content/shell/common/main_frame_counter_test_impl.h"
W. James MacLean99c4ba42017-12-01 14:44:43132#include "content/shell/common/shell_switches.h"
[email protected]893558b2014-04-25 23:01:06133#include "content/test/content_browser_test_utils_internal.h"
Arthur Hemery2a0a28be2019-03-06 17:51:36134#include "content/test/did_commit_navigation_interceptor.h"
Fergal Daly2e7e1e12020-06-24 09:18:28135#include "content/test/render_document_feature.h"
Ken Rockotfd907632017-09-14 04:23:41136#include "ipc/constants.mojom.h"
Becca Hughes60af7d42017-12-12 10:53:15137#include "media/base/media_switches.h"
Miyoung Shin2be9da72019-09-04 09:04:10138#include "mojo/public/cpp/bindings/pending_remote.h"
Aman Verma2e897242025-01-14 12:08:26139#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
Will Harriseb6cca012022-02-11 01:23:17140#include "mojo/public/cpp/test_support/test_utils.h"
Kenichi Ishibashi8e087a62023-06-06 06:17:41141#include "net/base/url_util.h"
[email protected]9b159a52013-10-03 17:24:55142#include "net/dns/mock_host_resolver.h"
Aaron Tagliaboschid4ad7a302021-09-24 19:51:51143#include "net/http/mock_http_cache.h"
naskocbce0e62014-10-07 14:04:26144#include "net/test/embedded_test_server/embedded_test_server.h"
csharrisond86c35bc2017-02-02 17:41:26145#include "net/test/embedded_test_server/http_request.h"
146#include "net/test/embedded_test_server/http_response.h"
rhalavati4cda417b2017-06-12 11:00:24147#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
Yutaka Hiranod8789f92018-01-30 09:59:51148#include "services/network/public/cpp/features.h"
Sandor Majorca47512a2025-02-11 16:58:04149#include "services/network/public/cpp/permissions_policy/origin_with_possible_wildcards.h"
Sandor Major878f8352025-02-18 20:16:02150#include "services/network/public/cpp/permissions_policy/permissions_policy_declaration.h"
arthursonzognib93a4472020-04-10 07:38:00151#include "services/network/public/cpp/web_sandbox_flags.h"
Sandor Major878f8352025-02-18 20:16:02152#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-shared.h"
arthursonzognib93a4472020-04-10 07:38:00153#include "services/network/public/mojom/web_sandbox_flags.mojom-shared.h"
Aman Verma2e897242025-01-14 12:08:26154#include "services/viz/privileged/mojom/compositing/features.mojom-features.h"
lunalufec23402017-02-22 16:49:34155#include "testing/gmock/include/gmock/gmock.h"
lukasza8e1c02e42016-05-17 20:05:10156#include "testing/gtest/include/gtest/gtest.h"
Antonio Gomesda7e03d2020-08-05 15:03:08157#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
Yao Xiaoc646d0b2020-01-31 22:30:59158#include "third_party/blink/public/common/features.h"
Dave Tapuska129cef82019-12-19 16:36:48159#include "third_party/blink/public/common/input/web_input_event.h"
Charlie Hue010cf22021-03-03 23:09:51160#include "third_party/blink/public/common/permissions_policy/policy_value.h"
Camillo Brunidcd255e32021-10-14 15:50:05161#include "third_party/blink/public/common/switches.h"
Daniel Cheng4daa51222021-02-21 07:20:11162#include "third_party/blink/public/common/tokens/tokens.h"
Mario Sanchez Prada5c4e7e3b2020-01-22 10:30:06163#include "third_party/blink/public/mojom/frame/frame.mojom-test-utils.h"
Antonio Gomesda7e03d2020-08-05 15:03:08164#include "third_party/blink/public/mojom/frame/frame.mojom.h"
Gyuyoung Kimc16e52e92021-03-19 02:45:37165#include "third_party/blink/public/mojom/frame/frame_replication_state.mojom.h"
Daniel Cheng463b5fd2020-12-12 02:59:55166#include "third_party/blink/public/mojom/leak_detector/leak_detector.mojom-test-utils.h"
167#include "third_party/blink/public/mojom/leak_detector/leak_detector.mojom.h"
Dave Tapuskac63607a2020-10-22 18:44:24168#include "third_party/blink/public/mojom/page/widget.mojom-test-utils.h"
Julie Jeongeun Kimd90e2dd2020-03-03 11:45:37169#include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom.h"
oshima06b39602016-05-11 02:40:10170#include "ui/display/display_switches.h"
wjmaclean8a795f32016-08-11 23:49:58171#include "ui/display/screen.h"
dtapuska899ac222017-01-03 18:09:16172#include "ui/events/base_event_utils.h"
Xida Chena964d8a92019-03-15 20:09:19173#include "ui/events/blink/blink_features.h"
wjmacleanfab616a2016-03-02 18:40:39174#include "ui/events/event.h"
175#include "ui/events/event_utils.h"
Kevin McNeee21d23b2018-06-29 15:25:04176#include "ui/events/keycodes/dom/dom_code.h"
177#include "ui/events/keycodes/dom/dom_key.h"
Kevin McNeee21d23b2018-06-29 15:25:04178#include "ui/events/keycodes/keyboard_codes.h"
wjmacleanfab616a2016-03-02 18:40:39179#include "ui/gfx/geometry/point.h"
EhsanKbd2cea992017-11-23 18:49:08180#include "ui/gfx/geometry/rect.h"
Xianzhu Wang65ef1ad32021-10-07 03:12:33181#include "ui/gfx/geometry/transform.h"
mfomitchev3ba450ad2017-04-03 18:20:40182#include "ui/latency/latency_info.h"
Zoraiz Naeembadbeda2025-03-03 22:15:03183#include "ui/native_theme/features/native_theme_features.h"
[email protected]bbdd1b20b2012-12-11 21:24:13184
wjmacleanfab616a2016-03-02 18:40:39185#if defined(USE_AURA)
Scott Violet1098538e2017-10-05 19:23:33186#include "content/browser/renderer_host/render_widget_host_view_aura.h"
Scott Violet3c03cfef2019-04-03 22:22:06187#include "ui/aura/window.h"
ahest1d019b72016-02-02 07:24:42188#endif
189
Xiaohan Wang1ecfd002022-01-19 22:33:10190#if BUILDFLAG(IS_ANDROID)
ekaramada06dc6c2017-02-22 20:07:43191#include "base/android/jni_android.h"
192#include "base/android/jni_string.h"
193#include "base/android/scoped_java_ref.h"
David Bokan963f2752020-03-18 18:36:37194#include "content/browser/android/gesture_listener_manager.h"
jinsukkim48f82db72017-04-05 04:10:26195#include "content/browser/android/ime_adapter_android.h"
W. James MacLean12ba7972017-07-08 01:58:26196#include "content/browser/renderer_host/input/touch_selection_controller_client_manager_android.h"
ekaramada06dc6c2017-02-22 20:07:43197#include "content/browser/renderer_host/render_widget_host_view_android.h"
Kevin McNee18430052018-08-10 16:50:56198#include "content/browser/web_contents/web_contents_view_android.h"
Bo Liu168c8642017-08-28 18:26:02199#include "content/public/browser/android/child_process_importance.h"
Kevin McNee80e0a452017-08-09 14:42:18200#include "content/test/mock_overscroll_refresh_handler_android.h"
Kevin McNee18430052018-08-10 16:50:56201#include "ui/android/view_android.h"
202#include "ui/android/window_android.h"
203#include "ui/events/android/event_handler_android.h"
Keigo Okab9022c52025-07-19 02:34:27204#include "ui/events/android/motion_event_android_factory.h"
Kartar Singh07396d9a2024-10-08 15:27:44205#include "ui/events/android/motion_event_android_java.h"
Keigo Okadfd57572025-07-05 08:36:09206#include "ui/events/motionevent_jni_headers/MotionEvent_jni.h"
W. James MacLean12ba7972017-07-08 01:58:26207#include "ui/gfx/geometry/point_f.h"
ekaramada06dc6c2017-02-22 20:07:43208#endif
209
Daniel Cheng999698bd2017-03-22 04:56:37210using ::testing::SizeIs;
Arthur Sonzognif8840b92018-11-07 14:10:35211using ::testing::WhenSorted;
212using ::testing::ElementsAre;
Daniel Cheng999698bd2017-03-22 04:56:37213
[email protected]bbdd1b20b2012-12-11 21:24:13214namespace content {
215
alexmos9f8705a2015-05-06 19:58:59216namespace {
217
Thiabaud Engelbrecht89f0d3a2022-08-10 01:45:15218void VerifyChildProcessHasMainFrame(
219 mojo::Remote<mojom::MainFrameCounterTest>& main_frame_counter,
220 bool expected_state) {
221 main_frame_counter.FlushForTesting();
Andrew Rayskiy2646d4142023-08-07 10:32:03222 base::test::TestFuture<bool> has_main_frame_future;
223 main_frame_counter->HasMainFrame(has_main_frame_future.GetCallback());
224 EXPECT_EQ(expected_state, has_main_frame_future.Get());
Thiabaud Engelbrecht89f0d3a2022-08-10 01:45:15225}
226
Alex Moshchuk9b0fd822020-10-26 23:08:15227using CrashVisibility = CrossProcessFrameConnector::CrashVisibility;
228
alexmos9f8705a2015-05-06 19:58:59229// Helper function to send a postMessage and wait for a reply message. The
230// |post_message_script| is executed on the |sender_ftn| frame, and the sender
231// frame is expected to post |reply_status| from the DOMAutomationController
232// when it receives a reply.
233void PostMessageAndWaitForReply(FrameTreeNode* sender_ftn,
234 const std::string& post_message_script,
235 const std::string& reply_status) {
Chris Fredricksond3bb2682023-05-10 03:32:26236 // Subtle: msg_queue needs to be declared before the EvalJs below, or
alexmos17e57532015-08-15 02:54:36237 // else it might miss the message of interest. See https://crbug.com/518729.
Colin Blundellecd384f2022-05-11 08:58:30238 DOMMessageQueue msg_queue(sender_ftn->current_frame_host());
alexmos17e57532015-08-15 02:54:36239
Nick Carterb7e71312018-08-03 23:36:13240 EXPECT_EQ(true, EvalJs(sender_ftn, "(" + post_message_script + ");"));
alexmos9f8705a2015-05-06 19:58:59241
alexmos9f8705a2015-05-06 19:58:59242 std::string status;
243 while (msg_queue.WaitForMessage(&status)) {
244 if (status == reply_status)
245 break;
246 }
247}
248
alexmos58729042015-06-18 23:20:00249// Helper function to extract and return "window.receivedMessages" from the
250// |sender_ftn| frame. This variable is used in post_message.html to count the
251// number of messages received via postMessage by the current window.
252int GetReceivedMessages(FrameTreeNode* ftn) {
Nick Carterb7e71312018-08-03 23:36:13253 return EvalJs(ftn, "window.receivedMessages;").ExtractInt();
alexmos58729042015-06-18 23:20:00254}
255
alexmos646fec02015-07-25 00:11:49256// Helper function to perform a window.open from the |caller_frame| targeting a
257// frame with the specified name.
258void NavigateNamedFrame(const ToRenderFrameHost& caller_frame,
259 const GURL& url,
260 const std::string& name) {
Nick Carterb7e71312018-08-03 23:36:13261 EXPECT_EQ(true, EvalJs(caller_frame,
262 JsReplace("!!window.open($1, $2)", url, name)));
alexmos646fec02015-07-25 00:11:49263}
264
alexmosb1dc2162015-11-05 00:59:20265// Helper function to generate a click on the given RenderWidgetHost. The
266// mouse event is forwarded directly to the RenderWidgetHost without any
267// hit-testing.
268void SimulateMouseClick(RenderWidgetHost* rwh, int x, int y) {
Daniel Cheng93c80a92018-02-14 19:02:43269 blink::WebMouseEvent mouse_event(
Dave Tapuska347d60a2020-04-21 23:55:47270 blink::WebInputEvent::Type::kMouseDown,
271 blink::WebInputEvent::kNoModifiers,
Daniel Cheng93c80a92018-02-14 19:02:43272 blink::WebInputEvent::GetStaticTimeStampForTests());
Blink Reformat1c4d759e2017-04-09 16:34:54273 mouse_event.button = blink::WebPointerProperties::Button::kLeft;
274 mouse_event.SetPositionInWidget(x, y);
alexmosb1dc2162015-11-05 00:59:20275 rwh->ForwardMouseEvent(mouse_event);
276}
277
Philip Jägenstedt67302a22018-09-14 09:58:05278// Retrieve self.origin for the frame |ftn|.
279EvalJsResult GetOriginFromRenderer(FrameTreeNode* ftn) {
280 return EvalJs(ftn, "self.origin;");
alexmos6e940102016-01-19 22:47:25281}
282
kenrb19221852016-04-29 17:21:40283// This observer detects when WebContents receives notification of a user
284// gesture having occurred, following a user input event targeted to
285// a RenderWidgetHost under that WebContents.
286class UserInteractionObserver : public WebContentsObserver {
287 public:
288 explicit UserInteractionObserver(WebContents* web_contents)
289 : WebContentsObserver(web_contents), user_interaction_received_(false) {}
290
Peter Boström828b9022021-09-21 02:28:43291 UserInteractionObserver(const UserInteractionObserver&) = delete;
292 UserInteractionObserver& operator=(const UserInteractionObserver&) = delete;
293
kenrb19221852016-04-29 17:21:40294 ~UserInteractionObserver() override {}
295
296 // Retrieve the flag. There is no need to wait on a loop since
297 // DidGetUserInteraction() should be called synchronously with the input
298 // event processing in the browser process.
299 bool WasUserInteractionReceived() { return user_interaction_received_; }
300
301 void Reset() { user_interaction_received_ = false; }
302
303 private:
304 // WebContentsObserver
Emily Starkc7bd40c42020-07-21 19:12:55305 void DidGetUserInteraction(const blink::WebInputEvent& event) override {
kenrb19221852016-04-29 17:21:40306 user_interaction_received_ = true;
307 }
308
309 bool user_interaction_received_;
kenrb19221852016-04-29 17:21:40310};
311
Pavol Markof2575552020-07-25 18:19:13312// Supports waiting until a WebContents notifies its observers that the visible
313// security state changed, and a test-specific condition is true at that time.
314class VisibleSecurityStateObserver : public WebContentsObserver {
315 public:
316 // Invoked at Wait() start and when the visible security state changes.
317 // If the callback returns true, stops waiting.
318 using ConditionCallback = base::RepeatingCallback<bool(WebContents*)>;
319
320 // Creates a VisibleSecurityStateObserver which will wait until
321 // a visible security state change is announced by |web_contents| and
322 // |condition_callback| returns true (unless |condition_callback| returns true
323 // in Wait() already, when it will not wait at all).
324 VisibleSecurityStateObserver(WebContents* web_contents,
325 ConditionCallback condition_callback)
326 : WebContentsObserver(web_contents),
327 condition_callback_(condition_callback) {}
328 ~VisibleSecurityStateObserver() override = default;
329
330 VisibleSecurityStateObserver(const VisibleSecurityStateObserver& other) =
331 delete;
332 VisibleSecurityStateObserver& operator=(
333 const VisibleSecurityStateObserver& other) = delete;
334
335 // If the |condition_callback| passed to the constructor returns true, this
336 // returns immediately. Otherwise, blocks until the |web_contents| passed to
337 // the constructor notifies about a visible security state change and the
338 // |condition_callback| evaluates to true.
339 void Wait() {
340 if (condition_callback_.Run(web_contents()))
341 return;
342 run_loop_.Run();
343 }
344
345 void DidChangeVisibleSecurityState() override {
346 if (condition_callback_.Run(web_contents()))
347 run_loop_.Quit();
348 }
349
350 private:
351 ConditionCallback condition_callback_;
352 base::RunLoop run_loop_;
353};
354
alexmos21acae52015-11-07 01:04:43355// Helper function to focus a frame by sending it a mouse click and then
356// waiting for it to become focused.
357void FocusFrame(FrameTreeNode* frame) {
avallee0206f782016-07-28 18:55:33358 FrameFocusedObserver focus_observer(frame->current_frame_host());
alexmos21acae52015-11-07 01:04:43359 SimulateMouseClick(frame->current_frame_host()->GetRenderWidgetHost(), 1, 1);
360 focus_observer.Wait();
361}
362
W. James MacLean99c4ba42017-12-01 14:44:43363bool ConvertJSONToPoint(const std::string& str, gfx::PointF* point) {
Ho Cheung6f158a02025-01-17 00:42:25364 std::optional<base::Value::Dict> value = base::JSONReader::ReadDict(str);
365 if (!value) {
W. James MacLean99c4ba42017-12-01 14:44:43366 return false;
Ho Cheung6f158a02025-01-17 00:42:25367 }
368 std::optional<double> x = value->FindDouble("x");
369 std::optional<double> y = value->FindDouble("y");
370 if (!x || !y) {
W. James MacLean99c4ba42017-12-01 14:44:43371 return false;
Ho Cheung6f158a02025-01-17 00:42:25372 }
Avi Drissman57e5d7df2019-03-21 19:12:53373 point->set_x(x.value());
374 point->set_y(y.value());
W. James MacLean99c4ba42017-12-01 14:44:43375 return true;
376}
W. James MacLean99c4ba42017-12-01 14:44:43377
Charlie Hu5130d25e2021-03-05 21:53:39378// Helper function to generate a permissions policy for a single feature and a
379// list of origins. (Equivalent to the declared policy "feature origin1 origin2
380// ...".) If the origins list is empty, it's treated as matches all origins
Charlie Hu1834d07f2019-10-08 14:38:21381// (Equivalent to the declared policy "feature *")
Sandor Major878f8352025-02-18 20:16:02382network::ParsedPermissionsPolicyDeclaration
Charlie Hue24f04832021-03-04 21:07:06383CreateParsedPermissionsPolicyDeclaration(
Sandor «Alex» Majore9545a72025-01-31 20:40:46384 network::mojom::PermissionsPolicyFeature feature,
Ian Clellandee2fce32020-09-24 23:12:59385 const std::vector<GURL>& origins,
Ari Chivukula04f6ff7e2023-03-22 18:02:00386 bool match_all_origins = false,
Arthur Sonzognic686e8f2024-01-11 08:36:37387 const std::optional<GURL> self_if_matches = std::nullopt) {
Sandor Major878f8352025-02-18 20:16:02388 network::ParsedPermissionsPolicyDeclaration declaration;
Charlie Hu1834d07f2019-10-08 14:38:21389
Charlie Hu1834d07f2019-10-08 14:38:21390 declaration.feature = feature;
Ari Chivukula04f6ff7e2023-03-22 18:02:00391 if (self_if_matches.has_value()) {
392 declaration.self_if_matches = url::Origin::Create(*self_if_matches);
393 }
Ian Clellandee2fce32020-09-24 23:12:59394 declaration.matches_all_origins = match_all_origins;
395 declaration.matches_opaque_src = match_all_origins;
Charlie Hu1834d07f2019-10-08 14:38:21396
Nico Weber6dcde5b2020-02-22 20:49:20397 for (const auto& origin : origins)
Ari Chivukula30c4e912023-05-25 12:35:50398 declaration.allowed_origins.emplace_back(
Sandor Majorca47512a2025-02-11 16:58:04399 *network::OriginWithPossibleWildcards::FromOrigin(
Ari Chivukula30c4e912023-05-25 12:35:50400 url::Origin::Create(origin)));
Charlie Hu8daefb12020-04-24 19:57:12401
402 std::sort(declaration.allowed_origins.begin(),
403 declaration.allowed_origins.end());
Charlie Hu1834d07f2019-10-08 14:38:21404
405 return declaration;
Luna Lu58c77b02019-03-05 20:13:46406}
407
Sandor Major878f8352025-02-18 20:16:02408network::ParsedPermissionsPolicy CreateParsedPermissionsPolicy(
Sandor «Alex» Majore9545a72025-01-31 20:40:46409 const std::vector<network::mojom::PermissionsPolicyFeature>& features,
Ian Clellandee2fce32020-09-24 23:12:59410 const std::vector<GURL>& origins,
Ari Chivukula04f6ff7e2023-03-22 18:02:00411 bool match_all_origins = false,
Arthur Sonzognic686e8f2024-01-11 08:36:37412 const std::optional<GURL> self_if_matches = std::nullopt) {
Sandor Major878f8352025-02-18 20:16:02413 network::ParsedPermissionsPolicy result;
Charlie Hu1834d07f2019-10-08 14:38:21414 result.reserve(features.size());
415 for (const auto& feature : features)
Charlie Hue24f04832021-03-04 21:07:06416 result.push_back(CreateParsedPermissionsPolicyDeclaration(
Ari Chivukula04f6ff7e2023-03-22 18:02:00417 feature, origins, match_all_origins, self_if_matches));
Ian Clelland91cff7ae2018-02-15 16:51:37418 return result;
419}
420
Sandor Major878f8352025-02-18 20:16:02421network::ParsedPermissionsPolicy CreateParsedPermissionsPolicyMatchesSelf(
Sandor «Alex» Majore9545a72025-01-31 20:40:46422 const std::vector<network::mojom::PermissionsPolicyFeature>& features,
Ari Chivukula04f6ff7e2023-03-22 18:02:00423 const GURL& self_if_matches) {
424 return CreateParsedPermissionsPolicy(features, {}, false, self_if_matches);
425}
426
Sandor Major878f8352025-02-18 20:16:02427network::ParsedPermissionsPolicy CreateParsedPermissionsPolicyMatchesAll(
Sandor «Alex» Majore9545a72025-01-31 20:40:46428 const std::vector<network::mojom::PermissionsPolicyFeature>& features) {
Charlie Hue24f04832021-03-04 21:07:06429 return CreateParsedPermissionsPolicy(features, {}, true);
Ian Clellandee2fce32020-09-24 23:12:59430}
431
Sandor Major878f8352025-02-18 20:16:02432network::ParsedPermissionsPolicy CreateParsedPermissionsPolicyMatchesNone(
Sandor «Alex» Majore9545a72025-01-31 20:40:46433 const std::vector<network::mojom::PermissionsPolicyFeature>& features) {
Charlie Hue24f04832021-03-04 21:07:06434 return CreateParsedPermissionsPolicy(features, {});
Ian Clelland91cff7ae2018-02-15 16:51:37435}
436
Bo Liua13e7c02018-03-28 22:24:02437// Check frame depth on node, widget, and process all match expected depth.
438void CheckFrameDepth(unsigned int expected_depth, FrameTreeNode* node) {
Harkiran Bolaria2e3c0b772021-09-01 16:01:40439 EXPECT_EQ(expected_depth, node->current_frame_host()->GetFrameDepth());
Lei Zhangc8d18022022-08-24 17:58:02440 RenderProcessHostPriorityClient::Priority priority =
Bo Liua13e7c02018-03-28 22:24:02441 node->current_frame_host()->GetRenderWidgetHost()->GetPriority();
442 EXPECT_EQ(expected_depth, priority.frame_depth);
Bo Liu2f75db32018-05-03 00:56:21443 EXPECT_EQ(expected_depth,
444 node->current_frame_host()->GetProcess()->GetFrameDepth());
Bo Liua13e7c02018-03-28 22:24:02445}
446
Xida Chen2eec314f2018-06-07 11:42:42447void GenerateTapDownGesture(RenderWidgetHost* rwh) {
448 blink::WebGestureEvent gesture_tap_down(
Dave Tapuska347d60a2020-04-21 23:55:47449 blink::WebGestureEvent::Type::kGestureTapDown,
Xida Chen2eec314f2018-06-07 11:42:42450 blink::WebInputEvent::kNoModifiers,
451 blink::WebInputEvent::GetStaticTimeStampForTests(),
Daniel Cheng7f9ec902019-04-18 05:07:00452 blink::WebGestureDevice::kTouchscreen);
Jihwan Marc Kim0bdf0ca2020-11-05 23:35:26453 gesture_tap_down.is_source_touch_event_set_blocking = true;
Xida Chen2eec314f2018-06-07 11:42:42454 rwh->ForwardGestureEvent(gesture_tap_down);
455}
456
Alex Attarb82599d2025-05-13 14:30:06457// Overrides process reuse preference based on URL for testing purposes.
458class SitePerProcessWithMainFrameThresholdAndSiteRestrictionBrowserClient
459 : public ContentBrowserTestContentBrowserClient {
460 public:
461 SitePerProcessWithMainFrameThresholdAndSiteRestrictionBrowserClient() =
462 default;
463 ~SitePerProcessWithMainFrameThresholdAndSiteRestrictionBrowserClient()
464 override = default;
465
466 SitePerProcessWithMainFrameThresholdAndSiteRestrictionBrowserClient(
467 const SitePerProcessWithMainFrameThresholdAndSiteRestrictionBrowserClient&) =
468 delete;
469 SitePerProcessWithMainFrameThresholdAndSiteRestrictionBrowserClient&
470 operator=(
471 const SitePerProcessWithMainFrameThresholdAndSiteRestrictionBrowserClient&) =
472 delete;
473
474 // Controls whether reuse is preferred under the main frame threshold policy.
475 bool ShouldReuseExistingProcessForNewMainFrameSiteInstance(
476 content::BrowserContext* browser_context,
477 const GURL& site_instance_original_url) override {
478 // Only reuse for foo.com/title1.html specifically.
479 if (site_instance_original_url.DomainIs("foo.com") &&
480 site_instance_original_url.path_piece() == "/title1.html") {
481 return true;
482 }
483 // For all other URLs, including other paths on foo.com or other domains,
484 // do not force reuse via this override. Let default policies apply.
485 return false;
486 }
487};
488
Abhijeet Kandalkarcaabfc32020-10-14 07:17:47489} // namespace
Bo Liuf3a5a642018-07-30 17:28:44490
dmazzoni0b5d2482014-09-10 19:45:57491//
Fergal Daly2e7e1e12020-06-24 09:18:28492// SitePerProcessBrowserTestBase
dmazzoni0b5d2482014-09-10 19:45:57493//
[email protected]c96e9702014-02-15 08:29:50494
Fergal Daly2e7e1e12020-06-24 09:18:28495SitePerProcessBrowserTestBase::SitePerProcessBrowserTestBase() {
Xiaohan Wang1ecfd002022-01-19 22:33:10496#if !BUILDFLAG(IS_ANDROID)
Ken Rockot28910122019-10-10 19:07:58497 // TODO(bokan): Needed for scrollability check in
498 // FrameOwnerPropertiesPropagationScrolling. crbug.com/662196.
Zhengzheng Liu1096965b2025-01-29 18:53:57499 // Overlay scrollbar will be turned off with both conditions satisfied:
500 // 1) feature flag `kOverlayScrollbar` is off
Zhengzheng Liua98daae7a2025-03-18 20:44:24501 // 2) always show scrollbar preference setting on.
Zhengzheng Liu1096965b2025-01-29 18:53:57502 feature_list_.InitWithFeatures(
503 /*enabled_features=*/{},
Zhengzheng Liua98daae7a2025-03-18 20:44:24504 /*disabled_features=*/{features::kOverlayScrollbar});
Ken Rockot28910122019-10-10 19:07:58505#endif
506}
dmazzoni0b5d2482014-09-10 19:45:57507
Fergal Daly2e7e1e12020-06-24 09:18:28508std::string SitePerProcessBrowserTestBase::DepictFrameTree(
509 FrameTreeNode* node) {
nick44bacf32015-04-14 02:06:39510 return visualizer_.DepictFrameTree(node);
nick59dcb162015-04-09 20:29:01511}
512
Chris Fredrickson5b9810c2023-03-27 21:08:30513std::string SitePerProcessBrowserTestBase::WaitForMessageScript(
514 const std::string& result_expression) {
515 return base::StringPrintf(
516 "var onMessagePromise = new Promise(resolve => {"
517 " window.addEventListener('message', function(event) {"
518 " resolve(%s);"
519 " });"
520 "});",
521 result_expression.c_str());
522}
523
Fergal Daly2e7e1e12020-06-24 09:18:28524void SitePerProcessBrowserTestBase::SetUpCommandLine(
avi83883c82014-12-23 00:08:49525 base::CommandLine* command_line) {
nickd30fd962015-07-27 21:51:08526 IsolateAllSitesForTesting(command_line);
Kevin McNeee8b9e972019-02-12 20:44:53527
Kartar Singhd084e582024-06-12 19:22:24528 command_line->AppendSwitch(input::switches::kValidateInputEventStream);
Ian Vollicke8c68ff02023-05-17 16:51:09529 // Without this, FocusFrame can be flaky. It depends on dispatching input
530 // events which can inadventently get dropped.
531 command_line->AppendSwitch(blink::switches::kAllowPreCommitInput);
chaopengd3ca34feb2017-04-20 17:11:22532}
[email protected]bbdd1b20b2012-12-11 21:24:13533
Fergal Daly2e7e1e12020-06-24 09:18:28534void SitePerProcessBrowserTestBase::SetUpOnMainThread() {
naskocbce0e62014-10-07 14:04:26535 host_resolver()->AddRule("*", "127.0.0.1");
naskocbce0e62014-10-07 14:04:26536 SetupCrossSiteRedirector(embedded_test_server());
martijna4dce162016-11-16 17:25:04537 ASSERT_TRUE(embedded_test_server()->Start());
naskocbce0e62014-10-07 14:04:26538}
539
Stefan Zager8544f8492021-01-16 00:16:55540void SitePerProcessBrowserTestBase::ForceUpdateViewportIntersection(
541 FrameTreeNode* frame_tree_node,
542 const blink::mojom::ViewportIntersectionState& intersection_state) {
543 frame_tree_node->render_manager()
544 ->GetProxyToParent()
545 ->cross_process_frame_connector()
Jayson Adamsf2f867e52021-06-24 21:36:44546 ->UpdateViewportIntersectionInternal(intersection_state, false);
Stefan Zager8544f8492021-01-16 00:16:55547}
548
Peter Kastinga903c7f2021-04-30 15:44:37549void SitePerProcessBrowserTestBase::RunPostedTasks() {
550 base::RunLoop loop;
Sean Maher5b9af51f2022-11-21 15:32:47551 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
552 FROM_HERE, loop.QuitClosure());
Peter Kastinga903c7f2021-04-30 15:44:37553 loop.Run();
554}
555
Fergal Daly2e7e1e12020-06-24 09:18:28556// SitePerProcessBrowserTest
Fergal Daly2e7e1e12020-06-24 09:18:28557
558SitePerProcessBrowserTest::SitePerProcessBrowserTest() {
559 InitAndEnableRenderDocumentFeature(&feature_list_, GetParam());
560}
561
Lukasz Anforowicze9db2652021-01-27 00:24:12562std::string SitePerProcessBrowserTest::GetExpectedOrigin(
563 const std::string& host) {
564 GURL url = embedded_test_server()->GetURL(host, "/");
565 return url::Origin::Create(url).Serialize();
566}
567
estark56dc8e22016-01-26 17:58:29568// SitePerProcessIgnoreCertErrorsBrowserTest
569
Sharon Yang7345f872021-12-15 18:01:24570void SitePerProcessIgnoreCertErrorsBrowserTest::SetUpOnMainThread() {
571 SitePerProcessBrowserTest::SetUpOnMainThread();
572 mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK);
573}
estark56dc8e22016-01-26 17:58:29574
Sharon Yang7345f872021-12-15 18:01:24575void SitePerProcessIgnoreCertErrorsBrowserTest::SetUpCommandLine(
576 base::CommandLine* command_line) {
577 SitePerProcessBrowserTest::SetUpCommandLine(command_line);
578 mock_cert_verifier_.SetUpCommandLine(command_line);
579}
Arthur Sonzogni33b19682021-08-06 12:43:31580
Sharon Yang7345f872021-12-15 18:01:24581void SitePerProcessIgnoreCertErrorsBrowserTest::
582 SetUpInProcessBrowserTestFixture() {
583 SitePerProcessBrowserTest::SetUpInProcessBrowserTestFixture();
584 mock_cert_verifier_.SetUpInProcessBrowserTestFixture();
585}
Arthur Sonzogni33b19682021-08-06 12:43:31586
Sharon Yang7345f872021-12-15 18:01:24587void SitePerProcessIgnoreCertErrorsBrowserTest::
588 TearDownInProcessBrowserTestFixture() {
589 SitePerProcessBrowserTest::TearDownInProcessBrowserTestFixture();
590 mock_cert_verifier_.TearDownInProcessBrowserTestFixture();
591}
estark56dc8e22016-01-26 17:58:29592
Becca Hughes60af7d42017-12-12 10:53:15593// SitePerProcessAutoplayBrowserTest
594
595class SitePerProcessAutoplayBrowserTest : public SitePerProcessBrowserTest {
596 public:
597 SitePerProcessAutoplayBrowserTest() = default;
598
599 void SetUpCommandLine(base::CommandLine* command_line) override {
Fergal Daly2e7e1e12020-06-24 09:18:28600 SitePerProcessBrowserTestBase::SetUpCommandLine(command_line);
Becca Hughes60af7d42017-12-12 10:53:15601 command_line->AppendSwitchASCII(
602 switches::kAutoplayPolicy,
603 switches::autoplay::kDocumentUserActivationRequiredPolicy);
Becca Hughes60af7d42017-12-12 10:53:15604 }
605
606 bool AutoplayAllowed(const ToRenderFrameHost& adapter,
607 bool with_user_gesture) {
Avi Drissmanc91bd8e2021-04-19 23:58:44608 return EvalJs(adapter, "attemptPlay();",
Chris Fredricksonb854bbf2023-03-27 17:27:44609 with_user_gesture ? EXECUTE_SCRIPT_DEFAULT_OPTIONS
610 : EXECUTE_SCRIPT_NO_USER_GESTURE)
Avi Drissmanc91bd8e2021-04-19 23:58:44611 .ExtractBool();
Becca Hughes60af7d42017-12-12 10:53:15612 }
Becca Hughes60af7d42017-12-12 10:53:15613};
614
Jiacheng Guo782c9ac2024-08-01 08:14:24615// Certain tests require the speculative RFH to be created before the browser
616// receives any data from the server. The delay of creating the RFH is set to 0
617// in these tests so that the speculative RFH is created when the request is
618// sent.
619class SitePerProcessBrowserTestWithoutSpeculativeRFHDelay
620 : public SitePerProcessBrowserTest {
621 public:
622 SitePerProcessBrowserTestWithoutSpeculativeRFHDelay() {
623 feature_list_for_defer_speculative_rfh_.InitAndEnableFeatureWithParameters(
624 features::kDeferSpeculativeRFHCreation,
625 {{"create_speculative_rfh_delay_ms", "0"}});
626 }
627
628 private:
629 base::test::ScopedFeatureList feature_list_for_defer_speculative_rfh_;
630};
631
nasko983ea9c2014-10-25 00:27:53632// Ensure that navigating subframes in --site-per-process mode works and the
633// correct documents are committed.
Fergal Daly2e7e1e12020-06-24 09:18:28634IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, CrossSiteIframe) {
nick4e68f5252015-08-28 20:17:05635 GURL main_url(embedded_test_server()->GetURL(
636 "a.com", "/cross_site_iframe_factory.html?a(a,a(a,a(a)))"));
davidsac6e6c35e42016-11-21 19:45:57637 EXPECT_TRUE(NavigateToURL(shell(), main_url));
[email protected]bbdd1b20b2012-12-11 21:24:13638
[email protected]893558b2014-04-25 23:01:06639 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:55640 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
[email protected]8d613aa2014-02-12 20:37:20641
clamyf1ccb4d2015-01-28 17:40:38642 TestNavigationObserver observer(shell()->web_contents());
[email protected]bbdd1b20b2012-12-11 21:24:13643
[email protected]a1b99262013-12-27 21:56:22644 // Load same-site page into iframe.
[email protected]9a1abe72014-06-19 23:49:02645 FrameTreeNode* child = root->child_at(0);
nick4e68f5252015-08-28 20:17:05646 GURL http_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:20647 EXPECT_TRUE(NavigateToURLFromRenderer(child, http_url));
clamyf1ccb4d2015-01-28 17:40:38648 EXPECT_EQ(http_url, observer.last_navigation_url());
649 EXPECT_TRUE(observer.last_navigation_succeeded());
[email protected]de3c5d82014-05-28 22:12:59650 {
651 // There should be only one RenderWidgetHost when there are no
652 // cross-process iframes.
Kevin McNeeef1e9362021-06-08 21:31:52653 std::set<RenderWidgetHostViewBase*> views_set =
654 web_contents()->GetRenderWidgetHostViewsInWebContentsTree();
[email protected]948481d2014-06-11 18:32:22655 EXPECT_EQ(1U, views_set.size());
[email protected]de3c5d82014-05-28 22:12:59656 }
nick44bacf32015-04-14 02:06:39657
658 EXPECT_EQ(
659 " Site A\n"
660 " |--Site A\n"
661 " +--Site A\n"
662 " |--Site A\n"
663 " +--Site A\n"
664 " +--Site A\n"
nick4e68f5252015-08-28 20:17:05665 "Where A = http://a.com/",
nick44bacf32015-04-14 02:06:39666 DepictFrameTree(root));
[email protected]a1b99262013-12-27 21:56:22667
668 // Load cross-site page into iframe.
nasko30374c72014-10-30 19:18:37669 GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html");
creis29f85682016-11-08 01:52:42670 {
671 RenderFrameDeletedObserver deleted_observer(child->current_frame_host());
Lukasz Anforowicz69c25dfd2020-11-12 21:50:20672 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), url));
creis29f85682016-11-08 01:52:42673 deleted_observer.WaitUntilDeleted();
674 }
naskocbce0e62014-10-07 14:04:26675 // Verify that the navigation succeeded and the expected URL was loaded.
clamyf1ccb4d2015-01-28 17:40:38676 EXPECT_TRUE(observer.last_navigation_succeeded());
677 EXPECT_EQ(url, observer.last_navigation_url());
[email protected]a1b99262013-12-27 21:56:22678
679 // Ensure that we have created a new process for the subframe.
naskoe6edde32014-10-17 15:36:48680 ASSERT_EQ(2U, root->child_count());
[email protected]893558b2014-04-25 23:01:06681 SiteInstance* site_instance = child->current_frame_host()->GetSiteInstance();
682 RenderViewHost* rvh = child->current_frame_host()->render_view_host();
683 RenderProcessHost* rph = child->current_frame_host()->GetProcess();
Dave Tapuska327c06c92022-06-13 20:31:51684 EXPECT_NE(shell()->web_contents()->GetPrimaryMainFrame()->GetRenderViewHost(),
685 rvh);
[email protected]893558b2014-04-25 23:01:06686 EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site_instance);
Dave Tapuska327c06c92022-06-13 20:31:51687 EXPECT_NE(shell()->web_contents()->GetPrimaryMainFrame()->GetProcess(), rph);
[email protected]de3c5d82014-05-28 22:12:59688 {
689 // There should be now two RenderWidgetHosts, one for each process
690 // rendering a frame.
Kevin McNeeef1e9362021-06-08 21:31:52691 std::set<RenderWidgetHostViewBase*> views_set =
692 web_contents()->GetRenderWidgetHostViewsInWebContentsTree();
[email protected]948481d2014-06-11 18:32:22693 EXPECT_EQ(2U, views_set.size());
[email protected]de3c5d82014-05-28 22:12:59694 }
Thiabaud Engelbrecht89f0d3a2022-08-10 01:45:15695 mojo::Remote<mojom::MainFrameCounterTest> main_frame_counter;
696 shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->BindReceiver(
697 main_frame_counter.BindNewPipeAndPassReceiver());
698
699 VerifyChildProcessHasMainFrame(main_frame_counter, true);
700
701 mojo::Remote<mojom::MainFrameCounterTest> main_frame_counter_child;
702 rph->BindReceiver(main_frame_counter_child.BindNewPipeAndPassReceiver());
703
704 VerifyChildProcessHasMainFrame(main_frame_counter_child, false);
705
nick44bacf32015-04-14 02:06:39706 RenderFrameProxyHost* proxy_to_parent =
707 child->render_manager()->GetProxyToParent();
[email protected]9a1abe72014-06-19 23:49:02708 EXPECT_TRUE(proxy_to_parent);
709 EXPECT_TRUE(proxy_to_parent->cross_process_frame_connector());
kenrba7199832015-01-22 23:44:59710 // The out-of-process iframe should have its own RenderWidgetHost,
711 // independent of any RenderViewHost.
712 EXPECT_NE(
avif9ab5d942015-10-15 14:05:44713 rvh->GetWidget()->GetView(),
[email protected]9a1abe72014-06-19 23:49:02714 proxy_to_parent->cross_process_frame_connector()->get_view_for_testing());
kenrba7199832015-01-22 23:44:59715 EXPECT_TRUE(child->current_frame_host()->GetRenderWidgetHost());
[email protected]893558b2014-04-25 23:01:06716
nick44bacf32015-04-14 02:06:39717 EXPECT_EQ(
718 " Site A ------------ proxies for B\n"
719 " |--Site B ------- proxies for A\n"
720 " +--Site A ------- proxies for B\n"
721 " |--Site A -- proxies for B\n"
722 " +--Site A -- proxies for B\n"
723 " +--Site A -- proxies for B\n"
nick4e68f5252015-08-28 20:17:05724 "Where A = http://a.com/\n"
nick44bacf32015-04-14 02:06:39725 " B = http://foo.com/",
726 DepictFrameTree(root));
727
[email protected]893558b2014-04-25 23:01:06728 // Load another cross-site page into the same iframe.
nasko30374c72014-10-30 19:18:37729 url = embedded_test_server()->GetURL("bar.com", "/title3.html");
creis29f85682016-11-08 01:52:42730 {
731 RenderFrameDeletedObserver deleted_observer(child->current_frame_host());
Lukasz Anforowicz69c25dfd2020-11-12 21:50:20732 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), url));
creis29f85682016-11-08 01:52:42733 deleted_observer.WaitUntilDeleted();
734 }
clamyf1ccb4d2015-01-28 17:40:38735 EXPECT_TRUE(observer.last_navigation_succeeded());
736 EXPECT_EQ(url, observer.last_navigation_url());
[email protected]893558b2014-04-25 23:01:06737
738 // Check again that a new process is created and is different from the
739 // top level one and the previous one.
naskoe6edde32014-10-17 15:36:48740 ASSERT_EQ(2U, root->child_count());
[email protected]893558b2014-04-25 23:01:06741 child = root->child_at(0);
Dave Tapuska327c06c92022-06-13 20:31:51742 EXPECT_NE(shell()->web_contents()->GetPrimaryMainFrame()->GetRenderViewHost(),
[email protected]a1b99262013-12-27 21:56:22743 child->current_frame_host()->render_view_host());
[email protected]893558b2014-04-25 23:01:06744 EXPECT_NE(rvh, child->current_frame_host()->render_view_host());
[email protected]a1b99262013-12-27 21:56:22745 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
[email protected]893558b2014-04-25 23:01:06746 child->current_frame_host()->GetSiteInstance());
747 EXPECT_NE(site_instance,
748 child->current_frame_host()->GetSiteInstance());
Dave Tapuska327c06c92022-06-13 20:31:51749 EXPECT_NE(shell()->web_contents()->GetPrimaryMainFrame()->GetProcess(),
[email protected]a1b99262013-12-27 21:56:22750 child->current_frame_host()->GetProcess());
[email protected]893558b2014-04-25 23:01:06751 EXPECT_NE(rph, child->current_frame_host()->GetProcess());
Thiabaud Engelbrecht89f0d3a2022-08-10 01:45:15752 VerifyChildProcessHasMainFrame(main_frame_counter, true);
[email protected]de3c5d82014-05-28 22:12:59753 {
Kevin McNeeef1e9362021-06-08 21:31:52754 std::set<RenderWidgetHostViewBase*> views_set =
755 web_contents()->GetRenderWidgetHostViewsInWebContentsTree();
[email protected]948481d2014-06-11 18:32:22756 EXPECT_EQ(2U, views_set.size());
[email protected]de3c5d82014-05-28 22:12:59757 }
[email protected]9a1abe72014-06-19 23:49:02758 EXPECT_EQ(proxy_to_parent, child->render_manager()->GetProxyToParent());
759 EXPECT_TRUE(proxy_to_parent->cross_process_frame_connector());
kenrba7199832015-01-22 23:44:59760 EXPECT_NE(
avi3627ecac2015-10-16 17:40:43761 child->current_frame_host()->render_view_host()->GetWidget()->GetView(),
[email protected]9a1abe72014-06-19 23:49:02762 proxy_to_parent->cross_process_frame_connector()->get_view_for_testing());
kenrba7199832015-01-22 23:44:59763 EXPECT_TRUE(child->current_frame_host()->GetRenderWidgetHost());
nick44bacf32015-04-14 02:06:39764
765 EXPECT_EQ(
766 " Site A ------------ proxies for C\n"
767 " |--Site C ------- proxies for A\n"
768 " +--Site A ------- proxies for C\n"
769 " |--Site A -- proxies for C\n"
770 " +--Site A -- proxies for C\n"
771 " +--Site A -- proxies for C\n"
nick4e68f5252015-08-28 20:17:05772 "Where A = http://a.com/\n"
nick44bacf32015-04-14 02:06:39773 " C = http://bar.com/",
774 DepictFrameTree(root));
[email protected]bbdd1b20b2012-12-11 21:24:13775}
776
Alex Moshchukafb91c392024-12-05 20:22:26777// Simple test to set up a A(B,C) page and then navigate the C subframe to D.
778// This can be used to study performance of proxy creation code.
779IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, NavigateABCToABD) {
780 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
781 EXPECT_TRUE(NavigateToURL(shell(), main_url));
782 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
783
784 // Add a new child frame and navigate it to B.
785 RenderFrameHostCreatedObserver frame_observer(shell()->web_contents(), 1);
786 EXPECT_TRUE(ExecJs(
787 root, "document.body.appendChild(document.createElement('iframe'));"));
788 frame_observer.Wait();
789
790 FrameTreeNode* child1 = root->child_at(0);
791 {
792 RenderFrameDeletedObserver deleted_observer(child1->current_frame_host());
793 GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
794 EXPECT_TRUE(NavigateToURLFromRenderer(child1, b_url));
795 deleted_observer.WaitUntilDeleted();
796 }
797
798 // Add a second child frame and navigate it to C.
799 RenderFrameHostCreatedObserver frame_observer2(shell()->web_contents(), 1);
800 EXPECT_TRUE(ExecJs(
801 root, "document.body.appendChild(document.createElement('iframe'));"));
802 frame_observer2.Wait();
803
804 FrameTreeNode* child2 = root->child_at(1);
805 {
806 RenderFrameDeletedObserver deleted_observer(child2->current_frame_host());
807 GURL c_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
808 EXPECT_TRUE(NavigateToURLFromRenderer(child2, c_url));
809 deleted_observer.WaitUntilDeleted();
810 }
811 EXPECT_EQ(
812 " Site A ------------ proxies for B C\n"
813 " |--Site B ------- proxies for A C\n"
814 " +--Site C ------- proxies for A B\n"
815 "Where A = http://a.com/\n"
816 " B = http://b.com/\n"
817 " C = http://c.com/",
818 DepictFrameTree(root));
819
820 // Navigate second child frame from C to D.
821 {
822 RenderFrameDeletedObserver deleted_observer(child2->current_frame_host());
823 GURL d_url(embedded_test_server()->GetURL("d.com", "/title1.html"));
824 EXPECT_TRUE(NavigateToURLFromRenderer(child2, d_url));
825 deleted_observer.WaitUntilDeleted();
826 }
827 EXPECT_EQ(
828 " Site A ------------ proxies for B D\n"
829 " |--Site B ------- proxies for A D\n"
830 " +--Site D ------- proxies for A B\n"
831 "Where A = http://a.com/\n"
832 " B = http://b.com/\n"
833 " D = http://d.com/",
834 DepictFrameTree(root));
835}
836
Thiabaud Engelbrecht89f0d3a2022-08-10 01:45:15837// Ensure that processes for iframes correctly track whether or not they have a
838// local main frame.
839IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
840 CrossSiteIframeMainFrameCount) {
841 GURL main_url(embedded_test_server()->GetURL(
842 "a.com", "/cross_site_iframe_factory.html?a(a,a,a(a,a))"));
843 EXPECT_TRUE(NavigateToURL(shell(), main_url));
844
845 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
846
847 TestNavigationObserver observer(shell()->web_contents());
848
849 EXPECT_EQ(
850 " Site A\n"
851 " |--Site A\n"
852 " |--Site A\n"
853 " +--Site A\n"
854 " |--Site A\n"
855 " +--Site A\n"
856 "Where A = http://a.com/",
857 DepictFrameTree(root));
858
859 mojo::Remote<mojom::MainFrameCounterTest> main_frame_counter;
860 shell()->web_contents()->GetPrimaryMainFrame()->GetProcess()->BindReceiver(
861 main_frame_counter.BindNewPipeAndPassReceiver());
862 VerifyChildProcessHasMainFrame(main_frame_counter, true);
863
864 GURL url = embedded_test_server()->GetURL(
865 "b.com", "/cross_site_iframe_factory.html?b(a,a)");
866 {
867 RenderFrameDeletedObserver deleted_observer(
868 root->child_at(2)->current_frame_host());
869 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(2), url));
870 deleted_observer.WaitUntilDeleted();
871 }
872
873 EXPECT_EQ(
874 " Site A ------------ proxies for B\n"
875 " |--Site A ------- proxies for B\n"
876 " |--Site A ------- proxies for B\n"
877 " +--Site B ------- proxies for A\n"
878 " |--Site A -- proxies for B\n"
879 " +--Site A -- proxies for B\n"
880 "Where A = http://a.com/\n"
881 " B = http://b.com/",
882 DepictFrameTree(root));
883
884 VerifyChildProcessHasMainFrame(main_frame_counter, true);
885
886 mojo::Remote<mojom::MainFrameCounterTest> main_frame_counter_child;
887 root->child_at(2)->current_frame_host()->GetProcess()->BindReceiver(
888 main_frame_counter_child.BindNewPipeAndPassReceiver());
889 VerifyChildProcessHasMainFrame(main_frame_counter_child, false);
890}
891
creis0bfe5282016-06-02 06:46:20892// Ensure that title updates affect the correct NavigationEntry after a new
893// subframe navigation with an out-of-process iframe. https://crbug.com/616609.
Fergal Daly2e7e1e12020-06-24 09:18:28894IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, TitleAfterCrossSiteIframe) {
creis0bfe5282016-06-02 06:46:20895 // Start at an initial page.
896 GURL initial_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
897 EXPECT_TRUE(NavigateToURL(shell(), initial_url));
898
899 // Navigate to a same-site page with a same-site iframe.
900 GURL main_url(embedded_test_server()->GetURL(
901 "a.com", "/cross_site_iframe_factory.html?a(a)"));
902 EXPECT_TRUE(NavigateToURL(shell(), main_url));
903
Carlos Caballero15caeeb2021-10-27 09:57:55904 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
creis0bfe5282016-06-02 06:46:20905
906 // Make the main frame update its title after the subframe loads.
Avi Drissmanc91bd8e2021-04-19 23:58:44907 EXPECT_TRUE(ExecJs(shell()->web_contents(),
908 "document.querySelector('iframe').onload = "
909 " function() { document.title = 'loaded'; };"));
creis0bfe5282016-06-02 06:46:20910 EXPECT_TRUE(
Avi Drissmanc91bd8e2021-04-19 23:58:44911 ExecJs(shell()->web_contents(), "document.title = 'not loaded';"));
Jan Wilken Dörrie2c470ea2021-03-22 22:26:24912 std::u16string expected_title(u"loaded");
creis0bfe5282016-06-02 06:46:20913 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
914
915 // Navigate the iframe cross-site.
916 TestNavigationObserver load_observer(shell()->web_contents());
917 GURL frame_url = embedded_test_server()->GetURL("b.com", "/title2.html");
Avi Drissmanc91bd8e2021-04-19 23:58:44918 EXPECT_TRUE(ExecJs(root->child_at(0)->current_frame_host(),
919 JsReplace("window.location.href = $1", frame_url)));
creis0bfe5282016-06-02 06:46:20920 load_observer.Wait();
921
922 // Wait for the title to update and ensure it affects the right NavEntry.
923 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
924 NavigationEntry* entry =
925 shell()->web_contents()->GetController().GetLastCommittedEntry();
926 EXPECT_EQ(expected_title, entry->GetTitle());
927}
928
wjmacleancff98372017-04-28 18:56:55929// This test verifies that scroll bubbling from an OOPIF properly forwards
930// GestureFlingStart events from the child frame to the parent frame. This
931// test times out on failure.
Fergal Daly2e7e1e12020-06-24 09:18:28932IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
wjmacleancff98372017-04-28 18:56:55933 GestureFlingStartEventsBubble) {
934 GURL main_url(embedded_test_server()->GetURL(
935 "a.com", "/cross_site_iframe_factory.html?a(b)"));
936 EXPECT_TRUE(NavigateToURL(shell(), main_url));
937
938 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:55939 ->GetPrimaryFrameTree()
940 .root();
wjmacleancff98372017-04-28 18:56:55941 ASSERT_EQ(1U, root->child_count());
942
943 FrameTreeNode* child_iframe_node = root->child_at(0);
944
sahel45a2b442017-07-04 15:23:28945 RenderWidgetHost* child_rwh =
946 child_iframe_node->current_frame_host()->GetRenderWidgetHost();
947
Sahel Sharifye6d81f472018-07-11 20:40:26948 // The fling start won't bubble since its corresponding GSB hasn't bubbled.
Kevin McNeecbb8687a2017-11-14 19:10:59949 InputEventAckWaiter gesture_fling_start_ack_observer(
Dave Tapuska347d60a2020-04-21 23:55:47950 child_rwh, blink::WebInputEvent::Type::kGestureFlingStart);
wjmacleancff98372017-04-28 18:56:55951
kylechara7c549b2019-07-29 17:47:28952 WaitForHitTestData(child_iframe_node->current_frame_host());
wjmacleancff98372017-04-28 18:56:55953
Kevin McNeecbb8687a2017-11-14 19:10:59954 gesture_fling_start_ack_observer.Reset();
Xida Chen2eec314f2018-06-07 11:42:42955
956 GenerateTapDownGesture(child_rwh);
957
wjmacleancff98372017-04-28 18:56:55958 // Send a GSB, GSU, GFS sequence and verify that the GFS bubbles.
959 blink::WebGestureEvent gesture_scroll_begin(
Dave Tapuska347d60a2020-04-21 23:55:47960 blink::WebGestureEvent::Type::kGestureScrollBegin,
wjmacleancff98372017-04-28 18:56:55961 blink::WebInputEvent::kNoModifiers,
Ella Ge1116059d2018-03-21 02:06:13962 blink::WebInputEvent::GetStaticTimeStampForTests(),
Daniel Cheng7f9ec902019-04-18 05:07:00963 blink::WebGestureDevice::kTouchscreen);
wjmacleancff98372017-04-28 18:56:55964 gesture_scroll_begin.data.scroll_begin.delta_hint_units =
Mohsen Izadiffcbc61f12020-02-09 06:31:27965 ui::ScrollGranularity::kScrollByPrecisePixel;
wjmacleancff98372017-04-28 18:56:55966 gesture_scroll_begin.data.scroll_begin.delta_x_hint = 0.f;
967 gesture_scroll_begin.data.scroll_begin.delta_y_hint = 5.f;
968
969 child_rwh->ForwardGestureEvent(gesture_scroll_begin);
970
971 blink::WebGestureEvent gesture_scroll_update(
Dave Tapuska347d60a2020-04-21 23:55:47972 blink::WebGestureEvent::Type::kGestureScrollUpdate,
wjmacleancff98372017-04-28 18:56:55973 blink::WebInputEvent::kNoModifiers,
Ella Ge1116059d2018-03-21 02:06:13974 blink::WebInputEvent::GetStaticTimeStampForTests(),
Daniel Cheng7f9ec902019-04-18 05:07:00975 blink::WebGestureDevice::kTouchscreen);
wjmacleancff98372017-04-28 18:56:55976 gesture_scroll_update.data.scroll_update.delta_units =
Mohsen Izadiffcbc61f12020-02-09 06:31:27977 ui::ScrollGranularity::kScrollByPrecisePixel;
wjmacleancff98372017-04-28 18:56:55978 gesture_scroll_update.data.scroll_update.delta_x = 0.f;
979 gesture_scroll_update.data.scroll_update.delta_y = 5.f;
wjmacleancff98372017-04-28 18:56:55980
981 child_rwh->ForwardGestureEvent(gesture_scroll_update);
982
983 blink::WebGestureEvent gesture_fling_start(
Dave Tapuska347d60a2020-04-21 23:55:47984 blink::WebGestureEvent::Type::kGestureFlingStart,
wjmacleancff98372017-04-28 18:56:55985 blink::WebInputEvent::kNoModifiers,
Ella Ge1116059d2018-03-21 02:06:13986 blink::WebInputEvent::GetStaticTimeStampForTests(),
Daniel Cheng7f9ec902019-04-18 05:07:00987 blink::WebGestureDevice::kTouchscreen);
wjmacleancff98372017-04-28 18:56:55988 gesture_fling_start.data.fling_start.velocity_x = 0.f;
989 gesture_fling_start.data.fling_start.velocity_y = 5.f;
990
991 child_rwh->ForwardGestureEvent(gesture_fling_start);
992
993 // We now wait for the fling start event to be acked by the parent
994 // frame. If the test fails, then the test times out.
Kevin McNeecbb8687a2017-11-14 19:10:59995 gesture_fling_start_ack_observer.Wait();
wjmacleancff98372017-04-28 18:56:55996}
997
Sahel Sharify70c3bf52018-03-21 18:39:06998// Test that fling on an out-of-process iframe progresses properly.
Fergal Daly2e7e1e12020-06-24 09:18:28999IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Sahel Sharify7c8dfc22018-05-25 00:11:581000 TouchscreenGestureFlingStart) {
Sahel Sharify70c3bf52018-03-21 18:39:061001 GURL main_url(embedded_test_server()->GetURL(
1002 "a.com", "/cross_site_iframe_factory.html?a(b)"));
1003 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1004
1005 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551006 ->GetPrimaryFrameTree()
1007 .root();
Sahel Sharify70c3bf52018-03-21 18:39:061008 ASSERT_EQ(1U, root->child_count());
1009
1010 FrameTreeNode* child_iframe_node = root->child_at(0);
1011
1012 RenderWidgetHost* child_rwh =
1013 child_iframe_node->current_frame_host()->GetRenderWidgetHost();
kylechara7c549b2019-07-29 17:47:281014 WaitForHitTestData(child_iframe_node->current_frame_host());
Sahel Sharify70c3bf52018-03-21 18:39:061015
Xida Chen2eec314f2018-06-07 11:42:421016 GenerateTapDownGesture(child_rwh);
Sahel Sharify70c3bf52018-03-21 18:39:061017 // Send a GSB to start scrolling sequence.
1018 blink::WebGestureEvent gesture_scroll_begin(
Dave Tapuska347d60a2020-04-21 23:55:471019 blink::WebGestureEvent::Type::kGestureScrollBegin,
Sahel Sharify70c3bf52018-03-21 18:39:061020 blink::WebInputEvent::kNoModifiers,
1021 blink::WebInputEvent::GetStaticTimeStampForTests());
Daniel Cheng7f9ec902019-04-18 05:07:001022 gesture_scroll_begin.SetSourceDevice(blink::WebGestureDevice::kTouchscreen);
Sahel Sharify70c3bf52018-03-21 18:39:061023 gesture_scroll_begin.data.scroll_begin.delta_hint_units =
Mohsen Izadiffcbc61f12020-02-09 06:31:271024 ui::ScrollGranularity::kScrollByPrecisePixel;
Sahel Sharify70c3bf52018-03-21 18:39:061025 gesture_scroll_begin.data.scroll_begin.delta_x_hint = 0.f;
1026 gesture_scroll_begin.data.scroll_begin.delta_y_hint = 5.f;
1027 child_rwh->ForwardGestureEvent(gesture_scroll_begin);
1028
1029 // Send a GFS and wait for the ack of the first GSU generated from progressing
1030 // the fling on the browser.
1031 InputEventAckWaiter gesture_scroll_update_ack_observer(
Dave Tapuska347d60a2020-04-21 23:55:471032 child_rwh, blink::WebInputEvent::Type::kGestureScrollUpdate);
Sahel Sharify70c3bf52018-03-21 18:39:061033 gesture_scroll_update_ack_observer.Reset();
1034 blink::WebGestureEvent gesture_fling_start(
Dave Tapuska347d60a2020-04-21 23:55:471035 blink::WebGestureEvent::Type::kGestureFlingStart,
Sahel Sharify70c3bf52018-03-21 18:39:061036 blink::WebInputEvent::kNoModifiers,
1037 blink::WebInputEvent::GetStaticTimeStampForTests());
Daniel Cheng7f9ec902019-04-18 05:07:001038 gesture_fling_start.SetSourceDevice(blink::WebGestureDevice::kTouchscreen);
Sahel Sharify70c3bf52018-03-21 18:39:061039 gesture_fling_start.data.fling_start.velocity_x = 0.f;
1040 gesture_fling_start.data.fling_start.velocity_y = 50.f;
1041 child_rwh->ForwardGestureEvent(gesture_fling_start);
1042 gesture_scroll_update_ack_observer.Wait();
1043}
1044
Sahel Sharify7c8dfc22018-05-25 00:11:581045// Test that fling on an out-of-process iframe progresses properly.
Fergal Daly2e7e1e12020-06-24 09:18:281046IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, TouchpadGestureFlingStart) {
Sahel Sharify7c8dfc22018-05-25 00:11:581047 GURL main_url(embedded_test_server()->GetURL(
1048 "a.com", "/cross_site_iframe_factory.html?a(b)"));
1049 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1050
1051 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551052 ->GetPrimaryFrameTree()
1053 .root();
Sahel Sharify7c8dfc22018-05-25 00:11:581054 ASSERT_EQ(1U, root->child_count());
1055
1056 FrameTreeNode* child_iframe_node = root->child_at(0);
1057
1058 RenderWidgetHost* child_rwh =
1059 child_iframe_node->current_frame_host()->GetRenderWidgetHost();
1060
1061 // Send a wheel event with phaseBegan to start scrolling sequence.
1062 InputEventAckWaiter gesture_scroll_begin_ack_observer(
Dave Tapuska347d60a2020-04-21 23:55:471063 child_rwh, blink::WebInputEvent::Type::kGestureScrollBegin);
Sahel Sharify7c8dfc22018-05-25 00:11:581064 blink::WebMouseWheelEvent scroll_event(
Dave Tapuska347d60a2020-04-21 23:55:471065 blink::WebInputEvent::Type::kMouseWheel,
1066 blink::WebInputEvent::kNoModifiers,
Sahel Sharify7c8dfc22018-05-25 00:11:581067 blink::WebInputEvent::GetStaticTimeStampForTests());
Mohsen Izadiffcbc61f12020-02-09 06:31:271068 scroll_event.delta_units = ui::ScrollGranularity::kScrollByPrecisePixel;
Sahel Sharify7c8dfc22018-05-25 00:11:581069 scroll_event.delta_x = 0.0f;
1070 scroll_event.delta_y = 5.0f;
1071 scroll_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
Sahel Sharify7c8dfc22018-05-25 00:11:581072 child_rwh->ForwardWheelEvent(scroll_event);
1073 gesture_scroll_begin_ack_observer.Wait();
1074
1075 // Send a GFS and wait for the ack of the first GSU generated from progressing
1076 // the fling on the browser.
1077 InputEventAckWaiter gesture_scroll_update_ack_observer(
Dave Tapuska347d60a2020-04-21 23:55:471078 child_rwh, blink::WebInputEvent::Type::kGestureScrollUpdate);
Sahel Sharify7c8dfc22018-05-25 00:11:581079 gesture_scroll_update_ack_observer.Reset();
1080 blink::WebGestureEvent gesture_fling_start(
Dave Tapuska347d60a2020-04-21 23:55:471081 blink::WebGestureEvent::Type::kGestureFlingStart,
Sahel Sharify7c8dfc22018-05-25 00:11:581082 blink::WebInputEvent::kNoModifiers,
1083 blink::WebInputEvent::GetStaticTimeStampForTests());
Daniel Cheng7f9ec902019-04-18 05:07:001084 gesture_fling_start.SetSourceDevice(blink::WebGestureDevice::kTouchpad);
Sahel Sharify7c8dfc22018-05-25 00:11:581085 gesture_fling_start.data.fling_start.velocity_x = 0.f;
1086 gesture_fling_start.data.fling_start.velocity_y = 50.f;
1087 child_rwh->ForwardGestureEvent(gesture_fling_start);
1088 // The test will pass when the GSU ack arrives, since it shows that the fling
1089 // controller has properly generated a GSU event from progressing the fling.
1090 gesture_scroll_update_ack_observer.Wait();
1091}
1092
lazyboy2f1a9d12015-04-03 05:16:151093// Tests OOPIF rendering by checking that the RWH of the iframe generates
1094// OnSwapCompositorFrame message.
Fergal Daly2e7e1e12020-06-24 09:18:281095IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, CompositorFrameSwapped) {
nick4e68f5252015-08-28 20:17:051096 GURL main_url(embedded_test_server()->GetURL(
1097 "a.com", "/cross_site_iframe_factory.html?a(baz)"));
davidsac6e6c35e42016-11-21 19:45:571098 EXPECT_TRUE(NavigateToURL(shell(), main_url));
lazyboy2f1a9d12015-04-03 05:16:151099
1100 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:551101 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
lazyboy2f1a9d12015-04-03 05:16:151102 ASSERT_EQ(1U, root->child_count());
1103
1104 FrameTreeNode* child_node = root->child_at(0);
nick4e68f5252015-08-28 20:17:051105 GURL site_url(embedded_test_server()->GetURL(
1106 "baz.com", "/cross_site_iframe_factory.html?baz()"));
lazyboy2f1a9d12015-04-03 05:16:151107 EXPECT_EQ(site_url, child_node->current_url());
1108 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
1109 child_node->current_frame_host()->GetSiteInstance());
jonrossafe76382018-05-25 16:44:321110 // Wait for CompositorFrame submission.
1111 RenderFrameSubmissionObserver observer(
1112 child_node->current_frame_host()
1113 ->GetRenderWidgetHost()
1114 ->render_frame_metadata_provider());
1115 observer.WaitForAnyFrameSubmission();
lazyboy2f1a9d12015-04-03 05:16:151116}
1117
creis0f6edddc2015-04-08 00:20:521118// Ensure that OOPIFs are deleted after navigating to a new main frame.
Fergal Daly2e7e1e12020-06-24 09:18:281119IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, CleanupCrossSiteIframe) {
Dominique Fauteux-Chapleau3e8d3452021-07-14 17:20:021120 // The test assumes the previous page gets deleted after navigation. Disable
1121 // back-forward cache to ensure that it doesn't get preserved in the cache.
1122 DisableBackForwardCacheForTesting(
Rakina Zata Amni30af7062022-01-19 23:46:361123 web_contents(), content::BackForwardCache::TEST_REQUIRES_NO_CACHING);
nick4e68f5252015-08-28 20:17:051124 GURL main_url(embedded_test_server()->GetURL(
1125 "a.com", "/cross_site_iframe_factory.html?a(a,a(a,a(a)))"));
davidsac6e6c35e42016-11-21 19:45:571126 EXPECT_TRUE(NavigateToURL(shell(), main_url));
creis0f6edddc2015-04-08 00:20:521127
1128 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:551129 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
creis0f6edddc2015-04-08 00:20:521130
1131 TestNavigationObserver observer(shell()->web_contents());
1132
1133 // Load a cross-site page into both iframes.
1134 GURL foo_url = embedded_test_server()->GetURL("foo.com", "/title2.html");
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201135 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), foo_url));
creis0f6edddc2015-04-08 00:20:521136 EXPECT_TRUE(observer.last_navigation_succeeded());
1137 EXPECT_EQ(foo_url, observer.last_navigation_url());
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201138 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(1), foo_url));
creis0f6edddc2015-04-08 00:20:521139 EXPECT_TRUE(observer.last_navigation_succeeded());
1140 EXPECT_EQ(foo_url, observer.last_navigation_url());
1141
1142 // Ensure that we have created a new process for the subframes.
naskof95ab0e2015-05-23 02:27:241143 EXPECT_EQ(
1144 " Site A ------------ proxies for B\n"
1145 " |--Site B ------- proxies for A\n"
1146 " +--Site B ------- proxies for A\n"
nick4e68f5252015-08-28 20:17:051147 "Where A = http://a.com/\n"
naskof95ab0e2015-05-23 02:27:241148 " B = http://foo.com/",
1149 DepictFrameTree(root));
1150
1151 int subframe_process_id = root->child_at(0)
1152 ->current_frame_host()
1153 ->GetSiteInstance()
1154 ->GetProcess()
Emily Andrewsd15fd762024-12-10 20:41:541155 ->GetDeprecatedID();
naskof95ab0e2015-05-23 02:27:241156 int subframe_rvh_id = root->child_at(0)
1157 ->current_frame_host()
1158 ->render_view_host()
1159 ->GetRoutingID();
1160 EXPECT_TRUE(RenderViewHost::FromID(subframe_process_id, subframe_rvh_id));
creis0f6edddc2015-04-08 00:20:521161
1162 // Use Javascript in the parent to remove one of the frames and ensure that
1163 // the subframe goes away.
Avi Drissmanc91bd8e2021-04-19 23:58:441164 EXPECT_TRUE(ExecJs(shell(),
1165 "document.body.removeChild("
1166 "document.querySelectorAll('iframe')[0])"));
creis0f6edddc2015-04-08 00:20:521167 ASSERT_EQ(1U, root->child_count());
1168
1169 // Load a new same-site page in the top-level frame and ensure the other
1170 // subframe goes away.
nick4e68f5252015-08-28 20:17:051171 GURL new_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
davidsac6e6c35e42016-11-21 19:45:571172 EXPECT_TRUE(NavigateToURL(shell(), new_url));
creis0f6edddc2015-04-08 00:20:521173 ASSERT_EQ(0U, root->child_count());
naskof95ab0e2015-05-23 02:27:241174
1175 // Ensure the RVH for the subframe gets cleaned up when the frame goes away.
1176 EXPECT_FALSE(RenderViewHost::FromID(subframe_process_id, subframe_rvh_id));
creis0f6edddc2015-04-08 00:20:521177}
1178
Fergal Daly2e7e1e12020-06-24 09:18:281179IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, NavigateRemoteFrame) {
nick4e68f5252015-08-28 20:17:051180 GURL main_url(embedded_test_server()->GetURL(
1181 "a.com", "/cross_site_iframe_factory.html?a(a,a(a,a(a)))"));
davidsac6e6c35e42016-11-21 19:45:571182 EXPECT_TRUE(NavigateToURL(shell(), main_url));
japhet70ea1342014-09-30 21:56:391183
1184 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:551185 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
japhet70ea1342014-09-30 21:56:391186
clamyf1ccb4d2015-01-28 17:40:381187 TestNavigationObserver observer(shell()->web_contents());
japhet70ea1342014-09-30 21:56:391188
1189 // Load same-site page into iframe.
1190 FrameTreeNode* child = root->child_at(0);
nick4e68f5252015-08-28 20:17:051191 GURL http_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201192 EXPECT_TRUE(NavigateToURLFromRenderer(child, http_url));
clamyf1ccb4d2015-01-28 17:40:381193 EXPECT_EQ(http_url, observer.last_navigation_url());
1194 EXPECT_TRUE(observer.last_navigation_succeeded());
japhet70ea1342014-09-30 21:56:391195
japhet70ea1342014-09-30 21:56:391196 // Load cross-site page into iframe.
nasko30374c72014-10-30 19:18:371197 GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html");
creis29f85682016-11-08 01:52:421198 {
1199 RenderFrameDeletedObserver deleted_observer(child->current_frame_host());
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201200 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), url));
creis29f85682016-11-08 01:52:421201 deleted_observer.WaitUntilDeleted();
1202 }
clamyf1ccb4d2015-01-28 17:40:381203 EXPECT_TRUE(observer.last_navigation_succeeded());
1204 EXPECT_EQ(url, observer.last_navigation_url());
japhet70ea1342014-09-30 21:56:391205
1206 // Ensure that we have created a new process for the subframe.
nick4e68f5252015-08-28 20:17:051207 EXPECT_EQ(
1208 " Site A ------------ proxies for B\n"
1209 " |--Site B ------- proxies for A\n"
1210 " +--Site A ------- proxies for B\n"
1211 " |--Site A -- proxies for B\n"
1212 " +--Site A -- proxies for B\n"
1213 " +--Site A -- proxies for B\n"
1214 "Where A = http://a.com/\n"
1215 " B = http://foo.com/",
1216 DepictFrameTree(root));
japhet70ea1342014-09-30 21:56:391217 SiteInstance* site_instance = child->current_frame_host()->GetSiteInstance();
1218 EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site_instance);
1219
1220 // Emulate the main frame changing the src of the iframe such that it
1221 // navigates cross-site.
nasko30374c72014-10-30 19:18:371222 url = embedded_test_server()->GetURL("bar.com", "/title3.html");
creis29f85682016-11-08 01:52:421223 {
1224 RenderFrameDeletedObserver deleted_observer(child->current_frame_host());
1225 NavigateIframeToURL(shell()->web_contents(), "child-0", url);
1226 deleted_observer.WaitUntilDeleted();
1227 }
clamyf1ccb4d2015-01-28 17:40:381228 EXPECT_TRUE(observer.last_navigation_succeeded());
1229 EXPECT_EQ(url, observer.last_navigation_url());
japhet70ea1342014-09-30 21:56:391230
1231 // Check again that a new process is created and is different from the
1232 // top level one and the previous one.
nick4e68f5252015-08-28 20:17:051233 EXPECT_EQ(
1234 " Site A ------------ proxies for C\n"
1235 " |--Site C ------- proxies for A\n"
1236 " +--Site A ------- proxies for C\n"
1237 " |--Site A -- proxies for C\n"
1238 " +--Site A -- proxies for C\n"
1239 " +--Site A -- proxies for C\n"
1240 "Where A = http://a.com/\n"
1241 " C = http://bar.com/",
1242 DepictFrameTree(root));
japhet70ea1342014-09-30 21:56:391243
japhete6adf142014-10-31 00:01:491244 // Navigate back to the parent's origin and ensure we return to the
1245 // parent's process.
creis29f85682016-11-08 01:52:421246 {
1247 RenderFrameDeletedObserver deleted_observer(child->current_frame_host());
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201248 EXPECT_TRUE(NavigateToURLFromRenderer(child, http_url));
creis29f85682016-11-08 01:52:421249 deleted_observer.WaitUntilDeleted();
1250 }
clamyf1ccb4d2015-01-28 17:40:381251 EXPECT_EQ(http_url, observer.last_navigation_url());
1252 EXPECT_TRUE(observer.last_navigation_succeeded());
japhete6adf142014-10-31 00:01:491253 EXPECT_EQ(shell()->web_contents()->GetSiteInstance(),
1254 child->current_frame_host()->GetSiteInstance());
japhet70ea1342014-09-30 21:56:391255}
1256
Fergal Daly2e7e1e12020-06-24 09:18:281257IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
nick4e68f5252015-08-28 20:17:051258 NavigateRemoteFrameToBlankAndDataURLs) {
1259 GURL main_url(embedded_test_server()->GetURL(
1260 "a.com", "/cross_site_iframe_factory.html?a(a,a(a))"));
davidsac6e6c35e42016-11-21 19:45:571261 EXPECT_TRUE(NavigateToURL(shell(), main_url));
lfgf52ea142015-03-07 23:03:331262
1263 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:551264 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
lfgf52ea142015-03-07 23:03:331265
1266 TestNavigationObserver observer(shell()->web_contents());
1267
1268 // Load same-site page into iframe.
1269 FrameTreeNode* child = root->child_at(0);
nick4e68f5252015-08-28 20:17:051270 GURL http_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201271 EXPECT_TRUE(NavigateToURLFromRenderer(child, http_url));
lfgf52ea142015-03-07 23:03:331272 EXPECT_EQ(http_url, observer.last_navigation_url());
1273 EXPECT_TRUE(observer.last_navigation_succeeded());
nick4e68f5252015-08-28 20:17:051274 EXPECT_EQ(
1275 " Site A\n"
1276 " |--Site A\n"
1277 " +--Site A\n"
1278 " +--Site A\n"
1279 "Where A = http://a.com/",
1280 DepictFrameTree(root));
lfgf52ea142015-03-07 23:03:331281
1282 // Load cross-site page into iframe.
1283 GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html");
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201284 EXPECT_TRUE(NavigateToURLFromRenderer(child, url));
lfgf52ea142015-03-07 23:03:331285 EXPECT_TRUE(observer.last_navigation_succeeded());
1286 EXPECT_EQ(url, observer.last_navigation_url());
nick4e68f5252015-08-28 20:17:051287 EXPECT_EQ(
1288 " Site A ------------ proxies for B\n"
1289 " |--Site B ------- proxies for A\n"
1290 " +--Site A ------- proxies for B\n"
1291 " +--Site A -- proxies for B\n"
1292 "Where A = http://a.com/\n"
1293 " B = http://foo.com/",
1294 DepictFrameTree(root));
lfgf52ea142015-03-07 23:03:331295
1296 // Navigate iframe to a data URL. The navigation happens from a script in the
Sharon Yang85d8ee72024-11-13 00:52:221297 // parent frame, so the data URL should be committed in the same
1298 // SiteInstanceGroup as the parent frame. If kSiteInstanceGroupsForDataUrls is
1299 // enabled, the data URL should be in its own SiteInstance. Otherwise it
1300 // shares a SiteInstance with its parent.
lfgf2d4f912016-05-11 23:18:481301 RenderFrameDeletedObserver deleted_observer1(
1302 root->child_at(0)->current_frame_host());
lfgf52ea142015-03-07 23:03:331303 GURL data_url("data:text/html,dataurl");
nick4e68f5252015-08-28 20:17:051304 NavigateIframeToURL(shell()->web_contents(), "child-0", data_url);
lfgf52ea142015-03-07 23:03:331305 EXPECT_TRUE(observer.last_navigation_succeeded());
1306 EXPECT_EQ(data_url, observer.last_navigation_url());
1307
lfgf2d4f912016-05-11 23:18:481308 // Wait for the old process to exit, to verify that the proxies go away.
1309 deleted_observer1.WaitUntilDeleted();
1310
lfgf52ea142015-03-07 23:03:331311 // Ensure that we have navigated using the top level process.
Sharon Yang85d8ee72024-11-13 00:52:221312 if (ShouldCreateSiteInstanceForDataUrls()) {
1313 // Site A and Site C are in the same SiteInstanceGroup, so there are no
1314 // proxies for each other.
1315 // TODO(crbug.com/341741267, yangsharon): Update output to show that A and C
1316 // are in the same SiteInstanceGroup.
1317 EXPECT_EQ(
1318 " Site A\n"
1319 " |--Site C\n"
1320 " +--Site A\n"
1321 " +--Site A\n"
1322 "Where A = http://a.com/\n"
1323 " C = data:nonce_C",
1324 DepictFrameTree(root));
1325 } else {
1326 EXPECT_EQ(
1327 " Site A\n"
1328 " |--Site A\n"
1329 " +--Site A\n"
1330 " +--Site A\n"
1331 "Where A = http://a.com/",
1332 DepictFrameTree(root));
1333 }
lfgf52ea142015-03-07 23:03:331334
1335 // Load cross-site page into iframe.
1336 url = embedded_test_server()->GetURL("bar.com", "/title2.html");
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201337 EXPECT_TRUE(NavigateToURLFromRenderer(child, url));
lfgf52ea142015-03-07 23:03:331338 EXPECT_TRUE(observer.last_navigation_succeeded());
1339 EXPECT_EQ(url, observer.last_navigation_url());
Sharon Yang85d8ee72024-11-13 00:52:221340 if (ShouldCreateSiteInstanceForDataUrls()) {
1341 EXPECT_EQ(
1342 " Site A ------------ proxies for D\n"
1343 " |--Site D ------- proxies for {A,C}\n"
1344 " +--Site A ------- proxies for D\n"
1345 " +--Site A -- proxies for D\n"
1346 "Where A = http://a.com/\n"
1347 " C = data:nonce_C\n"
1348 " D = http://bar.com/",
1349 DepictFrameTree(root));
1350 } else {
1351 EXPECT_EQ(
1352 " Site A ------------ proxies for C\n"
1353 " |--Site C ------- proxies for A\n"
1354 " +--Site A ------- proxies for C\n"
1355 " +--Site A -- proxies for C\n"
1356 "Where A = http://a.com/\n"
1357 " C = http://bar.com/",
1358 DepictFrameTree(root));
1359 }
lfgf52ea142015-03-07 23:03:331360
1361 // Navigate iframe to about:blank. The navigation happens from a script in the
1362 // parent frame, so it should be committed in the same SiteInstance as the
1363 // parent frame.
lfgf2d4f912016-05-11 23:18:481364 RenderFrameDeletedObserver deleted_observer2(
1365 root->child_at(0)->current_frame_host());
Nasko Oskovcae49c82018-04-27 19:08:131366 GURL about_blank_url("about:blank#foo");
nick4e68f5252015-08-28 20:17:051367 NavigateIframeToURL(shell()->web_contents(), "child-0", about_blank_url);
lfgf52ea142015-03-07 23:03:331368 EXPECT_TRUE(observer.last_navigation_succeeded());
1369 EXPECT_EQ(about_blank_url, observer.last_navigation_url());
1370
lfgf2d4f912016-05-11 23:18:481371 // Wait for the old process to exit, to verify that the proxies go away.
1372 deleted_observer2.WaitUntilDeleted();
1373
lfgf52ea142015-03-07 23:03:331374 // Ensure that we have navigated using the top level process.
nick4e68f5252015-08-28 20:17:051375 EXPECT_EQ(
1376 " Site A\n"
1377 " |--Site A\n"
1378 " +--Site A\n"
1379 " +--Site A\n"
1380 "Where A = http://a.com/",
1381 DepictFrameTree(root));
naskod948aa862016-04-26 19:58:521382
1383 // Load cross-site page into iframe again.
1384 url = embedded_test_server()->GetURL("f00.com", "/title3.html");
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201385 EXPECT_TRUE(NavigateToURLFromRenderer(child, url));
naskod948aa862016-04-26 19:58:521386 EXPECT_TRUE(observer.last_navigation_succeeded());
1387 EXPECT_EQ(url, observer.last_navigation_url());
Sharon Yang85d8ee72024-11-13 00:52:221388 if (ShouldCreateSiteInstanceForDataUrls()) {
1389 EXPECT_EQ(
1390 " Site A ------------ proxies for E\n"
1391 " |--Site E ------- proxies for {A,C}\n"
1392 " +--Site A ------- proxies for E\n"
1393 " +--Site A -- proxies for E\n"
1394 "Where A = http://a.com/\n"
1395 " C = data:nonce_C\n"
1396 " E = http://f00.com/",
1397 DepictFrameTree(root));
1398 } else {
1399 EXPECT_EQ(
1400 " Site A ------------ proxies for D\n"
1401 " |--Site D ------- proxies for A\n"
1402 " +--Site A ------- proxies for D\n"
1403 " +--Site A -- proxies for D\n"
1404 "Where A = http://a.com/\n"
1405 " D = http://f00.com/",
1406 DepictFrameTree(root));
1407 }
naskod948aa862016-04-26 19:58:521408
1409 // Navigate the iframe itself to about:blank using a script executing in its
1410 // own context. It should stay in the same SiteInstance as before, not the
1411 // parent one.
naskod948aa862016-04-26 19:58:521412 TestFrameNavigationObserver frame_observer(child);
Avi Drissmanc91bd8e2021-04-19 23:58:441413 EXPECT_TRUE(ExecJs(child, "window.location.href = 'about:blank#foo';"));
naskod948aa862016-04-26 19:58:521414 frame_observer.Wait();
1415 EXPECT_EQ(about_blank_url, child->current_url());
1416
1417 // Ensure that we have navigated using the top level process.
Sharon Yang85d8ee72024-11-13 00:52:221418 if (ShouldCreateSiteInstanceForDataUrls()) {
1419 EXPECT_EQ(
1420 " Site A ------------ proxies for E\n"
1421 " |--Site E ------- proxies for {A,C}\n"
1422 " +--Site A ------- proxies for E\n"
1423 " +--Site A -- proxies for E\n"
1424 "Where A = http://a.com/\n"
1425 " C = data:nonce_C\n"
1426 " E = http://f00.com/",
1427 DepictFrameTree(root));
1428 } else {
1429 EXPECT_EQ(
1430 " Site A ------------ proxies for D\n"
1431 " |--Site D ------- proxies for A\n"
1432 " +--Site A ------- proxies for D\n"
1433 " +--Site A -- proxies for D\n"
1434 "Where A = http://a.com/\n"
1435 " D = http://f00.com/",
1436 DepictFrameTree(root));
1437 }
lfgf52ea142015-03-07 23:03:331438}
1439
lazyboybb1af562015-02-04 02:36:021440// This test checks that killing a renderer process of a remote frame
1441// and then navigating some other frame to the same SiteInstance of the killed
1442// process works properly.
1443// This can be illustrated as follows,
1444// where 1/2/3 are FrameTreeNode-s and A/B are processes and B* is the killed
1445// B process:
lazyboyf2852202014-12-19 05:31:521446//
lazyboybb1af562015-02-04 02:36:021447// 1 A A A
1448// / \ -> / \ -> Kill B -> / \ -> Navigate 3 to B -> / \ .
1449// 2 3 B A B* A B* B
1450//
1451// Initially, node1.proxy_hosts_ = {B}
1452// After we kill B, we make sure B stays in node1.proxy_hosts_, then we navigate
1453// 3 to B and we expect that to complete normally.
1454// See http://crbug.com/432107.
1455//
1456// Note that due to http://crbug.com/450681, node2 cannot be re-navigated to
1457// site B and stays in not rendered state.
Fergal Daly2e7e1e12020-06-24 09:18:281458IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
lazyboybb1af562015-02-04 02:36:021459 NavigateRemoteFrameToKilledProcess) {
1460 GURL main_url(embedded_test_server()->GetURL(
nick4e68f5252015-08-28 20:17:051461 "foo.com", "/cross_site_iframe_factory.html?foo.com(bar.com, foo.com)"));
davidsac6e6c35e42016-11-21 19:45:571462 EXPECT_TRUE(NavigateToURL(shell(), main_url));
lazyboybb1af562015-02-04 02:36:021463
1464 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:551465 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
lazyboybb1af562015-02-04 02:36:021466
1467 TestNavigationObserver observer(shell()->web_contents());
1468 ASSERT_EQ(2U, root->child_count());
1469
1470 // Make sure node2 points to the correct cross-site page.
nick4e68f5252015-08-28 20:17:051471 GURL site_b_url = embedded_test_server()->GetURL(
1472 "bar.com", "/cross_site_iframe_factory.html?bar.com()");
lazyboybb1af562015-02-04 02:36:021473 FrameTreeNode* node2 = root->child_at(0);
1474 EXPECT_EQ(site_b_url, node2->current_url());
1475
1476 // Kill that cross-site renderer.
Ria Jiangabad8d9a2018-01-24 16:52:361477 RenderProcessHost* child_process = node2->current_frame_host()->GetProcess();
lazyboybb1af562015-02-04 02:36:021478 RenderProcessHostWatcher crash_observer(
1479 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:451480 child_process->Shutdown(0);
lazyboybb1af562015-02-04 02:36:021481 crash_observer.Wait();
1482
1483 // Now navigate the second iframe (node3) to the same site as the node2.
1484 FrameTreeNode* node3 = root->child_at(1);
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201485 EXPECT_TRUE(NavigateToURLFromRenderer(node3, site_b_url));
lazyboybb1af562015-02-04 02:36:021486 EXPECT_TRUE(observer.last_navigation_succeeded());
1487 EXPECT_EQ(site_b_url, observer.last_navigation_url());
1488}
1489
avallee8e881592016-11-19 00:39:301490// This test ensures that WebContentsImpl::FocusOwningWebContents does not crash
1491// the browser if the currently focused frame's renderer has disappeared.
Fergal Daly2e7e1e12020-06-24 09:18:281492IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, RemoveFocusFromKilledFrame) {
avallee8e881592016-11-19 00:39:301493 GURL main_url(embedded_test_server()->GetURL(
1494 "foo.com", "/cross_site_iframe_factory.html?foo.com(bar.com)"));
Alex Moshchukaeb20fe32019-09-25 17:40:011495 EXPECT_TRUE(NavigateToURL(shell(), main_url));
avallee8e881592016-11-19 00:39:301496
1497 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:551498 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
avallee8e881592016-11-19 00:39:301499
1500 TestNavigationObserver observer(shell()->web_contents());
1501 ASSERT_EQ(1U, root->child_count());
1502
1503 // Make sure node2 points to the correct cross-site page.
1504 GURL site_b_url = embedded_test_server()->GetURL(
1505 "bar.com", "/cross_site_iframe_factory.html?bar.com()");
1506 FrameTreeNode* node2 = root->child_at(0);
1507 EXPECT_EQ(site_b_url, node2->current_url());
1508
1509 web_contents()->SetFocusedFrame(
Sharon Yangefe52632022-03-08 23:06:061510 node2, node2->current_frame_host()->GetSiteInstance()->group());
avallee8e881592016-11-19 00:39:301511
1512 // Kill that cross-site renderer.
1513 RenderProcessHost* child_process = node2->current_frame_host()->GetProcess();
1514 RenderProcessHostWatcher crash_observer(
1515 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:451516 child_process->Shutdown(0);
avallee8e881592016-11-19 00:39:301517 crash_observer.Wait();
1518
1519 // Try to focus the root's owning WebContents.
1520 web_contents()->FocusOwningWebContents(
1521 root->current_frame_host()->GetRenderWidgetHost());
1522}
1523
lazyboybb1af562015-02-04 02:36:021524// This test is similar to
1525// SitePerProcessBrowserTest.NavigateRemoteFrameToKilledProcess with
1526// addition that node2 also has a cross-origin frame to site C.
1527//
1528// 1 A A A
1529// / \ / \ / \ / \ .
1530// 2 3 -> B A -> Kill B -> B* A -> Navigate 3 -> B* B
lazyboyf2852202014-12-19 05:31:521531// / /
1532// 4 C
1533//
lazyboybb1af562015-02-04 02:36:021534// Initially, node1.proxy_hosts_ = {B, C}
1535// After we kill B, we make sure B stays in node1.proxy_hosts_, but
1536// C gets cleared from node1.proxy_hosts_.
lazyboyf2852202014-12-19 05:31:521537//
lazyboybb1af562015-02-04 02:36:021538// Note that due to http://crbug.com/450681, node2 cannot be re-navigated to
1539// site B and stays in not rendered state.
Fergal Daly2e7e1e12020-06-24 09:18:281540IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
lazyboybb1af562015-02-04 02:36:021541 NavigateRemoteFrameToKilledProcessWithSubtree) {
nick44bacf32015-04-14 02:06:391542 GURL main_url(embedded_test_server()->GetURL(
nick4e68f5252015-08-28 20:17:051543 "a.com", "/cross_site_iframe_factory.html?a(bar(baz), a)"));
davidsac6e6c35e42016-11-21 19:45:571544 EXPECT_TRUE(NavigateToURL(shell(), main_url));
lazyboyf2852202014-12-19 05:31:521545
1546 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:551547 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
clamyf1ccb4d2015-01-28 17:40:381548 TestNavigationObserver observer(shell()->web_contents());
lazyboyf2852202014-12-19 05:31:521549
1550 ASSERT_EQ(2U, root->child_count());
1551
nick4e68f5252015-08-28 20:17:051552 GURL site_b_url(embedded_test_server()->GetURL(
1553 "bar.com", "/cross_site_iframe_factory.html?bar(baz())"));
clamyf1ccb4d2015-01-28 17:40:381554 // We can't use a TestNavigationObserver to verify the URL here,
lazyboyf2852202014-12-19 05:31:521555 // since the frame has children that may have clobbered it in the observer.
1556 EXPECT_EQ(site_b_url, root->child_at(0)->current_url());
1557
1558 // Ensure that a new process is created for node2.
1559 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
1560 root->child_at(0)->current_frame_host()->GetSiteInstance());
1561 // Ensure that a new process is *not* created for node3.
1562 EXPECT_EQ(shell()->web_contents()->GetSiteInstance(),
1563 root->child_at(1)->current_frame_host()->GetSiteInstance());
1564
1565 ASSERT_EQ(1U, root->child_at(0)->child_count());
1566
lazyboybb1af562015-02-04 02:36:021567 // Make sure node4 points to the correct cross-site page.
lazyboyf2852202014-12-19 05:31:521568 FrameTreeNode* node4 = root->child_at(0)->child_at(0);
nick4e68f5252015-08-28 20:17:051569 GURL site_c_url(embedded_test_server()->GetURL(
1570 "baz.com", "/cross_site_iframe_factory.html?baz()"));
lazyboybb1af562015-02-04 02:36:021571 EXPECT_EQ(site_c_url, node4->current_url());
1572
1573 // |site_instance_c| is expected to go away once we kill |child_process_b|
1574 // below, so create a local scope so we can extend the lifetime of
1575 // |site_instance_c| with a refptr.
1576 {
nick44bacf32015-04-14 02:06:391577 // Initially each frame has proxies for the other sites.
1578 EXPECT_EQ(
1579 " Site A ------------ proxies for B C\n"
1580 " |--Site B ------- proxies for A C\n"
1581 " | +--Site C -- proxies for A B\n"
1582 " +--Site A ------- proxies for B C\n"
1583 "Where A = http://a.com/\n"
1584 " B = http://bar.com/\n"
1585 " C = http://baz.com/",
1586 DepictFrameTree(root));
lazyboybb1af562015-02-04 02:36:021587
nick44bacf32015-04-14 02:06:391588 // Kill the render process for Site B.
lazyboybb1af562015-02-04 02:36:021589 RenderProcessHost* child_process_b =
1590 root->child_at(0)->current_frame_host()->GetProcess();
1591 RenderProcessHostWatcher crash_observer(
1592 child_process_b, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:451593 child_process_b->Shutdown(0);
lazyboybb1af562015-02-04 02:36:021594 crash_observer.Wait();
1595
nick44bacf32015-04-14 02:06:391596 // The Site C frame (a child of the crashed Site B frame) should go away,
1597 // and there should be no remaining proxies for site C anywhere.
1598 EXPECT_EQ(
1599 " Site A ------------ proxies for B\n"
1600 " |--Site B ------- proxies for A\n"
1601 " +--Site A ------- proxies for B\n"
1602 "Where A = http://a.com/\n"
1603 " B = http://bar.com/ (no process)",
1604 DepictFrameTree(root));
lazyboybb1af562015-02-04 02:36:021605 }
1606
nick44bacf32015-04-14 02:06:391607 // Now navigate the second iframe (node3) to Site B also.
lazyboybb1af562015-02-04 02:36:021608 FrameTreeNode* node3 = root->child_at(1);
1609 GURL url = embedded_test_server()->GetURL("bar.com", "/title1.html");
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201610 EXPECT_TRUE(NavigateToURLFromRenderer(node3, url));
clamyf1ccb4d2015-01-28 17:40:381611 EXPECT_TRUE(observer.last_navigation_succeeded());
lazyboybb1af562015-02-04 02:36:021612 EXPECT_EQ(url, observer.last_navigation_url());
nick44bacf32015-04-14 02:06:391613
1614 EXPECT_EQ(
1615 " Site A ------------ proxies for B\n"
1616 " |--Site B ------- proxies for A\n"
1617 " +--Site B ------- proxies for A\n"
1618 "Where A = http://a.com/\n"
1619 " B = http://bar.com/",
1620 DepictFrameTree(root));
lazyboybb1af562015-02-04 02:36:021621}
1622
creis93218682016-01-28 04:34:101623// Ensure that the renderer process doesn't crash when the main frame navigates
1624// a remote child to a page that results in a network error.
1625// See https://crbug.com/558016.
Fergal Daly2e7e1e12020-06-24 09:18:281626IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, NavigateRemoteAfterError) {
creis93218682016-01-28 04:34:101627 GURL main_url(embedded_test_server()->GetURL(
1628 "a.com", "/cross_site_iframe_factory.html?a(a)"));
davidsac6e6c35e42016-11-21 19:45:571629 EXPECT_TRUE(NavigateToURL(shell(), main_url));
creis93218682016-01-28 04:34:101630
1631 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:551632 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
creis93218682016-01-28 04:34:101633
creis93218682016-01-28 04:34:101634 // Load same-site page into iframe.
clamy7531fd262016-02-26 12:37:151635 {
1636 TestNavigationObserver observer(shell()->web_contents());
1637 FrameTreeNode* child = root->child_at(0);
1638 GURL http_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201639 EXPECT_TRUE(NavigateToURLFromRenderer(child, http_url));
clamy7531fd262016-02-26 12:37:151640 EXPECT_EQ(http_url, observer.last_navigation_url());
1641 EXPECT_TRUE(observer.last_navigation_succeeded());
1642 observer.Wait();
1643 }
creis93218682016-01-28 04:34:101644
1645 // Load cross-site page into iframe.
clamy7531fd262016-02-26 12:37:151646 {
1647 TestNavigationObserver observer(shell()->web_contents());
1648 FrameTreeNode* child = root->child_at(0);
1649 GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html");
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201650 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), url));
clamy7531fd262016-02-26 12:37:151651 EXPECT_TRUE(observer.last_navigation_succeeded());
1652 EXPECT_EQ(url, observer.last_navigation_url());
1653 observer.Wait();
creis93218682016-01-28 04:34:101654
clamy7531fd262016-02-26 12:37:151655 // Ensure that we have created a new process for the subframe.
1656 EXPECT_EQ(
1657 " Site A ------------ proxies for B\n"
1658 " +--Site B ------- proxies for A\n"
1659 "Where A = http://a.com/\n"
1660 " B = http://foo.com/",
1661 DepictFrameTree(root));
1662 SiteInstance* site_instance =
1663 child->current_frame_host()->GetSiteInstance();
1664 EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site_instance);
1665 }
creis93218682016-01-28 04:34:101666
1667 // Stop the test server and try to navigate the remote frame.
clamy7531fd262016-02-26 12:37:151668 {
1669 GURL url = embedded_test_server()->GetURL("bar.com", "/title3.html");
1670 EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
1671 NavigateIframeToURL(shell()->web_contents(), "child-0", url);
1672 }
creis93218682016-01-28 04:34:101673}
1674
creis1857908a2016-02-25 20:31:521675// Ensure that a cross-site page ends up in the correct process when it
1676// successfully loads after earlier encountering a network error for it.
1677// See https://crbug.com/560511.
1678// TODO(creis): Make the net error page show in the correct process as well,
1679// per https://crbug.com/588314.
Fergal Daly2e7e1e12020-06-24 09:18:281680IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, ProcessTransferAfterError) {
creis1857908a2016-02-25 20:31:521681 GURL main_url(embedded_test_server()->GetURL(
1682 "a.com", "/cross_site_iframe_factory.html?a(a)"));
1683 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1684
1685 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:551686 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
creis1857908a2016-02-25 20:31:521687 FrameTreeNode* child = root->child_at(0);
1688 GURL url_a = child->current_url();
1689
1690 // Disable host resolution in the test server and try to navigate the subframe
jam5c162d72017-01-26 16:47:191691 // cross-site, which will lead to a committed net error.
creis1857908a2016-02-25 20:31:521692 GURL url_b = embedded_test_server()->GetURL("b.com", "/title3.html");
Lei Zhange00db752021-04-17 00:48:461693 auto url_loader_interceptor = std::make_unique<URLLoaderInterceptor>(
John Abd-El-Malek64aa466c2018-05-02 20:59:311694 base::BindRepeating([](URLLoaderInterceptor::RequestParams* params) {
1695 network::URLLoaderCompletionStatus status;
1696 status.error_code = net::ERR_NOT_IMPLEMENTED;
1697 params->client->OnComplete(status);
1698 return true;
1699 }));
jambcc67882017-04-28 18:20:091700
creis1857908a2016-02-25 20:31:521701 TestNavigationObserver observer(shell()->web_contents());
1702 NavigateIframeToURL(shell()->web_contents(), "child-0", url_b);
jam5c162d72017-01-26 16:47:191703 EXPECT_FALSE(observer.last_navigation_succeeded());
creis1857908a2016-02-25 20:31:521704 EXPECT_EQ(url_b, observer.last_navigation_url());
clamy6e0ee03e2016-03-02 15:15:521705 EXPECT_EQ(2, shell()->web_contents()->GetController().GetEntryCount());
1706
Nasko Oskov63dda2952018-02-09 19:35:421707 // Ensure that we have created a new process for the subframe.
1708 EXPECT_EQ(
1709 " Site A ------------ proxies for B\n"
1710 " +--Site B ------- proxies for A\n"
1711 "Where A = http://a.com/\n"
1712 " B = http://b.com/",
1713 DepictFrameTree(root));
1714 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
1715 child->current_frame_host()->GetSiteInstance());
creis1857908a2016-02-25 20:31:521716
Nasko Oskov63dda2952018-02-09 19:35:421717 // We have switched RenderFrameHosts for the subframe, so the last successful
1718 // url should be empty (since the frame only loaded an error page).
1719 EXPECT_EQ(GURL(), child->current_frame_host()->last_successful_url());
creis1857908a2016-02-25 20:31:521720 EXPECT_EQ(url_b, child->current_url());
1721 EXPECT_EQ("null", child->current_origin().Serialize());
1722
1723 // Try again after re-enabling host resolution.
John Abd-El-Malek64aa466c2018-05-02 20:59:311724 url_loader_interceptor.reset();
jambcc67882017-04-28 18:20:091725
Mustaq Ahmeda4b45762021-02-05 16:40:301726 // Activate the root frame by executing a dummy script.
1727 //
1728 // TODO(mustaq): Why does the |back_load_observer.Wait()| below time out
1729 // without the user activation?
Avi Drissmanc91bd8e2021-04-19 23:58:441730 EXPECT_TRUE(ExecJs(root, "// No-op script"));
creis1857908a2016-02-25 20:31:521731 NavigateIframeToURL(shell()->web_contents(), "child-0", url_b);
1732 EXPECT_TRUE(observer.last_navigation_succeeded());
1733 EXPECT_EQ(url_b, observer.last_navigation_url());
1734
1735 // The FrameTreeNode should have updated its URL and origin.
1736 EXPECT_EQ(url_b, child->current_frame_host()->last_successful_url());
1737 EXPECT_EQ(url_b, child->current_url());
Mike West800532c2021-10-14 09:26:521738 EXPECT_EQ(url_b.DeprecatedGetOriginAsURL().spec(),
creis1857908a2016-02-25 20:31:521739 child->current_origin().Serialize() + '/');
1740
1741 // Ensure that we have created a new process for the subframe.
1742 EXPECT_EQ(
1743 " Site A ------------ proxies for B\n"
1744 " +--Site B ------- proxies for A\n"
1745 "Where A = http://a.com/\n"
1746 " B = http://b.com/",
1747 DepictFrameTree(root));
1748 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
1749 child->current_frame_host()->GetSiteInstance());
1750
1751 // Make sure that the navigation replaced the error page and that going back
1752 // ends up on the original site.
1753 EXPECT_EQ(2, shell()->web_contents()->GetController().GetEntryCount());
1754 {
lfgf2d4f912016-05-11 23:18:481755 RenderFrameDeletedObserver deleted_observer(child->current_frame_host());
creis1857908a2016-02-25 20:31:521756 TestNavigationObserver back_load_observer(shell()->web_contents());
1757 shell()->web_contents()->GetController().GoBack();
1758 back_load_observer.Wait();
lfgf2d4f912016-05-11 23:18:481759
1760 // Wait for the old process to exit, to verify that the proxies go away.
1761 deleted_observer.WaitUntilDeleted();
creis1857908a2016-02-25 20:31:521762 }
1763 EXPECT_EQ(
1764 " Site A\n"
1765 " +--Site A\n"
1766 "Where A = http://a.com/",
1767 DepictFrameTree(root));
1768 EXPECT_EQ(shell()->web_contents()->GetSiteInstance(),
1769 child->current_frame_host()->GetSiteInstance());
1770 EXPECT_EQ(url_a, child->current_frame_host()->last_successful_url());
1771 EXPECT_EQ(url_a, child->current_url());
Mike West800532c2021-10-14 09:26:521772 EXPECT_EQ(url_a.DeprecatedGetOriginAsURL().spec(),
creis1857908a2016-02-25 20:31:521773 child->current_origin().Serialize() + '/');
1774}
1775
alexmosdcbe3fb42015-04-29 23:10:371776// Verify that killing a cross-site frame's process B and then navigating a
1777// frame to B correctly recreates all proxies in B.
1778//
1779// 1 A A A
1780// / | \ / | \ / | \ / | \ .
1781// 2 3 4 -> B A A -> Kill B -> B* A A -> B* B A
1782//
1783// After the last step, the test sends a postMessage from node 3 to node 4,
1784// verifying that a proxy for node 4 has been recreated in process B. This
1785// verifies the fix for https://crbug.com/478892.
Fergal Daly2e7e1e12020-06-24 09:18:281786IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmosdcbe3fb42015-04-29 23:10:371787 NavigatingToKilledProcessRestoresAllProxies) {
1788 // Navigate to a page with three frames: one cross-site and two same-site.
1789 GURL main_url(embedded_test_server()->GetURL(
1790 "a.com", "/frame_tree/page_with_three_frames.html"));
davidsac6e6c35e42016-11-21 19:45:571791 EXPECT_TRUE(NavigateToURL(shell(), main_url));
alexmosdcbe3fb42015-04-29 23:10:371792
1793 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:551794 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmosdcbe3fb42015-04-29 23:10:371795 TestNavigationObserver observer(shell()->web_contents());
1796
1797 EXPECT_EQ(
1798 " Site A ------------ proxies for B\n"
1799 " |--Site B ------- proxies for A\n"
1800 " |--Site A ------- proxies for B\n"
1801 " +--Site A ------- proxies for B\n"
1802 "Where A = http://a.com/\n"
1803 " B = http://b.com/",
1804 DepictFrameTree(root));
1805
1806 // Kill the first subframe's b.com renderer.
1807 RenderProcessHost* child_process =
1808 root->child_at(0)->current_frame_host()->GetProcess();
1809 RenderProcessHostWatcher crash_observer(
1810 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:451811 child_process->Shutdown(0);
alexmosdcbe3fb42015-04-29 23:10:371812 crash_observer.Wait();
1813
1814 // Navigate the second subframe to b.com to recreate the b.com process.
1815 GURL b_url = embedded_test_server()->GetURL("b.com", "/post_message.html");
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201816 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(1), b_url));
alexmosdcbe3fb42015-04-29 23:10:371817 EXPECT_TRUE(observer.last_navigation_succeeded());
1818 EXPECT_EQ(b_url, observer.last_navigation_url());
1819 EXPECT_TRUE(root->child_at(1)->current_frame_host()->IsRenderFrameLive());
1820
1821 EXPECT_EQ(
1822 " Site A ------------ proxies for B\n"
1823 " |--Site B ------- proxies for A\n"
1824 " |--Site B ------- proxies for A\n"
1825 " +--Site A ------- proxies for B\n"
1826 "Where A = http://a.com/\n"
1827 " B = http://b.com/",
1828 DepictFrameTree(root));
1829
1830 // Check that third subframe's proxy is available in the b.com process by
alexmos9f8705a2015-05-06 19:58:591831 // sending it a postMessage from second subframe, and waiting for a reply.
1832 PostMessageAndWaitForReply(root->child_at(1),
1833 "postToSibling('subframe-msg','frame3')",
1834 "\"done-frame2\"");
alexmosdcbe3fb42015-04-29 23:10:371835}
1836
alexmosa3988992015-05-14 23:26:211837// Verify that proxy creation doesn't recreate a crashed process if no frame
1838// will be created in it.
1839//
1840// 1 A A A
1841// / | \ / | \ / | \ / | \ .
1842// 2 3 4 -> B A A -> Kill B -> B* A A -> B* A A
1843// \ .
1844// A
1845//
1846// The test kills process B (node 2), creates a child frame of node 4 in
1847// process A, and then checks that process B isn't resurrected to create a
1848// proxy for the new child frame. See https://crbug.com/476846.
Fergal Daly2e7e1e12020-06-24 09:18:281849IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmosa3988992015-05-14 23:26:211850 CreateChildFrameAfterKillingProcess) {
1851 // Navigate to a page with three frames: one cross-site and two same-site.
1852 GURL main_url(embedded_test_server()->GetURL(
1853 "a.com", "/frame_tree/page_with_three_frames.html"));
1854 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1855
1856 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:551857 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmosa3988992015-05-14 23:26:211858
1859 EXPECT_EQ(
1860 " Site A ------------ proxies for B\n"
1861 " |--Site B ------- proxies for A\n"
1862 " |--Site A ------- proxies for B\n"
1863 " +--Site A ------- proxies for B\n"
1864 "Where A = http://a.com/\n"
1865 " B = http://b.com/",
1866 DepictFrameTree(root));
Sharon Yang7424bda2021-11-04 20:27:431867 SiteInstanceImpl* b_site_instance =
alexmosa3988992015-05-14 23:26:211868 root->child_at(0)->current_frame_host()->GetSiteInstance();
1869
1870 // Kill the first subframe's renderer (B).
1871 RenderProcessHost* child_process =
1872 root->child_at(0)->current_frame_host()->GetProcess();
1873 RenderProcessHostWatcher crash_observer(
1874 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:451875 child_process->Shutdown(0);
alexmosa3988992015-05-14 23:26:211876 crash_observer.Wait();
1877
1878 // Add a new child frame to the third subframe.
1879 RenderFrameHostCreatedObserver frame_observer(shell()->web_contents(), 1);
Avi Drissmanc91bd8e2021-04-19 23:58:441880 EXPECT_TRUE(
1881 ExecJs(root->child_at(2),
1882 "document.body.appendChild(document.createElement('iframe'));"));
alexmosa3988992015-05-14 23:26:211883 frame_observer.Wait();
1884
1885 // The new frame should have a RenderFrameProxyHost for B, but it should not
1886 // be alive, and B should still not have a process (verified by last line of
1887 // expected DepictFrameTree output).
1888 EXPECT_EQ(
1889 " Site A ------------ proxies for B\n"
1890 " |--Site B ------- proxies for A\n"
1891 " |--Site A ------- proxies for B\n"
1892 " +--Site A ------- proxies for B\n"
1893 " +--Site A -- proxies for B\n"
1894 "Where A = http://a.com/\n"
1895 " B = http://b.com/ (no process)",
1896 DepictFrameTree(root));
1897 FrameTreeNode* grandchild = root->child_at(2)->child_at(0);
1898 RenderFrameProxyHost* grandchild_rfph =
Harkiran Bolariad22a1dca2022-02-22 17:01:121899 grandchild->current_frame_host()
1900 ->browsing_context_state()
1901 ->GetRenderFrameProxyHost(b_site_instance->group());
alexmosa3988992015-05-14 23:26:211902 EXPECT_FALSE(grandchild_rfph->is_render_frame_proxy_live());
1903
1904 // Navigate the second subframe to b.com to recreate process B.
1905 TestNavigationObserver observer(shell()->web_contents());
1906 GURL b_url = embedded_test_server()->GetURL("b.com", "/title1.html");
Lukasz Anforowicz69c25dfd2020-11-12 21:50:201907 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(1), b_url));
alexmosa3988992015-05-14 23:26:211908 EXPECT_TRUE(observer.last_navigation_succeeded());
1909 EXPECT_EQ(b_url, observer.last_navigation_url());
1910
Dave Tapuska2402595f2022-08-03 16:24:211911 // Ensure that the grandchild `blink::RemoteFrame` in B was created when
1912 // process B was restored.
alexmosa3988992015-05-14 23:26:211913 EXPECT_TRUE(grandchild_rfph->is_render_frame_proxy_live());
1914}
1915
alexmosd6ac54672015-06-25 18:10:141916// Verify that creating a child frame after killing and reloading an opener
1917// process doesn't crash. See https://crbug.com/501152.
1918// 1. Navigate to site A.
1919// 2. Open a popup with window.open and navigate it cross-process to site B.
1920// 3. Kill process A for the original tab.
1921// 4. Reload the original tab to resurrect process A.
1922// 5. Add a child frame to the top-level frame in the popup tab B.
1923// In step 5, we try to create proxies for the child frame in all SiteInstances
1924// for which its parent has proxies. This includes A. However, even though
1925// process A is live (step 4), the parent proxy in A is not live (which was
1926// incorrectly assumed previously). This is because step 4 does not resurrect
1927// proxies for popups opened before the crash.
Fergal Daly2e7e1e12020-06-24 09:18:281928IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmosd6ac54672015-06-25 18:10:141929 CreateChildFrameAfterKillingOpener) {
1930 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
1931 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1932
1933 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:551934 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Sharon Yang7424bda2021-11-04 20:27:431935 SiteInstanceImpl* site_instance_a =
1936 root->current_frame_host()->GetSiteInstance();
alexmosd6ac54672015-06-25 18:10:141937
1938 // Open a popup and navigate it cross-process to b.com.
1939 ShellAddedObserver new_shell_observer;
Avi Drissmanc91bd8e2021-04-19 23:58:441940 EXPECT_TRUE(ExecJs(root, "popup = window.open('about:blank');"));
alexmosd6ac54672015-06-25 18:10:141941 Shell* popup = new_shell_observer.GetShell();
1942 GURL popup_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
Alex Moshchuk7e26eca2018-03-03 01:34:291943 EXPECT_TRUE(NavigateToURLFromRenderer(popup, popup_url));
alexmosd6ac54672015-06-25 18:10:141944
1945 // Verify that each top-level frame has proxies in the other's SiteInstance.
1946 FrameTreeNode* popup_root =
1947 static_cast<WebContentsImpl*>(popup->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:551948 ->GetPrimaryFrameTree()
1949 .root();
alexmosd6ac54672015-06-25 18:10:141950 EXPECT_EQ(
1951 " Site A ------------ proxies for B\n"
1952 "Where A = http://a.com/\n"
1953 " B = http://b.com/",
1954 DepictFrameTree(root));
1955 EXPECT_EQ(
1956 " Site B ------------ proxies for A\n"
1957 "Where A = http://a.com/\n"
1958 " B = http://b.com/",
1959 DepictFrameTree(popup_root));
1960
1961 // Kill the first window's renderer (a.com).
1962 RenderProcessHost* child_process = root->current_frame_host()->GetProcess();
1963 RenderProcessHostWatcher crash_observer(
1964 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:451965 child_process->Shutdown(0);
alexmosd6ac54672015-06-25 18:10:141966 crash_observer.Wait();
1967 EXPECT_FALSE(root->current_frame_host()->IsRenderFrameLive());
1968
1969 // The proxy for the popup in a.com should've died.
1970 RenderFrameProxyHost* rfph =
Harkiran Bolariad22a1dca2022-02-22 17:01:121971 popup_root->current_frame_host()
1972 ->browsing_context_state()
1973 ->GetRenderFrameProxyHost(site_instance_a->group());
alexmosd6ac54672015-06-25 18:10:141974 EXPECT_FALSE(rfph->is_render_frame_proxy_live());
1975
1976 // Recreate the a.com renderer.
1977 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1978 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
1979
1980 // The popup's proxy in a.com should still not be live. Re-navigating the
1981 // main window to a.com doesn't reinitialize a.com proxies for popups
1982 // previously opened from the main window.
1983 EXPECT_FALSE(rfph->is_render_frame_proxy_live());
1984
1985 // Add a new child frame on the popup.
1986 RenderFrameHostCreatedObserver frame_observer(popup->web_contents(), 1);
Avi Drissmanc91bd8e2021-04-19 23:58:441987 EXPECT_TRUE(ExecJs(
nickadef4a52016-06-09 18:45:541988 popup, "document.body.appendChild(document.createElement('iframe'));"));
alexmosd6ac54672015-06-25 18:10:141989 frame_observer.Wait();
1990
1991 // Both the child frame's and its parent's proxies should still not be live.
1992 // The main page can't reach them since it lost reference to the popup after
1993 // it crashed, so there is no need to create them.
1994 EXPECT_FALSE(rfph->is_render_frame_proxy_live());
1995 RenderFrameProxyHost* child_rfph =
Harkiran Bolariad22a1dca2022-02-22 17:01:121996 popup_root->child_at(0)
1997 ->current_frame_host()
1998 ->browsing_context_state()
1999 ->GetRenderFrameProxyHost(site_instance_a->group());
alexmosd6ac54672015-06-25 18:10:142000 EXPECT_TRUE(child_rfph);
2001 EXPECT_FALSE(child_rfph->is_render_frame_proxy_live());
2002}
2003
lazyboybb1af562015-02-04 02:36:022004// In A-embed-B-embed-C scenario, verify that killing process B clears proxies
2005// of C from the tree.
2006//
2007// 1 A A
2008// / \ / \ / \ .
2009// 2 3 -> B A -> Kill B -> B* A
2010// / /
2011// 4 C
2012//
2013// node1 is the root.
2014// Initially, both node1.proxy_hosts_ and node3.proxy_hosts_ contain C.
2015// After we kill B, make sure proxies for C are cleared.
Fergal Daly2e7e1e12020-06-24 09:18:282016IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Ken Buchanancacd0082018-01-22 20:38:122017 KillingRendererClearsDescendantProxies) {
nick44bacf32015-04-14 02:06:392018 GURL main_url(embedded_test_server()->GetURL(
2019 "a.com", "/frame_tree/page_with_two_frames_nested.html"));
davidsac6e6c35e42016-11-21 19:45:572020 EXPECT_TRUE(NavigateToURL(shell(), main_url));
lazyboybb1af562015-02-04 02:36:022021
2022 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:552023 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
lazyboybb1af562015-02-04 02:36:022024 ASSERT_EQ(2U, root->child_count());
2025
Ria Jiangabad8d9a2018-01-24 16:52:362026 GURL site_b_url(embedded_test_server()->GetURL(
2027 "bar.com", "/frame_tree/page_with_one_frame.html"));
lazyboybb1af562015-02-04 02:36:022028 // We can't use a TestNavigationObserver to verify the URL here,
2029 // since the frame has children that may have clobbered it in the observer.
2030 EXPECT_EQ(site_b_url, root->child_at(0)->current_url());
2031
2032 // Ensure that a new process is created for node2.
2033 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
2034 root->child_at(0)->current_frame_host()->GetSiteInstance());
2035 // Ensure that a new process is *not* created for node3.
2036 EXPECT_EQ(shell()->web_contents()->GetSiteInstance(),
2037 root->child_at(1)->current_frame_host()->GetSiteInstance());
2038
2039 ASSERT_EQ(1U, root->child_at(0)->child_count());
2040
2041 // Make sure node4 points to the correct cross-site-page.
2042 FrameTreeNode* node4 = root->child_at(0)->child_at(0);
2043 GURL site_c_url(embedded_test_server()->GetURL("baz.com", "/title1.html"));
2044 EXPECT_EQ(site_c_url, node4->current_url());
lazyboyf2852202014-12-19 05:31:522045
Sharon Yanga2fe85e2022-02-09 21:38:292046 // |site_instance_c_group|'s frames and proxies are expected to go away once
2047 // we kill |child_process_b| below.
2048 scoped_refptr<SiteInstanceGroup> site_instance_c_group =
2049 node4->current_frame_host()->GetSiteInstance()->group();
lazyboyf2852202014-12-19 05:31:522050
nick44bacf32015-04-14 02:06:392051 // Initially proxies for both B and C will be present in the root.
2052 EXPECT_EQ(
2053 " Site A ------------ proxies for B C\n"
2054 " |--Site B ------- proxies for A C\n"
2055 " | +--Site C -- proxies for A B\n"
2056 " +--Site A ------- proxies for B C\n"
2057 "Where A = http://a.com/\n"
2058 " B = http://bar.com/\n"
2059 " C = http://baz.com/",
2060 DepictFrameTree(root));
creisceaabfd52015-05-27 20:26:462061
Sharon Yanga2fe85e2022-02-09 21:38:292062 EXPECT_GT(site_instance_c_group->active_frame_count(), 0U);
creisceaabfd52015-05-27 20:26:462063
nick44bacf32015-04-14 02:06:392064 // Kill process B.
2065 RenderProcessHost* child_process_b =
2066 root->child_at(0)->current_frame_host()->GetProcess();
2067 RenderProcessHostWatcher crash_observer(
2068 child_process_b, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:452069 child_process_b->Shutdown(0);
nick44bacf32015-04-14 02:06:392070 crash_observer.Wait();
lazyboyf2852202014-12-19 05:31:522071
nick44bacf32015-04-14 02:06:392072 // Make sure proxy C has gone from root.
2073 // Make sure proxy C has gone from node3 as well.
2074 // Make sure proxy B stays around in root and node3.
2075 EXPECT_EQ(
2076 " Site A ------------ proxies for B\n"
2077 " |--Site B ------- proxies for A\n"
2078 " +--Site A ------- proxies for B\n"
2079 "Where A = http://a.com/\n"
2080 " B = http://bar.com/ (no process)",
2081 DepictFrameTree(root));
lazyboyf2852202014-12-19 05:31:522082
Sharon Yanga2fe85e2022-02-09 21:38:292083 EXPECT_EQ(0U, site_instance_c_group->active_frame_count());
lazyboyf2852202014-12-19 05:31:522084}
2085
[email protected]81c6c5e2014-02-13 20:20:072086// Crash a subframe and ensures its children are cleared from the FrameTree.
2087// See http://crbug.com/338508.
Fergal Daly2e7e1e12020-06-24 09:18:282088IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, CrashSubframe) {
nick4e68f5252015-08-28 20:17:052089 GURL main_url(embedded_test_server()->GetURL(
2090 "a.com", "/cross_site_iframe_factory.html?a(b)"));
davidsac6e6c35e42016-11-21 19:45:572091 EXPECT_TRUE(NavigateToURL(shell(), main_url));
[email protected]81c6c5e2014-02-13 20:20:072092
[email protected]81c6c5e2014-02-13 20:20:072093 // Check the subframe process.
Carlos Caballero15caeeb2021-10-27 09:57:552094 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
nick4e68f5252015-08-28 20:17:052095 EXPECT_EQ(
2096 " Site A ------------ proxies for B\n"
2097 " +--Site B ------- proxies for A\n"
2098 "Where A = http://a.com/\n"
2099 " B = http://b.com/",
2100 DepictFrameTree(root));
[email protected]81c6c5e2014-02-13 20:20:072101 FrameTreeNode* child = root->child_at(0);
creise42f2a52014-09-18 18:14:572102 EXPECT_TRUE(
2103 child->current_frame_host()->render_view_host()->IsRenderViewLive());
2104 EXPECT_TRUE(child->current_frame_host()->IsRenderFrameLive());
2105
[email protected]81c6c5e2014-02-13 20:20:072106 // Crash the subframe process.
2107 RenderProcessHost* root_process = root->current_frame_host()->GetProcess();
2108 RenderProcessHost* child_process = child->current_frame_host()->GetProcess();
2109 {
2110 RenderProcessHostWatcher crash_observer(
Ria Jiangabad8d9a2018-01-24 16:52:362111 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:452112 child_process->Shutdown(0);
[email protected]81c6c5e2014-02-13 20:20:072113 crash_observer.Wait();
2114 }
2115
2116 // Ensure that the child frame still exists but has been cleared.
nick4e68f5252015-08-28 20:17:052117 EXPECT_EQ(
2118 " Site A ------------ proxies for B\n"
2119 " +--Site B ------- proxies for A\n"
2120 "Where A = http://a.com/\n"
2121 " B = http://b.com/ (no process)",
2122 DepictFrameTree(root));
2123 EXPECT_EQ(1U, root->child_count());
[email protected]58faf942014-02-20 21:03:582124 EXPECT_EQ(main_url, root->current_url());
2125 EXPECT_EQ(GURL(), child->current_url());
[email protected]81c6c5e2014-02-13 20:20:072126
creise42f2a52014-09-18 18:14:572127 EXPECT_FALSE(
2128 child->current_frame_host()->render_view_host()->IsRenderViewLive());
2129 EXPECT_FALSE(child->current_frame_host()->IsRenderFrameLive());
creise42f2a52014-09-18 18:14:572130
[email protected]81c6c5e2014-02-13 20:20:072131 // Now crash the top-level page to clear the child frame.
2132 {
2133 RenderProcessHostWatcher crash_observer(
Ria Jiangabad8d9a2018-01-24 16:52:362134 root_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:452135 root_process->Shutdown(0);
[email protected]81c6c5e2014-02-13 20:20:072136 crash_observer.Wait();
2137 }
2138 EXPECT_EQ(0U, root->child_count());
[email protected]58faf942014-02-20 21:03:582139 EXPECT_EQ(GURL(), root->current_url());
[email protected]81c6c5e2014-02-13 20:20:072140}
2141
alexmos46e85ec2015-04-03 21:04:352142// When a new subframe is added, related SiteInstances that can reach the
2143// subframe should create proxies for it (https://crbug.com/423587). This test
2144// checks that if A embeds B and later adds a new subframe A2, A2 gets a proxy
2145// in B's process.
Fergal Daly2e7e1e12020-06-24 09:18:282146IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, CreateProxiesForNewFrames) {
nick44bacf32015-04-14 02:06:392147 GURL main_url(embedded_test_server()->GetURL(
2148 "b.com", "/frame_tree/page_with_one_frame.html"));
alexmos46e85ec2015-04-03 21:04:352149 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2150
2151 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:552152 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos46e85ec2015-04-03 21:04:352153 ASSERT_EQ(1U, root->child_count());
2154
2155 // Make sure the frame starts out at the correct cross-site URL.
2156 EXPECT_EQ(embedded_test_server()->GetURL("baz.com", "/title1.html"),
2157 root->child_at(0)->current_url());
2158
nick44bacf32015-04-14 02:06:392159 EXPECT_EQ(
2160 " Site A ------------ proxies for B\n"
2161 " +--Site B ------- proxies for A\n"
2162 "Where A = http://b.com/\n"
2163 " B = http://baz.com/",
2164 DepictFrameTree(root));
2165
alexmos46e85ec2015-04-03 21:04:352166 // Add a new child frame to the top-level frame.
2167 RenderFrameHostCreatedObserver frame_observer(shell()->web_contents(), 1);
Sharon Yang85d8ee72024-11-13 00:52:222168 EXPECT_TRUE(ExecJs(shell(), "addFrame('about:blank');"));
alexmos46e85ec2015-04-03 21:04:352169 frame_observer.Wait();
alexmos46e85ec2015-04-03 21:04:352170
nick44bacf32015-04-14 02:06:392171 // The new frame should have a proxy in Site B, for use by the old frame.
2172 EXPECT_EQ(
2173 " Site A ------------ proxies for B\n"
2174 " |--Site B ------- proxies for A\n"
2175 " +--Site A ------- proxies for B\n"
2176 "Where A = http://b.com/\n"
2177 " B = http://baz.com/",
2178 DepictFrameTree(root));
alexmos46e85ec2015-04-03 21:04:352179}
2180
[email protected]0f7d449e2013-01-23 15:12:352181// TODO(nasko): Disable this test until out-of-process iframes is ready and the
2182// security checks are back in place.
Fergal Daly2e7e1e12020-06-24 09:18:282183IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
[email protected]0f7d449e2013-01-23 15:12:352184 DISABLED_CrossSiteIframeRedirectOnce) {
svaldezc3a9a172015-11-03 22:01:332185 net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
Alan Cutterfb5689192019-03-28 08:39:192186 https_server.ServeFilesFromSourceDirectory(GetTestDataFilePath());
[email protected]bbdd1b20b2012-12-11 21:24:132187 ASSERT_TRUE(https_server.Start());
2188
svaldezc3a9a172015-11-03 22:01:332189 GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
2190 GURL http_url(embedded_test_server()->GetURL("/title1.html"));
2191 GURL https_url(https_server.GetURL("/title1.html"));
[email protected]bbdd1b20b2012-12-11 21:24:132192
davidsac6e6c35e42016-11-21 19:45:572193 EXPECT_TRUE(NavigateToURL(shell(), main_url));
[email protected]bbdd1b20b2012-12-11 21:24:132194
clamyf1ccb4d2015-01-28 17:40:382195 TestNavigationObserver observer(shell()->web_contents());
[email protected]bbdd1b20b2012-12-11 21:24:132196 {
2197 // Load cross-site client-redirect page into Iframe.
2198 // Should be blocked.
svaldezc3a9a172015-11-03 22:01:332199 GURL client_redirect_https_url(
2200 https_server.GetURL("/client-redirect?/title1.html"));
alexmos75648bb32015-01-07 21:06:282201 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
2202 client_redirect_https_url));
[email protected]bbdd1b20b2012-12-11 21:24:132203 // DidFailProvisionalLoad when navigating to client_redirect_https_url.
clamyf1ccb4d2015-01-28 17:40:382204 EXPECT_EQ(observer.last_navigation_url(), client_redirect_https_url);
2205 EXPECT_FALSE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:132206 }
2207
2208 {
2209 // Load cross-site server-redirect page into Iframe,
2210 // which redirects to same-site page.
svaldezc3a9a172015-11-03 22:01:332211 GURL server_redirect_http_url(
2212 https_server.GetURL("/server-redirect?" + http_url.spec()));
alexmos75648bb32015-01-07 21:06:282213 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
2214 server_redirect_http_url));
clamyf1ccb4d2015-01-28 17:40:382215 EXPECT_EQ(observer.last_navigation_url(), http_url);
2216 EXPECT_TRUE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:132217 }
2218
2219 {
2220 // Load cross-site server-redirect page into Iframe,
2221 // which redirects to cross-site page.
svaldezc3a9a172015-11-03 22:01:332222 GURL server_redirect_http_url(
2223 https_server.GetURL("/server-redirect?/title1.html"));
alexmos75648bb32015-01-07 21:06:282224 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
2225 server_redirect_http_url));
[email protected]bbdd1b20b2012-12-11 21:24:132226 // DidFailProvisionalLoad when navigating to https_url.
clamyf1ccb4d2015-01-28 17:40:382227 EXPECT_EQ(observer.last_navigation_url(), https_url);
2228 EXPECT_FALSE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:132229 }
2230
2231 {
2232 // Load same-site server-redirect page into Iframe,
2233 // which redirects to cross-site page.
svaldezc3a9a172015-11-03 22:01:332234 GURL server_redirect_http_url(
2235 embedded_test_server()->GetURL("/server-redirect?" + https_url.spec()));
alexmos75648bb32015-01-07 21:06:282236 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
2237 server_redirect_http_url));
[email protected]bbdd1b20b2012-12-11 21:24:132238
clamyf1ccb4d2015-01-28 17:40:382239 EXPECT_EQ(observer.last_navigation_url(), https_url);
2240 EXPECT_FALSE(observer.last_navigation_succeeded());
Lukasz Anforowicz9e0ce4e2017-09-28 19:09:152241 }
[email protected]bbdd1b20b2012-12-11 21:24:132242
2243 {
2244 // Load same-site client-redirect page into Iframe,
2245 // which redirects to cross-site page.
svaldezc3a9a172015-11-03 22:01:332246 GURL client_redirect_http_url(
2247 embedded_test_server()->GetURL("/client-redirect?" + https_url.spec()));
[email protected]bbdd1b20b2012-12-11 21:24:132248
[email protected]71bc140b2022-03-14 23:45:012249 LoadStopObserver load_observer2(shell()->web_contents());
[email protected]bbdd1b20b2012-12-11 21:24:132250
alexmos75648bb32015-01-07 21:06:282251 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
2252 client_redirect_http_url));
[email protected]bbdd1b20b2012-12-11 21:24:132253
2254 // Same-site Client-Redirect Page should be loaded successfully.
clamyf1ccb4d2015-01-28 17:40:382255 EXPECT_EQ(observer.last_navigation_url(), client_redirect_http_url);
2256 EXPECT_TRUE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:132257
2258 // Redirecting to Cross-site Page should be blocked.
2259 load_observer2.Wait();
clamyf1ccb4d2015-01-28 17:40:382260 EXPECT_EQ(observer.last_navigation_url(), https_url);
2261 EXPECT_FALSE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:132262 }
2263
2264 {
2265 // Load same-site server-redirect page into Iframe,
2266 // which redirects to same-site page.
svaldezc3a9a172015-11-03 22:01:332267 GURL server_redirect_http_url(
2268 embedded_test_server()->GetURL("/server-redirect?/title1.html"));
alexmos75648bb32015-01-07 21:06:282269 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
2270 server_redirect_http_url));
clamyf1ccb4d2015-01-28 17:40:382271 EXPECT_EQ(observer.last_navigation_url(), http_url);
2272 EXPECT_TRUE(observer.last_navigation_succeeded());
Lukasz Anforowicz9e0ce4e2017-09-28 19:09:152273 }
[email protected]bbdd1b20b2012-12-11 21:24:132274
2275 {
2276 // Load same-site client-redirect page into Iframe,
2277 // which redirects to same-site page.
svaldezc3a9a172015-11-03 22:01:332278 GURL client_redirect_http_url(
2279 embedded_test_server()->GetURL("/client-redirect?" + http_url.spec()));
[email protected]71bc140b2022-03-14 23:45:012280 LoadStopObserver load_observer2(shell()->web_contents());
[email protected]bbdd1b20b2012-12-11 21:24:132281
alexmos75648bb32015-01-07 21:06:282282 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
2283 client_redirect_http_url));
[email protected]bbdd1b20b2012-12-11 21:24:132284
2285 // Same-site Client-Redirect Page should be loaded successfully.
clamyf1ccb4d2015-01-28 17:40:382286 EXPECT_EQ(observer.last_navigation_url(), client_redirect_http_url);
2287 EXPECT_TRUE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:132288
2289 // Redirecting to Same-site Page should be loaded successfully.
2290 load_observer2.Wait();
clamyf1ccb4d2015-01-28 17:40:382291 EXPECT_EQ(observer.last_navigation_url(), http_url);
2292 EXPECT_TRUE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:132293 }
2294}
2295
[email protected]0f7d449e2013-01-23 15:12:352296// TODO(nasko): Disable this test until out-of-process iframes is ready and the
2297// security checks are back in place.
Fergal Daly2e7e1e12020-06-24 09:18:282298IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
[email protected]0f7d449e2013-01-23 15:12:352299 DISABLED_CrossSiteIframeRedirectTwice) {
svaldezc3a9a172015-11-03 22:01:332300 net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
Alan Cutterfb5689192019-03-28 08:39:192301 https_server.ServeFilesFromSourceDirectory(GetTestDataFilePath());
[email protected]bbdd1b20b2012-12-11 21:24:132302 ASSERT_TRUE(https_server.Start());
2303
svaldezc3a9a172015-11-03 22:01:332304 GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
2305 GURL http_url(embedded_test_server()->GetURL("/title1.html"));
2306 GURL https_url(https_server.GetURL("/title1.html"));
[email protected]bbdd1b20b2012-12-11 21:24:132307
davidsac6e6c35e42016-11-21 19:45:572308 EXPECT_TRUE(NavigateToURL(shell(), main_url));
[email protected]bbdd1b20b2012-12-11 21:24:132309
clamyf1ccb4d2015-01-28 17:40:382310 TestNavigationObserver observer(shell()->web_contents());
[email protected]bbdd1b20b2012-12-11 21:24:132311 {
2312 // Load client-redirect page pointing to a cross-site client-redirect page,
2313 // which eventually redirects back to same-site page.
svaldezc3a9a172015-11-03 22:01:332314 GURL client_redirect_https_url(
2315 https_server.GetURL("/client-redirect?" + http_url.spec()));
2316 GURL client_redirect_http_url(embedded_test_server()->GetURL(
2317 "/client-redirect?" + client_redirect_https_url.spec()));
[email protected]bbdd1b20b2012-12-11 21:24:132318
2319 // We should wait until second client redirect get cancelled.
[email protected]71bc140b2022-03-14 23:45:012320 LoadStopObserver load_observer2(shell()->web_contents());
[email protected]bbdd1b20b2012-12-11 21:24:132321
alexmos75648bb32015-01-07 21:06:282322 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
2323 client_redirect_http_url));
[email protected]bbdd1b20b2012-12-11 21:24:132324
2325 // DidFailProvisionalLoad when navigating to client_redirect_https_url.
2326 load_observer2.Wait();
clamyf1ccb4d2015-01-28 17:40:382327 EXPECT_EQ(observer.last_navigation_url(), client_redirect_https_url);
2328 EXPECT_FALSE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:132329 }
2330
2331 {
2332 // Load server-redirect page pointing to a cross-site server-redirect page,
2333 // which eventually redirect back to same-site page.
svaldezc3a9a172015-11-03 22:01:332334 GURL server_redirect_https_url(
2335 https_server.GetURL("/server-redirect?" + http_url.spec()));
2336 GURL server_redirect_http_url(embedded_test_server()->GetURL(
2337 "/server-redirect?" + server_redirect_https_url.spec()));
alexmos75648bb32015-01-07 21:06:282338 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
2339 server_redirect_http_url));
clamyf1ccb4d2015-01-28 17:40:382340 EXPECT_EQ(observer.last_navigation_url(), http_url);
2341 EXPECT_TRUE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:132342 }
2343
2344 {
2345 // Load server-redirect page pointing to a cross-site server-redirect page,
2346 // which eventually redirects back to cross-site page.
svaldezc3a9a172015-11-03 22:01:332347 GURL server_redirect_https_url(
2348 https_server.GetURL("/server-redirect?" + https_url.spec()));
2349 GURL server_redirect_http_url(embedded_test_server()->GetURL(
2350 "/server-redirect?" + server_redirect_https_url.spec()));
alexmos75648bb32015-01-07 21:06:282351 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
2352 server_redirect_http_url));
[email protected]bbdd1b20b2012-12-11 21:24:132353
2354 // DidFailProvisionalLoad when navigating to https_url.
clamyf1ccb4d2015-01-28 17:40:382355 EXPECT_EQ(observer.last_navigation_url(), https_url);
2356 EXPECT_FALSE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:132357 }
2358
2359 {
2360 // Load server-redirect page pointing to a cross-site client-redirect page,
2361 // which eventually redirects back to same-site page.
svaldezc3a9a172015-11-03 22:01:332362 GURL client_redirect_http_url(
2363 https_server.GetURL("/client-redirect?" + http_url.spec()));
2364 GURL server_redirect_http_url(embedded_test_server()->GetURL(
2365 "/server-redirect?" + client_redirect_http_url.spec()));
alexmos75648bb32015-01-07 21:06:282366 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
2367 server_redirect_http_url));
[email protected]bbdd1b20b2012-12-11 21:24:132368
2369 // DidFailProvisionalLoad when navigating to client_redirect_http_url.
clamyf1ccb4d2015-01-28 17:40:382370 EXPECT_EQ(observer.last_navigation_url(), client_redirect_http_url);
2371 EXPECT_FALSE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:132372 }
2373}
2374
naskoe6edde32014-10-17 15:36:482375// Ensure that when navigating a frame cross-process RenderFrameProxyHosts are
Alex Moshchuk27caae82017-09-11 23:11:182376// created in the FrameTree skipping the subtree of the navigating frame (but
2377// not the navigating frame itself).
Fergal Daly2e7e1e12020-06-24 09:18:282378IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, ProxyCreationSkipsSubtree) {
nick4e68f5252015-08-28 20:17:052379 GURL main_url(embedded_test_server()->GetURL(
2380 "a.com", "/cross_site_iframe_factory.html?a(a,a(a,a(a)))"));
davidsac6e6c35e42016-11-21 19:45:572381 EXPECT_TRUE(NavigateToURL(shell(), main_url));
naskoe6edde32014-10-17 15:36:482382
2383 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:552384 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
naskoe6edde32014-10-17 15:36:482385
Ivan Kotenkov2c0d2bb32017-11-01 15:41:282386 EXPECT_TRUE(root->child_at(1) != nullptr);
naskoe6edde32014-10-17 15:36:482387 EXPECT_EQ(2U, root->child_at(1)->child_count());
2388
2389 {
2390 // Load same-site page into iframe.
clamyf1ccb4d2015-01-28 17:40:382391 TestNavigationObserver observer(shell()->web_contents());
nick4e68f5252015-08-28 20:17:052392 GURL http_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:202393 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), http_url));
clamyf1ccb4d2015-01-28 17:40:382394 EXPECT_EQ(http_url, observer.last_navigation_url());
2395 EXPECT_TRUE(observer.last_navigation_succeeded());
nick44bacf32015-04-14 02:06:392396 EXPECT_EQ(
2397 " Site A\n"
2398 " |--Site A\n"
2399 " +--Site A\n"
2400 " |--Site A\n"
2401 " +--Site A\n"
2402 " +--Site A\n"
nick4e68f5252015-08-28 20:17:052403 "Where A = http://a.com/",
nick44bacf32015-04-14 02:06:392404 DepictFrameTree(root));
naskoe6edde32014-10-17 15:36:482405 }
2406
2407 // Create the cross-site URL to navigate to.
nasko30374c72014-10-30 19:18:372408 GURL cross_site_url =
alexmos617df0372015-09-03 21:52:162409 embedded_test_server()->GetURL("foo.com", "/frame_tree/title2.html");
naskoe6edde32014-10-17 15:36:482410
2411 // Load cross-site page into the second iframe without waiting for the
2412 // navigation to complete. Once LoadURLWithParams returns, we would expect
2413 // proxies to have been created in the frame tree, but children of the
2414 // navigating frame to still be present. The reason is that we don't run the
2415 // message loop, so no IPCs that alter the frame tree can be processed.
2416 FrameTreeNode* child = root->child_at(1);
Ivan Kotenkov2c0d2bb32017-11-01 15:41:282417 SiteInstance* site = nullptr;
clamy610c63b32017-12-22 15:05:182418 std::string cross_site_rfh_type = "speculative";
naskoe6edde32014-10-17 15:36:482419 {
clamyf1ccb4d2015-01-28 17:40:382420 TestNavigationObserver observer(shell()->web_contents());
Jiacheng Guo4bdd0be2024-06-11 23:35:212421 TestNavigationManager navigation_manager(shell()->web_contents(),
2422 cross_site_url);
naskoe6edde32014-10-17 15:36:482423 NavigationController::LoadURLParams params(cross_site_url);
2424 params.transition_type = PageTransitionFromInt(ui::PAGE_TRANSITION_LINK);
2425 params.frame_tree_node_id = child->frame_tree_node_id();
Carlos Caballero04aab362021-02-15 17:38:162426 child->navigator().controller().LoadURLWithParams(params);
Jiacheng Guo4bdd0be2024-06-11 23:35:212427 navigation_manager.WaitForSpeculativeRenderFrameHostCreation();
naskoe6edde32014-10-17 15:36:482428
clamy610c63b32017-12-22 15:05:182429 site = child->render_manager()->speculative_frame_host()->GetSiteInstance();
naskoe6edde32014-10-17 15:36:482430 EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site);
2431
clamya529801b2015-06-02 13:28:212432 std::string tree = base::StringPrintf(
nick44bacf32015-04-14 02:06:392433 " Site A ------------ proxies for B\n"
2434 " |--Site A ------- proxies for B\n"
Alex Moshchuk27caae82017-09-11 23:11:182435 " +--Site A (B %s) -- proxies for B\n"
nick44bacf32015-04-14 02:06:392436 " |--Site A\n"
2437 " +--Site A\n"
2438 " +--Site A\n"
nick4e68f5252015-08-28 20:17:052439 "Where A = http://a.com/\n"
nick44bacf32015-04-14 02:06:392440 " B = http://foo.com/",
clamya529801b2015-06-02 13:28:212441 cross_site_rfh_type.c_str());
2442 EXPECT_EQ(tree, DepictFrameTree(root));
nick44bacf32015-04-14 02:06:392443
naskoe6edde32014-10-17 15:36:482444 // Now that the verification is done, run the message loop and wait for the
2445 // navigation to complete.
Jiacheng Guo4bdd0be2024-06-11 23:35:212446 ASSERT_TRUE(navigation_manager.WaitForNavigationFinished());
clamyf1ccb4d2015-01-28 17:40:382447 EXPECT_TRUE(observer.last_navigation_succeeded());
2448 EXPECT_EQ(cross_site_url, observer.last_navigation_url());
nick53d5cbf2015-04-23 22:50:142449
2450 EXPECT_EQ(
2451 " Site A ------------ proxies for B\n"
2452 " |--Site A ------- proxies for B\n"
2453 " +--Site B ------- proxies for A\n"
nick4e68f5252015-08-28 20:17:052454 "Where A = http://a.com/\n"
nick53d5cbf2015-04-23 22:50:142455 " B = http://foo.com/",
2456 DepictFrameTree(root));
naskoe6edde32014-10-17 15:36:482457 }
2458
2459 // Load another cross-site page into the same iframe.
alexmos617df0372015-09-03 21:52:162460 cross_site_url = embedded_test_server()->GetURL("bar.com", "/title3.html");
naskoe6edde32014-10-17 15:36:482461 {
2462 // Perform the same checks as the first cross-site navigation, since
2463 // there have been issues in subsequent cross-site navigations. Also ensure
2464 // that the SiteInstance has properly changed.
2465 // TODO(nasko): Once we have proper cleanup of resources, add code to
2466 // verify that the intermediate SiteInstance/RenderFrameHost have been
2467 // properly cleaned up.
clamyf1ccb4d2015-01-28 17:40:382468 TestNavigationObserver observer(shell()->web_contents());
Jiacheng Guo4bdd0be2024-06-11 23:35:212469 TestNavigationManager navigation_manager(shell()->web_contents(),
2470 cross_site_url);
naskoe6edde32014-10-17 15:36:482471 NavigationController::LoadURLParams params(cross_site_url);
2472 params.transition_type = PageTransitionFromInt(ui::PAGE_TRANSITION_LINK);
2473 params.frame_tree_node_id = child->frame_tree_node_id();
Carlos Caballero04aab362021-02-15 17:38:162474 child->navigator().controller().LoadURLWithParams(params);
Jiacheng Guo4bdd0be2024-06-11 23:35:212475 navigation_manager.WaitForSpeculativeRenderFrameHostCreation();
naskoe6edde32014-10-17 15:36:482476
clamy610c63b32017-12-22 15:05:182477 SiteInstance* site2 =
2478 child->render_manager()->speculative_frame_host()->GetSiteInstance();
naskoe6edde32014-10-17 15:36:482479 EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site2);
2480 EXPECT_NE(site, site2);
2481
clamya529801b2015-06-02 13:28:212482 std::string tree = base::StringPrintf(
nick44bacf32015-04-14 02:06:392483 " Site A ------------ proxies for B C\n"
2484 " |--Site A ------- proxies for B C\n"
Alex Moshchuk27caae82017-09-11 23:11:182485 " +--Site B (C %s) -- proxies for A C\n"
nick4e68f5252015-08-28 20:17:052486 "Where A = http://a.com/\n"
nick44bacf32015-04-14 02:06:392487 " B = http://foo.com/\n"
2488 " C = http://bar.com/",
clamya529801b2015-06-02 13:28:212489 cross_site_rfh_type.c_str());
2490 EXPECT_EQ(tree, DepictFrameTree(root));
naskoe6edde32014-10-17 15:36:482491
Jiacheng Guo4bdd0be2024-06-11 23:35:212492 ASSERT_TRUE(navigation_manager.WaitForNavigationFinished());
clamyf1ccb4d2015-01-28 17:40:382493 EXPECT_TRUE(observer.last_navigation_succeeded());
2494 EXPECT_EQ(cross_site_url, observer.last_navigation_url());
naskoe6edde32014-10-17 15:36:482495 EXPECT_EQ(0U, child->child_count());
2496 }
2497}
2498
nick9c79de22015-08-28 20:12:132499// Verify origin replication with an A-embed-B-embed-C-embed-A hierarchy.
Fergal Daly2e7e1e12020-06-24 09:18:282500IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, OriginReplication) {
nick9c79de22015-08-28 20:12:132501 GURL main_url(embedded_test_server()->GetURL(
2502 "a.com", "/cross_site_iframe_factory.html?a(b(c(a),b), a)"));
alexmos35d7b932014-12-05 03:55:232503 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2504
2505 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:552506 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos35d7b932014-12-05 03:55:232507
nick9c79de22015-08-28 20:12:132508 EXPECT_EQ(
2509 " Site A ------------ proxies for B C\n"
2510 " |--Site B ------- proxies for A C\n" // tiptop_child
2511 " | |--Site C -- proxies for A B\n" // middle_child
2512 " | | +--Site A -- proxies for B C\n" // lowest_child
2513 " | +--Site B -- proxies for A C\n"
2514 " +--Site A ------- proxies for B C\n"
2515 "Where A = http://a.com/\n"
2516 " B = http://b.com/\n"
2517 " C = http://c.com/",
2518 DepictFrameTree(root));
alexmos35d7b932014-12-05 03:55:232519
Nick Carterb7e71312018-08-03 23:36:132520 url::Origin a_origin =
2521 url::Origin::Create(embedded_test_server()->GetURL("a.com", "/"));
2522 url::Origin b_origin =
2523 url::Origin::Create(embedded_test_server()->GetURL("b.com", "/"));
2524 url::Origin c_origin =
2525 url::Origin::Create(embedded_test_server()->GetURL("c.com", "/"));
nick9c79de22015-08-28 20:12:132526 FrameTreeNode* tiptop_child = root->child_at(0);
2527 FrameTreeNode* middle_child = root->child_at(0)->child_at(0);
2528 FrameTreeNode* lowest_child = root->child_at(0)->child_at(0)->child_at(0);
alexmos35d7b932014-12-05 03:55:232529
nick9c79de22015-08-28 20:12:132530 // Check that b.com frame's location.ancestorOrigins contains the correct
alexmos17f643f2014-12-09 18:50:102531 // origin for the parent. The origin should have been replicated as part of
rockot067ca55f2016-09-30 22:00:152532 // the mojom::Renderer::CreateView message that created the parent's
Dave Tapuska2402595f2022-08-03 16:24:212533 // `blink::RemoteFrame` in b.com's process.
Nick Carterb7e71312018-08-03 23:36:132534 EXPECT_EQ(ListValueOf(a_origin),
2535 EvalJs(tiptop_child, "Array.from(location.ancestorOrigins);"));
alexmos17f643f2014-12-09 18:50:102536
nick9c79de22015-08-28 20:12:132537 // Check that c.com frame's location.ancestorOrigins contains the correct
alexmos17f643f2014-12-09 18:50:102538 // origin for its two ancestors. The topmost parent origin should be
rockot067ca55f2016-09-30 22:00:152539 // replicated as part of mojom::Renderer::CreateView, and the middle frame
rockot53be7caf2016-10-04 20:17:082540 // (b.com's) origin should be replicated as part of
Dave Tapuska0fcfeb782022-07-27 19:44:402541 // blink::mojom::RemoteFrame::CreateRemoteChild sent for b.com's frame in
2542 // c.com's process.
Nick Carterb7e71312018-08-03 23:36:132543 EXPECT_EQ(ListValueOf(b_origin, a_origin),
2544 EvalJs(middle_child, "Array.from(location.ancestorOrigins);"));
nick9c79de22015-08-28 20:12:132545
2546 // Check that the nested a.com frame's location.ancestorOrigins contains the
2547 // correct origin for its three ancestors.
Nick Carterb7e71312018-08-03 23:36:132548 EXPECT_EQ(ListValueOf(c_origin, b_origin, a_origin),
2549 EvalJs(lowest_child, "Array.from(location.ancestorOrigins);"));
alexmos35d7b932014-12-05 03:55:232550}
2551
Becca Hughes60af7d42017-12-12 10:53:152552// Test that HasReceivedUserGesture and HasReceivedUserGestureBeforeNavigation
2553// are propagated correctly across origins.
Alison Gale81f4f2c72024-04-22 19:33:312554// TODO(crbug.com/40653035): This test is flaky.
Fergal Daly2e7e1e12020-06-24 09:18:282555IN_PROC_BROWSER_TEST_P(SitePerProcessAutoplayBrowserTest,
bttk948fc042020-12-10 21:57:102556 DISABLED_PropagateUserGestureFlag) {
Becca Hughes60af7d42017-12-12 10:53:152557 GURL main_url(embedded_test_server()->GetURL(
2558 "example.com", "/media/autoplay/autoplay-enabled.html"));
2559 GURL foo_url(embedded_test_server()->GetURL(
2560 "foo.com", "/media/autoplay/autoplay-enabled.html"));
2561 GURL bar_url(embedded_test_server()->GetURL(
2562 "bar.com", "/media/autoplay/autoplay-enabled.html"));
2563 GURL secondary_url(embedded_test_server()->GetURL(
2564 "test.example.com", "/media/autoplay/autoplay-enabled.html"));
2565 GURL disabled_url(embedded_test_server()->GetURL(
2566 "test.example.com", "/media/autoplay/autoplay-disabled.html"));
2567
2568 // Load a page with an iframe that has autoplay.
Alex Moshchukebb63832020-11-04 23:34:442569 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:552570 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Becca Hughes60af7d42017-12-12 10:53:152571
2572 // Navigate the subframes to cross-origin pages.
Alex Moshchukebb63832020-11-04 23:34:442573 EXPECT_TRUE(NavigateFrameToURL(root->child_at(0), foo_url));
2574 EXPECT_TRUE(NavigateFrameToURL(root->child_at(0)->child_at(0), bar_url));
Becca Hughes60af7d42017-12-12 10:53:152575
2576 // Test that all frames can autoplay if there has been a gesture in the top
2577 // frame.
2578 EXPECT_TRUE(AutoplayAllowed(shell(), true));
2579 EXPECT_TRUE(AutoplayAllowed(root->child_at(0), false));
2580 EXPECT_TRUE(AutoplayAllowed(root->child_at(0)->child_at(0), false));
2581
2582 // Navigate to a new page on the same origin.
Alex Moshchukebb63832020-11-04 23:34:442583 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), secondary_url));
Carlos Caballero15caeeb2021-10-27 09:57:552584 root = web_contents()->GetPrimaryFrameTree().root();
Becca Hughes60af7d42017-12-12 10:53:152585
2586 // Navigate the subframes to cross-origin pages.
Alex Moshchukebb63832020-11-04 23:34:442587 EXPECT_TRUE(NavigateFrameToURL(root->child_at(0), foo_url));
2588 EXPECT_TRUE(NavigateFrameToURL(root->child_at(0)->child_at(0), bar_url));
Becca Hughes60af7d42017-12-12 10:53:152589
2590 // Test that all frames can autoplay because the gesture bit has been passed
2591 // through the navigation.
2592 EXPECT_TRUE(AutoplayAllowed(shell(), false));
2593 EXPECT_TRUE(AutoplayAllowed(root->child_at(0), false));
2594 EXPECT_TRUE(AutoplayAllowed(root->child_at(0)->child_at(0), false));
2595
2596 // Navigate to a page with autoplay disabled.
Alex Moshchukebb63832020-11-04 23:34:442597 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), disabled_url));
2598 EXPECT_TRUE(NavigateFrameToURL(root->child_at(0), foo_url));
Becca Hughes60af7d42017-12-12 10:53:152599
2600 // Test that autoplay is no longer allowed.
2601 EXPECT_TRUE(AutoplayAllowed(shell(), false));
2602 EXPECT_FALSE(AutoplayAllowed(root->child_at(0), false));
2603
2604 // Navigate to another origin and make sure autoplay is disabled.
Alex Moshchukebb63832020-11-04 23:34:442605 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), foo_url));
2606 EXPECT_TRUE(NavigateFrameToURL(root->child_at(0), bar_url));
Becca Hughes60af7d42017-12-12 10:53:152607 EXPECT_FALSE(AutoplayAllowed(shell(), false));
2608 EXPECT_FALSE(AutoplayAllowed(shell(), false));
2609}
2610
alexmosf832a2f2015-01-27 22:44:032611// Check that iframe sandbox flags are replicated correctly.
Fergal Daly2e7e1e12020-06-24 09:18:282612IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, SandboxFlagsReplication) {
alexmosf832a2f2015-01-27 22:44:032613 GURL main_url(embedded_test_server()->GetURL("/sandboxed_frames.html"));
Nick Carterb7e71312018-08-03 23:36:132614 const url::Origin main_origin = url::Origin::Create(main_url);
alexmosf832a2f2015-01-27 22:44:032615 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2616
2617 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:552618 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmosf832a2f2015-01-27 22:44:032619
clamyf1ccb4d2015-01-28 17:40:382620 TestNavigationObserver observer(shell()->web_contents());
alexmosf832a2f2015-01-27 22:44:032621
2622 // Navigate the second (sandboxed) subframe to a cross-site page with a
alexmos54dee8e2015-05-15 19:07:012623 // subframe.
alexmosf832a2f2015-01-27 22:44:032624 GURL foo_url(
2625 embedded_test_server()->GetURL("foo.com", "/frame_tree/1-1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:202626 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(1), foo_url));
alexmos54dee8e2015-05-15 19:07:012627 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
alexmosf832a2f2015-01-27 22:44:032628
clamyf1ccb4d2015-01-28 17:40:382629 // We can't use a TestNavigationObserver to verify the URL here,
alexmosf832a2f2015-01-27 22:44:032630 // since the frame has children that may have clobbered it in the observer.
2631 EXPECT_EQ(foo_url, root->child_at(1)->current_url());
2632
2633 // Load cross-site page into subframe's subframe.
2634 ASSERT_EQ(2U, root->child_at(1)->child_count());
2635 GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:202636 EXPECT_TRUE(
2637 NavigateToURLFromRenderer(root->child_at(1)->child_at(0), bar_url));
clamyf1ccb4d2015-01-28 17:40:382638 EXPECT_TRUE(observer.last_navigation_succeeded());
2639 EXPECT_EQ(bar_url, observer.last_navigation_url());
alexmosf832a2f2015-01-27 22:44:032640
2641 // Opening a popup in the sandboxed foo.com iframe should fail.
Nick Carterb7e71312018-08-03 23:36:132642 EXPECT_EQ(false, EvalJs(root->child_at(1),
2643 "!!window.open('data:text/html,dataurl');"));
alexmos6b294562015-03-05 19:24:102644 EXPECT_EQ(1u, Shell::windows().size());
alexmosf832a2f2015-01-27 22:44:032645
2646 // Opening a popup in a frame whose parent is sandboxed should also fail.
2647 // Here, bar.com frame's sandboxed parent frame is a remote frame in
2648 // bar.com's process.
Nick Carterb7e71312018-08-03 23:36:132649 EXPECT_EQ(false, EvalJs(root->child_at(1)->child_at(0),
2650 "!!window.open('data:text/html,dataurl');"));
alexmos6b294562015-03-05 19:24:102651 EXPECT_EQ(1u, Shell::windows().size());
alexmosf832a2f2015-01-27 22:44:032652
2653 // Same, but now try the case where bar.com frame's sandboxed parent is a
2654 // local frame in bar.com's process.
Nick Carterb7e71312018-08-03 23:36:132655 EXPECT_EQ(false, EvalJs(root->child_at(2)->child_at(0),
2656 "!!window.open('data:text/html,dataurl');"));
alexmos6b294562015-03-05 19:24:102657 EXPECT_EQ(1u, Shell::windows().size());
alexmosf832a2f2015-01-27 22:44:032658
2659 // Check that foo.com frame's location.ancestorOrigins contains the correct
2660 // origin for the parent, which should be unaffected by sandboxing.
Nick Carterb7e71312018-08-03 23:36:132661 EXPECT_EQ(ListValueOf(main_origin),
2662 EvalJs(root->child_at(1), "Array.from(location.ancestorOrigins);"));
alexmosf832a2f2015-01-27 22:44:032663
2664 // Now check location.ancestorOrigins for the bar.com frame. The middle frame
2665 // (foo.com's) origin should be unique, since that frame is sandboxed, and
2666 // the top frame should match |main_url|.
Nick Carterb7e71312018-08-03 23:36:132667 EXPECT_EQ(ListValueOf("null", main_origin),
2668 EvalJs(root->child_at(1)->child_at(0),
2669 "Array.from(location.ancestorOrigins);"));
alexmos6b294562015-03-05 19:24:102670}
2671
2672// Check that dynamic updates to iframe sandbox flags are propagated correctly.
Fergal Daly2e7e1e12020-06-24 09:18:282673IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, DynamicSandboxFlags) {
W. James MacLeanb0a37b7b2023-11-27 20:58:042674 bool sandboxed_iframes_are_isolated =
2675 SiteIsolationPolicy::AreIsolatedSandboxedIframesEnabled();
alexmos6b294562015-03-05 19:24:102676 GURL main_url(
2677 embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html"));
2678 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2679
2680 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:552681 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos6b294562015-03-05 19:24:102682
2683 TestNavigationObserver observer(shell()->web_contents());
2684 ASSERT_EQ(2U, root->child_count());
2685
2686 // Make sure first frame starts out at the correct cross-site page.
2687 EXPECT_EQ(embedded_test_server()->GetURL("bar.com", "/title1.html"),
2688 root->child_at(0)->current_url());
2689
2690 // Navigate second frame to another cross-site page.
2691 GURL baz_url(embedded_test_server()->GetURL("baz.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:202692 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(1), baz_url));
alexmos6b294562015-03-05 19:24:102693 EXPECT_TRUE(observer.last_navigation_succeeded());
2694 EXPECT_EQ(baz_url, observer.last_navigation_url());
2695
2696 // Both frames should not be sandboxed to start with.
arthursonzognib93a4472020-04-10 07:38:002697 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:122698 root->child_at(0)->pending_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:002699 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:122700 root->child_at(0)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:002701 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:122702 root->child_at(1)->pending_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:002703 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:122704 root->child_at(1)->effective_frame_policy().sandbox_flags);
alexmos6b294562015-03-05 19:24:102705
2706 // Dynamically update sandbox flags for the first frame.
Avi Drissmanc91bd8e2021-04-19 23:58:442707 EXPECT_TRUE(ExecJs(
Ria Jiangabad8d9a2018-01-24 16:52:362708 shell(), "document.querySelector('iframe').sandbox='allow-scripts';"));
alexmos6b294562015-03-05 19:24:102709
2710 // Check that updated sandbox flags are propagated to browser process.
Ian Clellandcdc4f312017-10-13 22:24:122711 // The new flags should be reflected in pending_frame_policy().sandbox_flags,
2712 // while effective_frame_policy().sandbox_flags should still reflect the old
2713 // flags, because sandbox flag updates take place only after navigations.
2714 // "allow-scripts" resets both SandboxFlags::Scripts and
2715 // SandboxFlags::AutomaticFeatures bits per blink::parseSandboxPolicy().
arthursonzognib93a4472020-04-10 07:38:002716 network::mojom::WebSandboxFlags expected_flags =
2717 network::mojom::WebSandboxFlags::kAll &
2718 ~network::mojom::WebSandboxFlags::kScripts &
2719 ~network::mojom::WebSandboxFlags::kAutomaticFeatures;
Ian Clellandcdc4f312017-10-13 22:24:122720 EXPECT_EQ(expected_flags,
2721 root->child_at(0)->pending_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:002722 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:122723 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmos6b294562015-03-05 19:24:102724
2725 // Navigate the first frame to a page on the same site. The new sandbox
avi98405c22015-05-21 20:47:062726 // flags should take effect.
alexmos6b294562015-03-05 19:24:102727 GURL bar_url(
2728 embedded_test_server()->GetURL("bar.com", "/frame_tree/2-4.html"));
W. James MacLeane176f342024-01-05 23:49:562729 {
2730 RenderFrameDeletedObserver deleted_observer(
2731 root->child_at(0)->current_frame_host());
2732 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), bar_url));
2733 if (sandboxed_iframes_are_isolated) {
2734 deleted_observer.WaitUntilDeleted();
2735 }
2736 }
avi98405c22015-05-21 20:47:062737 // (The new page has a subframe; wait for it to load as well.)
2738 ASSERT_TRUE(WaitForLoadStop(shell()->web_contents()));
alexmos6b294562015-03-05 19:24:102739 EXPECT_EQ(bar_url, root->child_at(0)->current_url());
2740 ASSERT_EQ(1U, root->child_at(0)->child_count());
2741
creis2069a0a2015-05-22 22:13:472742 EXPECT_EQ(
W. James MacLeanb0a37b7b2023-11-27 20:58:042743 base::StringPrintf(" Site A ------------ proxies for B C\n"
2744 " |--Site B ------- proxies for A C\n"
2745 " | +--Site B -- proxies for A C\n"
2746 " +--Site C ------- proxies for A B\n"
2747 "Where A = http://127.0.0.1/\n"
2748 " B = http://bar.com/%s\n"
2749 " C = http://baz.com/",
2750 sandboxed_iframes_are_isolated ? " (sandboxed)" : ""),
creis2069a0a2015-05-22 22:13:472751 DepictFrameTree(root));
2752
alexmos6b294562015-03-05 19:24:102753 // Confirm that the browser process has updated the frame's current sandbox
2754 // flags.
Ian Clellandcdc4f312017-10-13 22:24:122755 EXPECT_EQ(expected_flags,
2756 root->child_at(0)->pending_frame_policy().sandbox_flags);
2757 EXPECT_EQ(expected_flags,
2758 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmos6b294562015-03-05 19:24:102759
2760 // Opening a popup in the now-sandboxed frame should fail.
Nick Carterb7e71312018-08-03 23:36:132761 EXPECT_EQ(false, EvalJs(root->child_at(0),
2762 "!!window.open('data:text/html,dataurl');"));
alexmos6b294562015-03-05 19:24:102763 EXPECT_EQ(1u, Shell::windows().size());
2764
2765 // Navigate the child of the now-sandboxed frame to a page on baz.com. The
2766 // child should inherit the latest sandbox flags from its parent frame, which
2767 // is currently a proxy in baz.com's renderer process. This checks that the
2768 // proxies of |root->child_at(0)| were also updated with the latest sandbox
2769 // flags.
Alison Gale770f3fc2024-04-27 00:39:582770 // TODO(crbug.com/40943240): When IsolateSandboxedIframes is enabled,
W. James MacLeanb0a37b7b2023-11-27 20:58:042771 // this test no longer uses the proxy inheritance mentioned above, because
2772 // sandboxed and unsandboxed baz.com pages will be in different SiteInstances.
2773 // Restructure the test so it still provides coverage for proxy inheritance
2774 // when IsolateSandboxedIframes is enabled.
alexmos6b294562015-03-05 19:24:102775 GURL baz_child_url(embedded_test_server()->GetURL("baz.com", "/title2.html"));
W. James MacLeane176f342024-01-05 23:49:562776 {
2777 RenderFrameDeletedObserver deleted_observer(
2778 root->child_at(0)->child_at(0)->current_frame_host());
2779 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0)->child_at(0),
2780 baz_child_url));
2781 deleted_observer.WaitUntilDeleted();
2782 }
alexmos6b294562015-03-05 19:24:102783 EXPECT_TRUE(observer.last_navigation_succeeded());
2784 EXPECT_EQ(baz_child_url, observer.last_navigation_url());
2785
W. James MacLeanb0a37b7b2023-11-27 20:58:042786 if (sandboxed_iframes_are_isolated) {
2787 switch (blink::features::kIsolateSandboxedIframesGroupingParam.Get()) {
2788 case blink::features::IsolateSandboxedIframesGrouping::kPerSite:
2789 case blink::features::IsolateSandboxedIframesGrouping::kPerOrigin:
2790 EXPECT_EQ(
2791 " Site A ------------ proxies for B C D\n"
2792 " |--Site B ------- proxies for A C D\n"
2793 " | +--Site D -- proxies for A B C\n"
2794 " +--Site C ------- proxies for A B D\n"
2795 "Where A = http://127.0.0.1/\n"
2796 " B = http://bar.com/ (sandboxed)\n"
2797 " C = http://baz.com/\n"
2798 " D = http://baz.com/ (sandboxed)",
2799 DepictFrameTree(root));
2800 break;
2801 case blink::features::IsolateSandboxedIframesGrouping::kPerDocument:
Alison Gale770f3fc2024-04-27 00:39:582802 // TODO(crbug.com/40941714): Add output for the PerDocument
W. James MacLeanb0a37b7b2023-11-27 20:58:042803 // case, and parameterize this test to run all variants (none, per-site,
2804 // per-origin, per-document).
2805 break;
2806 }
2807 } else {
2808 EXPECT_EQ(
2809 " Site A ------------ proxies for B C\n"
2810 " |--Site B ------- proxies for A C\n"
2811 " | +--Site C -- proxies for A B\n"
2812 " +--Site C ------- proxies for A B\n"
2813 "Where A = http://127.0.0.1/\n"
2814 " B = http://bar.com/\n"
2815 " C = http://baz.com/",
2816 DepictFrameTree(root));
2817 }
creis2069a0a2015-05-22 22:13:472818
alexmos6b294562015-03-05 19:24:102819 // Opening a popup in the child of a sandboxed frame should fail.
Nick Carterb7e71312018-08-03 23:36:132820 EXPECT_EQ(false, EvalJs(root->child_at(0)->child_at(0),
2821 "!!window.open('data:text/html,dataurl');"));
alexmos6b294562015-03-05 19:24:102822 EXPECT_EQ(1u, Shell::windows().size());
alexmos6e940102016-01-19 22:47:252823
2824 // Child of a sandboxed frame should also be sandboxed on the browser side.
Ian Clellandcdc4f312017-10-13 22:24:122825 EXPECT_EQ(
2826 expected_flags,
2827 root->child_at(0)->child_at(0)->effective_frame_policy().sandbox_flags);
alexmos6b294562015-03-05 19:24:102828}
2829
2830// Check that dynamic updates to iframe sandbox flags are propagated correctly.
Fergal Daly2e7e1e12020-06-24 09:18:282831IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmos6b294562015-03-05 19:24:102832 DynamicSandboxFlagsRemoteToLocal) {
2833 GURL main_url(
2834 embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html"));
2835 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2836
2837 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:552838 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos6b294562015-03-05 19:24:102839
2840 TestNavigationObserver observer(shell()->web_contents());
2841 ASSERT_EQ(2U, root->child_count());
2842
2843 // Make sure the two frames starts out at correct URLs.
2844 EXPECT_EQ(embedded_test_server()->GetURL("bar.com", "/title1.html"),
2845 root->child_at(0)->current_url());
2846 EXPECT_EQ(embedded_test_server()->GetURL("/title1.html"),
2847 root->child_at(1)->current_url());
2848
2849 // Update the second frame's sandbox flags.
Avi Drissmanc91bd8e2021-04-19 23:58:442850 EXPECT_TRUE(
2851 ExecJs(shell(),
2852 "document.querySelectorAll('iframe')[1].sandbox='allow-scripts'"));
alexmos6b294562015-03-05 19:24:102853
2854 // Check that the current sandbox flags are updated but the effective
2855 // sandbox flags are not.
arthursonzognib93a4472020-04-10 07:38:002856 network::mojom::WebSandboxFlags expected_flags =
2857 network::mojom::WebSandboxFlags::kAll &
2858 ~network::mojom::WebSandboxFlags::kScripts &
2859 ~network::mojom::WebSandboxFlags::kAutomaticFeatures;
Ian Clellandcdc4f312017-10-13 22:24:122860 EXPECT_EQ(expected_flags,
2861 root->child_at(1)->pending_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:002862 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:122863 root->child_at(1)->effective_frame_policy().sandbox_flags);
alexmos6b294562015-03-05 19:24:102864
2865 // Navigate the second subframe to a page on bar.com. This will trigger a
nasko8206fa12016-03-22 02:24:132866 // remote-to-local frame swap in bar.com's process.
alexmos6b294562015-03-05 19:24:102867 GURL bar_url(embedded_test_server()->GetURL(
2868 "bar.com", "/frame_tree/page_with_one_frame.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:202869 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(1), bar_url));
alexmos6b294562015-03-05 19:24:102870 EXPECT_EQ(bar_url, root->child_at(1)->current_url());
2871 ASSERT_EQ(1U, root->child_at(1)->child_count());
2872
2873 // Confirm that the browser process has updated the current sandbox flags.
Ian Clellandcdc4f312017-10-13 22:24:122874 EXPECT_EQ(expected_flags,
2875 root->child_at(1)->pending_frame_policy().sandbox_flags);
2876 EXPECT_EQ(expected_flags,
2877 root->child_at(1)->effective_frame_policy().sandbox_flags);
alexmos6b294562015-03-05 19:24:102878
2879 // Opening a popup in the sandboxed second frame should fail.
Nick Carterb7e71312018-08-03 23:36:132880 EXPECT_EQ(false, EvalJs(root->child_at(1),
2881 "!!window.open('data:text/html,dataurl');"));
alexmos6b294562015-03-05 19:24:102882 EXPECT_EQ(1u, Shell::windows().size());
2883
2884 // Make sure that the child frame inherits the sandbox flags of its
2885 // now-sandboxed parent frame.
Nick Carterb7e71312018-08-03 23:36:132886 EXPECT_EQ(false, EvalJs(root->child_at(1)->child_at(0),
2887 "!!window.open('data:text/html,dataurl');"));
alexmos6b294562015-03-05 19:24:102888 EXPECT_EQ(1u, Shell::windows().size());
2889}
2890
2891// Check that dynamic updates to iframe sandbox flags are propagated correctly.
Fergal Daly2e7e1e12020-06-24 09:18:282892IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmos6b294562015-03-05 19:24:102893 DynamicSandboxFlagsRendererInitiatedNavigation) {
2894 GURL main_url(
2895 embedded_test_server()->GetURL("/frame_tree/page_with_one_frame.html"));
2896 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2897
2898 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:552899 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos6b294562015-03-05 19:24:102900
2901 TestNavigationObserver observer(shell()->web_contents());
2902 ASSERT_EQ(1U, root->child_count());
2903
2904 // Make sure the frame starts out at the correct cross-site page.
2905 EXPECT_EQ(embedded_test_server()->GetURL("baz.com", "/title1.html"),
2906 root->child_at(0)->current_url());
2907
2908 // The frame should not be sandboxed to start with.
arthursonzognib93a4472020-04-10 07:38:002909 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:122910 root->child_at(0)->pending_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:002911 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:122912 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmos6b294562015-03-05 19:24:102913
2914 // Dynamically update the frame's sandbox flags.
Avi Drissmanc91bd8e2021-04-19 23:58:442915 EXPECT_TRUE(ExecJs(
nickadef4a52016-06-09 18:45:542916 shell(), "document.querySelector('iframe').sandbox='allow-scripts';"));
alexmos6b294562015-03-05 19:24:102917
2918 // Check that updated sandbox flags are propagated to browser process.
Ian Clellandcdc4f312017-10-13 22:24:122919 // The new flags should be set in pending_frame_policy().sandbox_flags, while
2920 // effective_frame_policy().sandbox_flags should still reflect the old flags,
2921 // because sandbox flag updates take place only after navigations.
2922 // "allow-scripts" resets both SandboxFlags::Scripts and
2923 // SandboxFlags::AutomaticFeatures bits per blink::parseSandboxPolicy().
arthursonzognib93a4472020-04-10 07:38:002924 network::mojom::WebSandboxFlags expected_flags =
2925 network::mojom::WebSandboxFlags::kAll &
2926 ~network::mojom::WebSandboxFlags::kScripts &
2927 ~network::mojom::WebSandboxFlags::kAutomaticFeatures;
Ian Clellandcdc4f312017-10-13 22:24:122928 EXPECT_EQ(expected_flags,
2929 root->child_at(0)->pending_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:002930 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:122931 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmos6b294562015-03-05 19:24:102932
2933 // Perform a renderer-initiated same-site navigation in the first frame. The
2934 // new sandbox flags should take effect.
2935 TestFrameNavigationObserver frame_observer(root->child_at(0));
Avi Drissmanc91bd8e2021-04-19 23:58:442936 ASSERT_TRUE(ExecJs(root->child_at(0), "window.location.href='/title2.html'"));
alexmos6b294562015-03-05 19:24:102937 frame_observer.Wait();
2938 EXPECT_EQ(embedded_test_server()->GetURL("baz.com", "/title2.html"),
2939 root->child_at(0)->current_url());
2940
2941 // Confirm that the browser process has updated the frame's current sandbox
2942 // flags.
Ian Clellandcdc4f312017-10-13 22:24:122943 EXPECT_EQ(expected_flags,
2944 root->child_at(0)->pending_frame_policy().sandbox_flags);
2945 EXPECT_EQ(expected_flags,
2946 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmos6b294562015-03-05 19:24:102947
2948 // Opening a popup in the now-sandboxed frame should fail.
Nick Carterb7e71312018-08-03 23:36:132949 EXPECT_EQ(false, EvalJs(root->child_at(0),
2950 "!!window.open('data:text/html,dataurl');"));
alexmos6b294562015-03-05 19:24:102951 EXPECT_EQ(1u, Shell::windows().size());
alexmosf832a2f2015-01-27 22:44:032952}
2953
alexmos6e0ee0c2015-05-01 18:57:342954// Verify that when a new child frame is added, the proxies created for it in
2955// other SiteInstances have correct sandbox flags and origin.
2956//
2957// A A A
2958// / / \ / \ .
2959// B -> B A -> B A
2960// \ .
2961// B
2962//
2963// The test checks sandbox flags and origin for the proxy added in step 2, by
2964// checking whether the grandchild frame added in step 3 sees proper sandbox
2965// flags and origin for its (remote) parent. This wasn't addressed when
2966// https://crbug.com/423587 was fixed.
Alex Moshchuk3e4776082020-11-05 00:53:522967IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
2968 ProxiesForNewChildFramesHaveCorrectReplicationState) {
alexmos6e0ee0c2015-05-01 18:57:342969 GURL main_url(
2970 embedded_test_server()->GetURL("/frame_tree/page_with_one_frame.html"));
2971 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2972
2973 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:552974 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos6e0ee0c2015-05-01 18:57:342975
2976 EXPECT_EQ(
2977 " Site A ------------ proxies for B\n"
2978 " +--Site B ------- proxies for A\n"
2979 "Where A = http://127.0.0.1/\n"
2980 " B = http://baz.com/",
2981 DepictFrameTree(root));
2982
2983 // In the root frame, add a new sandboxed local frame, which itself has a
2984 // child frame on baz.com. Wait for three RenderFrameHosts to be created:
2985 // the new sandboxed local frame, its child (while it's still local), and a
Alex Moshchuk3e4776082020-11-05 00:53:522986 // speculative RFH when starting the cross-site navigation to baz.com.
alexmos6e0ee0c2015-05-01 18:57:342987 RenderFrameHostCreatedObserver frame_observer(shell()->web_contents(), 3);
Avi Drissmanc91bd8e2021-04-19 23:58:442988 EXPECT_TRUE(ExecJs(root,
2989 "addFrame('/frame_tree/page_with_one_frame.html',"
2990 " 'allow-scripts allow-same-origin')"));
alexmos6e0ee0c2015-05-01 18:57:342991 frame_observer.Wait();
2992
2993 // Wait for the cross-site navigation to baz.com in the grandchild to finish.
Alex Moshchuk3e4776082020-11-05 00:53:522994 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
alexmos6e0ee0c2015-05-01 18:57:342995 FrameTreeNode* bottom_child = root->child_at(1)->child_at(0);
Alex Moshchuk3e4776082020-11-05 00:53:522996 EXPECT_EQ(embedded_test_server()->GetURL("baz.com", "/title1.html"),
2997 bottom_child->current_url());
alexmos6e0ee0c2015-05-01 18:57:342998
2999 EXPECT_EQ(
3000 " Site A ------------ proxies for B\n"
3001 " |--Site B ------- proxies for A\n"
3002 " +--Site A ------- proxies for B\n"
3003 " +--Site B -- proxies for A\n"
3004 "Where A = http://127.0.0.1/\n"
3005 " B = http://baz.com/",
3006 DepictFrameTree(root));
3007
3008 // Use location.ancestorOrigins to check that the grandchild on baz.com sees
Alex Moshchuk3e4776082020-11-05 00:53:523009 // correct origin for its parent and grandparent, which are at the same URL
3010 // and origin (namely, page_with_one_frame.html on the server's default
3011 // origin).
3012 EXPECT_EQ(
3013 ListValueOf(url::Origin::Create(main_url), url::Origin::Create(main_url)),
3014 EvalJs(bottom_child, "Array.from(location.ancestorOrigins);"));
alexmos6e0ee0c2015-05-01 18:57:343015
3016 // Check that the sandbox flags in the browser process are correct.
arthursonzognib93a4472020-04-10 07:38:003017 // "allow-scripts" resets both network::mojom::WebSandboxFlags::Scripts and
3018 // network::mojom::WebSandboxFlags::AutomaticFeatures bits per
3019 // blink::parseSandboxPolicy().
3020 network::mojom::WebSandboxFlags expected_flags =
3021 network::mojom::WebSandboxFlags::kAll &
3022 ~network::mojom::WebSandboxFlags::kScripts &
3023 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
3024 ~network::mojom::WebSandboxFlags::kOrigin;
Ian Clellandcdc4f312017-10-13 22:24:123025 EXPECT_EQ(expected_flags,
3026 root->child_at(1)->effective_frame_policy().sandbox_flags);
alexmos6e0ee0c2015-05-01 18:57:343027
3028 // The child of the sandboxed frame should've inherited sandbox flags, so it
3029 // should not be able to create popups.
Ian Clellandcdc4f312017-10-13 22:24:123030 EXPECT_EQ(expected_flags,
3031 bottom_child->effective_frame_policy().sandbox_flags);
Nick Carterb7e71312018-08-03 23:36:133032 EXPECT_EQ(false,
3033 EvalJs(bottom_child, "!!window.open('data:text/html,dataurl')"));
alexmos6e0ee0c2015-05-01 18:57:343034 EXPECT_EQ(1u, Shell::windows().size());
3035}
3036
alexmos998581d2015-01-22 01:01:593037// Verify that a child frame can retrieve the name property set by its parent.
Fergal Daly2e7e1e12020-06-24 09:18:283038IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, WindowNameReplication) {
alexmos998581d2015-01-22 01:01:593039 GURL main_url(embedded_test_server()->GetURL("/frame_tree/2-4.html"));
3040 EXPECT_TRUE(NavigateToURL(shell(), main_url));
3041
3042 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:553043 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos998581d2015-01-22 01:01:593044
clamyf1ccb4d2015-01-28 17:40:383045 TestNavigationObserver observer(shell()->web_contents());
alexmos998581d2015-01-22 01:01:593046
3047 // Load cross-site page into iframe.
3048 GURL frame_url =
3049 embedded_test_server()->GetURL("foo.com", "/frame_tree/3-1.html");
Lukasz Anforowicz69c25dfd2020-11-12 21:50:203050 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url));
clamyf1ccb4d2015-01-28 17:40:383051 EXPECT_TRUE(observer.last_navigation_succeeded());
3052 EXPECT_EQ(frame_url, observer.last_navigation_url());
alexmos998581d2015-01-22 01:01:593053
3054 // Ensure that a new process is created for the subframe.
3055 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
3056 root->child_at(0)->current_frame_host()->GetSiteInstance());
3057
3058 // Check that the window.name seen by the frame matches the name attribute
3059 // specified by its parent in the iframe tag.
Nick Carterb7e71312018-08-03 23:36:133060 EXPECT_EQ("3-1-name", EvalJs(root->child_at(0), "window.name;"));
alexmos998581d2015-01-22 01:01:593061}
3062
alexmosbe2f4c32015-03-10 02:30:233063// Verify that dynamic updates to a frame's window.name propagate to the
3064// frame's proxies, so that the latest frame names can be used in navigations.
Fergal Daly2e7e1e12020-06-24 09:18:283065IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, DynamicWindowName) {
alexmosbe2f4c32015-03-10 02:30:233066 GURL main_url(embedded_test_server()->GetURL("/frame_tree/2-4.html"));
3067 EXPECT_TRUE(NavigateToURL(shell(), main_url));
3068
3069 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:553070 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmosbe2f4c32015-03-10 02:30:233071 TestNavigationObserver observer(shell()->web_contents());
3072
3073 // Load cross-site page into iframe.
3074 GURL frame_url =
3075 embedded_test_server()->GetURL("foo.com", "/frame_tree/3-1.html");
Lukasz Anforowicz69c25dfd2020-11-12 21:50:203076 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url));
alexmosbe2f4c32015-03-10 02:30:233077 EXPECT_TRUE(observer.last_navigation_succeeded());
3078 EXPECT_EQ(frame_url, observer.last_navigation_url());
3079
3080 // Browser process should know the child frame's original window.name
3081 // specified in the iframe element.
3082 EXPECT_EQ(root->child_at(0)->frame_name(), "3-1-name");
3083
3084 // Update the child frame's window.name.
Avi Drissmanc91bd8e2021-04-19 23:58:443085 EXPECT_TRUE(ExecJs(root->child_at(0), "window.name = 'updated-name';"));
alexmosbe2f4c32015-03-10 02:30:233086
3087 // The change should propagate to the browser process.
3088 EXPECT_EQ(root->child_at(0)->frame_name(), "updated-name");
3089
3090 // The proxy in the parent process should also receive the updated name.
yukishiinoc9d8a85a2017-01-26 09:55:573091 // Now iframe's name and the content window's name differ, so it shouldn't
3092 // be possible to access to the content window with the updated name.
Yuki Shiinoc82258442021-03-11 03:02:053093 EXPECT_EQ(true, EvalJs(shell(), "frames['updated-name'] === undefined;"));
yukishiinoc9d8a85a2017-01-26 09:55:573094 // Change iframe's name to match the content window's name so that it can
3095 // reference the child frame by its new name in case of cross origin.
Avi Drissmanc91bd8e2021-04-19 23:58:443096 EXPECT_TRUE(ExecJs(root, "window['3-1-id'].name = 'updated-name';"));
Nick Carterb7e71312018-08-03 23:36:133097 EXPECT_EQ(true, EvalJs(shell(), "frames['updated-name'] == frames[0];"));
alexmosbe2f4c32015-03-10 02:30:233098
3099 // Issue a renderer-initiated navigation from the root frame to the child
3100 // frame using the frame's name. Make sure correct frame is navigated.
3101 //
3102 // TODO(alexmos): When blink::createWindow is refactored to handle
3103 // RemoteFrames, this should also be tested via window.open(url, frame_name)
3104 // and a more complicated frame hierarchy (https://crbug.com/463742)
3105 TestFrameNavigationObserver frame_observer(root->child_at(0));
3106 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
Avi Drissmanc91bd8e2021-04-19 23:58:443107 EXPECT_TRUE(
3108 ExecJs(shell(),
3109 JsReplace("frames['updated-name'].location.href = $1", foo_url)));
alexmosbe2f4c32015-03-10 02:30:233110 frame_observer.Wait();
3111 EXPECT_EQ(foo_url, root->child_at(0)->current_url());
3112}
3113
alexmosa7a4ff822015-04-27 17:59:563114// Verify that when a frame is navigated to a new origin, the origin update
3115// propagates to the frame's proxies.
Fergal Daly2e7e1e12020-06-24 09:18:283116IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, OriginUpdatesReachProxies) {
alexmosa7a4ff822015-04-27 17:59:563117 GURL main_url(
3118 embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html"));
3119 EXPECT_TRUE(NavigateToURL(shell(), main_url));
3120
3121 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:553122 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmosa7a4ff822015-04-27 17:59:563123 TestNavigationObserver observer(shell()->web_contents());
3124
3125 EXPECT_EQ(
3126 " Site A ------------ proxies for B\n"
3127 " |--Site B ------- proxies for A\n"
3128 " +--Site A ------- proxies for B\n"
3129 "Where A = http://127.0.0.1/\n"
3130 " B = http://bar.com/",
3131 DepictFrameTree(root));
3132
3133 // Navigate second subframe to a baz.com. This should send an origin update
3134 // to the frame's proxy in the bar.com (first frame's) process.
3135 GURL frame_url = embedded_test_server()->GetURL("baz.com", "/title2.html");
Lukasz Anforowicz69c25dfd2020-11-12 21:50:203136 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(1), frame_url));
alexmosa7a4ff822015-04-27 17:59:563137 EXPECT_TRUE(observer.last_navigation_succeeded());
3138 EXPECT_EQ(frame_url, observer.last_navigation_url());
3139
3140 // The first frame can't directly observe the second frame's origin with
3141 // JavaScript. Instead, try to navigate the second frame from the first
3142 // frame. This should fail with a console error message, which should
3143 // contain the second frame's updated origin (see blink::Frame::canNavigate).
Devlin Cronined376d82020-05-01 18:37:553144 WebContentsConsoleObserver console_observer(shell()->web_contents());
arthursonzogni5ae0ff822020-12-09 10:14:463145 console_observer.SetPattern("Unsafe attempt to initiate navigation*");
alexmosa7a4ff822015-04-27 17:59:563146
3147 // frames[1] can't be used due to a bug where RemoteFrames are created out of
3148 // order (https://crbug.com/478792). Instead, target second frame by name.
Avi Drissmanc91bd8e2021-04-19 23:58:443149 EXPECT_TRUE(ExecJs(root->child_at(0),
3150 "try { parent.frames['frame2'].location.href = "
3151 "'data:text/html,foo'; } catch (e) {}"));
Fergal Daly7723f9d2022-10-29 07:03:133152 ASSERT_TRUE(console_observer.Wait());
alexmosa7a4ff822015-04-27 17:59:563153
creisabdd2bd62015-11-21 01:14:583154 std::string frame_origin = root->child_at(1)->current_origin().Serialize();
Mike West800532c2021-10-14 09:26:523155 EXPECT_EQ(frame_origin + "/", frame_url.DeprecatedGetOriginAsURL().spec());
Devlin Cronined376d82020-05-01 18:37:553156 EXPECT_TRUE(base::MatchPattern(console_observer.GetMessageAt(0u),
3157 "*" + frame_origin + "*"))
alexmosa7a4ff822015-04-27 17:59:563158 << "Error message does not contain the frame's latest origin ("
3159 << frame_origin << ")";
3160}
3161
nasko3e8c20e2014-12-18 06:54:563162// Ensure that navigating subframes in --site-per-process mode properly fires
3163// the DidStopLoading event on WebContentsObserver.
Fergal Daly2e7e1e12020-06-24 09:18:283164IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, CrossSiteDidStopLoading) {
nasko3e8c20e2014-12-18 06:54:563165 GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
davidsac6e6c35e42016-11-21 19:45:573166 EXPECT_TRUE(NavigateToURL(shell(), main_url));
nasko3e8c20e2014-12-18 06:54:563167
3168 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:553169 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
nasko3e8c20e2014-12-18 06:54:563170
clamyf1ccb4d2015-01-28 17:40:383171 TestNavigationObserver observer(shell()->web_contents());
nasko3e8c20e2014-12-18 06:54:563172
3173 // Load same-site page into iframe.
3174 FrameTreeNode* child = root->child_at(0);
3175 GURL http_url(embedded_test_server()->GetURL("/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:203176 EXPECT_TRUE(NavigateToURLFromRenderer(child, http_url));
clamyf1ccb4d2015-01-28 17:40:383177 EXPECT_EQ(http_url, observer.last_navigation_url());
3178 EXPECT_TRUE(observer.last_navigation_succeeded());
nasko3e8c20e2014-12-18 06:54:563179
3180 // Load cross-site page into iframe.
3181 TestNavigationObserver nav_observer(shell()->web_contents(), 1);
3182 GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html");
3183 NavigationController::LoadURLParams params(url);
3184 params.transition_type = ui::PAGE_TRANSITION_LINK;
3185 params.frame_tree_node_id = child->frame_tree_node_id();
Carlos Caballero04aab362021-02-15 17:38:163186 child->navigator().controller().LoadURLWithParams(params);
nasko3e8c20e2014-12-18 06:54:563187 nav_observer.Wait();
3188
3189 // Verify that the navigation succeeded and the expected URL was loaded.
clamyf1ccb4d2015-01-28 17:40:383190 EXPECT_TRUE(observer.last_navigation_succeeded());
3191 EXPECT_EQ(url, observer.last_navigation_url());
nasko3e8c20e2014-12-18 06:54:563192}
3193
creis0040d342015-02-19 01:42:373194// Ensure that the renderer does not crash when navigating a frame that has a
3195// sibling RemoteFrame. See https://crbug.com/426953.
Fergal Daly2e7e1e12020-06-24 09:18:283196IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
creis0040d342015-02-19 01:42:373197 NavigateWithSiblingRemoteFrame) {
3198 GURL main_url(
3199 embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html"));
davidsac6e6c35e42016-11-21 19:45:573200 EXPECT_TRUE(NavigateToURL(shell(), main_url));
creis0040d342015-02-19 01:42:373201
3202 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:553203 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
creis0040d342015-02-19 01:42:373204 TestNavigationObserver observer(shell()->web_contents());
3205
3206 // Make sure the first frame is out of process.
3207 ASSERT_EQ(2U, root->child_count());
3208 FrameTreeNode* node2 = root->child_at(0);
3209 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
3210 node2->current_frame_host()->GetSiteInstance());
3211
3212 // Make sure the second frame is in the parent's process.
3213 FrameTreeNode* node3 = root->child_at(1);
3214 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
3215 node3->current_frame_host()->GetSiteInstance());
3216
3217 // Navigate the second iframe (node3) to a URL in its own process.
3218 GURL title_url = embedded_test_server()->GetURL("/title2.html");
Lukasz Anforowicz69c25dfd2020-11-12 21:50:203219 EXPECT_TRUE(NavigateToURLFromRenderer(node3, title_url));
creis0040d342015-02-19 01:42:373220 EXPECT_TRUE(observer.last_navigation_succeeded());
3221 EXPECT_EQ(title_url, observer.last_navigation_url());
3222 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
3223 node3->current_frame_host()->GetSiteInstance());
3224 EXPECT_TRUE(node3->current_frame_host()->IsRenderFrameLive());
3225}
3226
kenrb77618cd52016-02-18 19:13:383227// Ensure that the renderer does not crash when a local frame with a remote
3228// parent frame is swapped from local to remote, then back to local again.
3229// See https://crbug.com/585654.
Fergal Daly2e7e1e12020-06-24 09:18:283230IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
kenrb77618cd52016-02-18 19:13:383231 NavigateSiblingsToSameProcess) {
3232 GURL main_url(
3233 embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html"));
davidsac6e6c35e42016-11-21 19:45:573234 EXPECT_TRUE(NavigateToURL(shell(), main_url));
kenrb77618cd52016-02-18 19:13:383235
3236 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:553237 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
kenrb77618cd52016-02-18 19:13:383238
3239 FrameTreeNode* node2 = root->child_at(0);
3240 FrameTreeNode* node3 = root->child_at(1);
3241
3242 // Navigate the second iframe to the same process as the first.
3243 GURL frame_url = embedded_test_server()->GetURL("bar.com", "/title1.html");
Lukasz Anforowicz69c25dfd2020-11-12 21:50:203244 EXPECT_TRUE(NavigateToURLFromRenderer(node3, frame_url));
kenrb77618cd52016-02-18 19:13:383245
3246 // Verify that they are in the same process.
3247 EXPECT_EQ(node2->current_frame_host()->GetSiteInstance(),
3248 node3->current_frame_host()->GetSiteInstance());
3249 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
3250 node3->current_frame_host()->GetSiteInstance());
3251
3252 // Navigate the first iframe into its parent's process.
3253 GURL title_url = embedded_test_server()->GetURL("/title2.html");
Lukasz Anforowicz69c25dfd2020-11-12 21:50:203254 EXPECT_TRUE(NavigateToURLFromRenderer(node2, title_url));
kenrb77618cd52016-02-18 19:13:383255 EXPECT_NE(node2->current_frame_host()->GetSiteInstance(),
3256 node3->current_frame_host()->GetSiteInstance());
3257
3258 // Return the first iframe to the same process as its sibling, and ensure
3259 // that it does not crash.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:203260 EXPECT_TRUE(NavigateToURLFromRenderer(node2, frame_url));
kenrb77618cd52016-02-18 19:13:383261 EXPECT_EQ(node2->current_frame_host()->GetSiteInstance(),
3262 node3->current_frame_host()->GetSiteInstance());
3263 EXPECT_TRUE(node2->current_frame_host()->IsRenderFrameLive());
3264}
3265
alexmosf40ce5b02015-02-25 20:19:563266// Verify that load events for iframe elements work when the child frame is
3267// out-of-process. In such cases, the load event is forwarded from the child
3268// frame to the parent frame via the browser process.
Fergal Daly2e7e1e12020-06-24 09:18:283269IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, LoadEventForwarding) {
alexmosf40ce5b02015-02-25 20:19:563270 // Load a page with a cross-site frame. The parent page has an onload
3271 // handler in the iframe element that appends "LOADED" to the document title.
3272 {
3273 GURL main_url(
3274 embedded_test_server()->GetURL("/frame_with_load_event.html"));
Jan Wilken Dörrie2c470ea2021-03-22 22:26:243275 std::u16string expected_title(u"LOADED");
alexmosf40ce5b02015-02-25 20:19:563276 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
3277 EXPECT_TRUE(NavigateToURL(shell(), main_url));
3278 EXPECT_EQ(title_watcher.WaitAndGetTitle(), expected_title);
3279 }
3280
3281 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:553282 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmosf40ce5b02015-02-25 20:19:563283
3284 // Load another cross-site page into the iframe and check that the load event
3285 // is fired.
3286 {
3287 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
Jan Wilken Dörrie2c470ea2021-03-22 22:26:243288 std::u16string expected_title(u"LOADEDLOADED");
alexmosf40ce5b02015-02-25 20:19:563289 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
3290 TestNavigationObserver observer(shell()->web_contents());
Lukasz Anforowicz69c25dfd2020-11-12 21:50:203291 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), foo_url));
alexmosf40ce5b02015-02-25 20:19:563292 EXPECT_TRUE(observer.last_navigation_succeeded());
3293 EXPECT_EQ(foo_url, observer.last_navigation_url());
3294 EXPECT_EQ(title_watcher.WaitAndGetTitle(), expected_title);
3295 }
3296}
3297
alexmose7da5a12015-04-09 02:22:163298// Check that postMessage can be routed between cross-site iframes.
Fergal Daly2e7e1e12020-06-24 09:18:283299IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, SubframePostMessage) {
alexmose7da5a12015-04-09 02:22:163300 GURL main_url(embedded_test_server()->GetURL(
3301 "/frame_tree/page_with_post_message_frames.html"));
3302 EXPECT_TRUE(NavigateToURL(shell(), main_url));
3303
3304 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:553305 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmose7da5a12015-04-09 02:22:163306
alexmose7da5a12015-04-09 02:22:163307 ASSERT_EQ(2U, root->child_count());
3308
3309 // Verify the frames start at correct URLs. First frame should be
3310 // same-site; second frame should be cross-site.
3311 GURL same_site_url(embedded_test_server()->GetURL("/post_message.html"));
3312 EXPECT_EQ(same_site_url, root->child_at(0)->current_url());
Ria Jiangabad8d9a2018-01-24 16:52:363313 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/post_message.html"));
alexmose7da5a12015-04-09 02:22:163314 EXPECT_EQ(foo_url, root->child_at(1)->current_url());
3315 EXPECT_NE(root->child_at(0)->current_frame_host()->GetSiteInstance(),
3316 root->child_at(1)->current_frame_host()->GetSiteInstance());
3317
3318 // Send a message from first, same-site frame to second, cross-site frame.
3319 // Expect the second frame to reply back to the first frame.
alexmos9f8705a2015-05-06 19:58:593320 PostMessageAndWaitForReply(root->child_at(0),
3321 "postToSibling('subframe-msg','subframe2')",
3322 "\"done-subframe1\"");
alexmose7da5a12015-04-09 02:22:163323
3324 // Send a postMessage from second, cross-site frame to its parent. Expect
3325 // parent to send a reply to the frame.
Jan Wilken Dörrie8aeb5742021-03-23 19:27:023326 std::u16string expected_title(u"subframe-msg");
alexmose7da5a12015-04-09 02:22:163327 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
alexmos9f8705a2015-05-06 19:58:593328 PostMessageAndWaitForReply(root->child_at(1), "postToParent('subframe-msg')",
3329 "\"done-subframe2\"");
alexmose7da5a12015-04-09 02:22:163330 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
3331
alexmose7da5a12015-04-09 02:22:163332 // Verify the total number of received messages for each subframe. First
alexmos58729042015-06-18 23:20:003333 // frame should have one message (reply from second frame). Second frame
alexmose7da5a12015-04-09 02:22:163334 // should have two messages (message from first frame and reply from parent).
alexmos58729042015-06-18 23:20:003335 // Parent should have one message (from second frame).
3336 EXPECT_EQ(1, GetReceivedMessages(root->child_at(0)));
3337 EXPECT_EQ(2, GetReceivedMessages(root->child_at(1)));
3338 EXPECT_EQ(1, GetReceivedMessages(root));
3339}
3340
3341// Check that postMessage can be sent from a subframe on a cross-process opener
3342// tab, and that its event.source points to a valid proxy.
Fergal Daly2e7e1e12020-06-24 09:18:283343IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmos17e57532015-08-15 02:54:363344 PostMessageWithSubframeOnOpenerChain) {
alexmos58729042015-06-18 23:20:003345 GURL main_url(embedded_test_server()->GetURL(
3346 "a.com", "/frame_tree/page_with_post_message_frames.html"));
3347 EXPECT_TRUE(NavigateToURL(shell(), main_url));
3348
3349 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:553350 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos58729042015-06-18 23:20:003351
3352 ASSERT_EQ(2U, root->child_count());
3353
3354 // Verify the initial state of the world. First frame should be same-site;
3355 // second frame should be cross-site.
3356 EXPECT_EQ(
3357 " Site A ------------ proxies for B\n"
3358 " |--Site A ------- proxies for B\n"
3359 " +--Site B ------- proxies for A\n"
3360 "Where A = http://a.com/\n"
3361 " B = http://foo.com/",
3362 DepictFrameTree(root));
3363
3364 // Open a popup from the first subframe (so that popup's window.opener points
3365 // to the subframe) and navigate it to bar.com.
3366 ShellAddedObserver new_shell_observer;
Avi Drissmanc91bd8e2021-04-19 23:58:443367 EXPECT_TRUE(ExecJs(root->child_at(0), "openPopup('about:blank');"));
alexmos58729042015-06-18 23:20:003368 Shell* popup = new_shell_observer.GetShell();
3369 GURL popup_url(
3370 embedded_test_server()->GetURL("bar.com", "/post_message.html"));
Alex Moshchuk7e26eca2018-03-03 01:34:293371 EXPECT_TRUE(NavigateToURLFromRenderer(popup, popup_url));
alexmos58729042015-06-18 23:20:003372
3373 // From the popup, open another popup for baz.com. This will be used to
3374 // check that the whole opener chain is processed when creating proxies and
3375 // not just an immediate opener.
3376 ShellAddedObserver new_shell_observer2;
Avi Drissmanc91bd8e2021-04-19 23:58:443377 EXPECT_TRUE(ExecJs(popup, "openPopup('about:blank');"));
alexmos58729042015-06-18 23:20:003378 Shell* popup2 = new_shell_observer2.GetShell();
3379 GURL popup2_url(
3380 embedded_test_server()->GetURL("baz.com", "/post_message.html"));
Alex Moshchuk7e26eca2018-03-03 01:34:293381 EXPECT_TRUE(NavigateToURLFromRenderer(popup2, popup2_url));
alexmos58729042015-06-18 23:20:003382
3383 // Ensure that we've created proxies for SiteInstances of both popups (C, D)
3384 // in the main window's frame tree.
3385 EXPECT_EQ(
3386 " Site A ------------ proxies for B C D\n"
3387 " |--Site A ------- proxies for B C D\n"
3388 " +--Site B ------- proxies for A C D\n"
3389 "Where A = http://a.com/\n"
3390 " B = http://foo.com/\n"
3391 " C = http://bar.com/\n"
3392 " D = http://baz.com/",
3393 DepictFrameTree(root));
3394
3395 // Check the first popup's frame tree as well. Note that it doesn't have a
3396 // proxy for foo.com, since foo.com can't reach the popup. It does have a
3397 // proxy for its opener a.com (which can reach it via the window.open
3398 // reference) and second popup (which can reach it via window.opener).
3399 FrameTreeNode* popup_root =
3400 static_cast<WebContentsImpl*>(popup->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:553401 ->GetPrimaryFrameTree()
3402 .root();
alexmos58729042015-06-18 23:20:003403 EXPECT_EQ(
3404 " Site C ------------ proxies for A D\n"
3405 "Where A = http://a.com/\n"
3406 " C = http://bar.com/\n"
3407 " D = http://baz.com/",
3408 DepictFrameTree(popup_root));
3409
3410 // Send a message from first subframe on main page to the first popup and
3411 // wait for a reply back. The reply verifies that the proxy for the opener
3412 // tab's subframe is targeted properly.
3413 PostMessageAndWaitForReply(root->child_at(0), "postToPopup('subframe-msg')",
3414 "\"done-subframe1\"");
3415
alexmos5ac402d2015-07-09 07:51:103416 // Send a postMessage from the popup to window.opener and ensure that it
3417 // reaches subframe1. This verifies that the subframe opener information
3418 // propagated to the popup's RenderFrame. Wait for subframe1 to send a reply
3419 // message to the popup.
Avi Drissmanc91bd8e2021-04-19 23:58:443420 EXPECT_TRUE(ExecJs(popup, "window.name = 'popup';"));
alexmos5ac402d2015-07-09 07:51:103421 PostMessageAndWaitForReply(popup_root, "postToOpener('subframe-msg', '*')",
3422 "\"done-popup\"");
alexmos58729042015-06-18 23:20:003423
alexmos5ac402d2015-07-09 07:51:103424 // Second a postMessage from popup2 to window.opener.opener, which should
3425 // resolve to subframe1. This tests opener chains of length greater than 1.
3426 // As before, subframe1 will send a reply to popup2.
3427 FrameTreeNode* popup2_root =
3428 static_cast<WebContentsImpl*>(popup2->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:553429 ->GetPrimaryFrameTree()
3430 .root();
Avi Drissmanc91bd8e2021-04-19 23:58:443431 EXPECT_TRUE(ExecJs(popup2, "window.name = 'popup2';"));
alexmos5ac402d2015-07-09 07:51:103432 PostMessageAndWaitForReply(popup2_root,
3433 "postToOpenerOfOpener('subframe-msg', '*')",
3434 "\"done-popup2\"");
3435
3436 // Verify the total number of received messages for each subframe:
3437 // - 3 for first subframe (two from first popup, one from second popup)
3438 // - 2 for popup (both from first subframe)
3439 // - 1 for popup2 (reply from first subframe)
3440 // - 0 for other frames
alexmos58729042015-06-18 23:20:003441 EXPECT_EQ(0, GetReceivedMessages(root));
alexmos5ac402d2015-07-09 07:51:103442 EXPECT_EQ(3, GetReceivedMessages(root->child_at(0)));
alexmos58729042015-06-18 23:20:003443 EXPECT_EQ(0, GetReceivedMessages(root->child_at(1)));
alexmos5ac402d2015-07-09 07:51:103444 EXPECT_EQ(2, GetReceivedMessages(popup_root));
3445 EXPECT_EQ(1, GetReceivedMessages(popup2_root));
alexmose7da5a12015-04-09 02:22:163446}
3447
Alex Moshchuk20acd532024-11-07 14:57:453448// Check that in certain situations, postMessage has to create proxies on demand
3449// so that event.source may be used to reply. In particular, this test starts on
3450// A(B) and opens C(D) from A. At this point, D can reach B (via
3451// parent.opener.frames[0], but B cannot see D. However, if D sends a
3452// postMessage to B, B now gains a reference to D through event.source, and
3453// therefore the message should create a proxy for D in B's process. See
3454// https://crbug.com/40261772.
3455IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
3456 PostMessageCreatesProxyOnDemand) {
3457 // Start on A(B).
3458 GURL opener_url(embedded_test_server()->GetURL(
3459 "a.com", "/cross_site_iframe_factory.html?a(b)"));
3460 EXPECT_TRUE(NavigateToURL(shell(), opener_url));
3461 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
3462
3463 // From A, open a popup with a C(D) page.
3464 GURL popup_url(embedded_test_server()->GetURL(
3465 "c.com", "/cross_site_iframe_factory.html?c(d)"));
3466 Shell* new_shell = OpenPopup(root, popup_url, "");
3467 EXPECT_TRUE(new_shell);
3468 FrameTreeNode* popup_root =
3469 static_cast<WebContentsImpl*>(new_shell->web_contents())
3470 ->GetPrimaryFrameTree()
3471 .root();
3472 EXPECT_EQ(root, popup_root->opener());
3473
3474 // Verify proxy setup. Both A and B should be visible to all four frames:
3475 // - C can reach A via opener
3476 // - C can reach B via opener.frames[0]
3477 // - D can reach A via parent.opener
3478 // - D can reach B via parent.opener.frames[0]
3479 EXPECT_EQ(
3480 " Site A ------------ proxies for B C D\n"
3481 " +--Site B ------- proxies for A C D\n"
3482 "Where A = http://a.com/\n"
3483 " B = http://b.com/\n"
3484 " C = http://c.com/\n"
3485 " D = http://d.com/",
3486 DepictFrameTree(root));
3487 // C and D are only visible to each other and to A, via A's window.open
3488 // reference. B doesn't have a way to reach them.
3489 EXPECT_EQ(
3490 " Site C ------------ proxies for A D\n"
3491 " +--Site D ------- proxies for A C\n"
3492 "Where A = http://a.com/\n"
3493 " C = http://c.com/\n"
3494 " D = http://d.com/",
3495 DepictFrameTree(popup_root));
3496
3497 // Install a postMessage handler in B that echoes back event.data with
3498 // "-reply" appended to it. This requires having a valid event.source.
3499 EXPECT_TRUE(ExecJs(root->child_at(0)->current_frame_host(),
3500 "window.addEventListener('message', function(event) {\n"
3501 " event.source.postMessage(event.data + '-reply', '*');\n"
3502 "});"));
3503
3504 // Send a message from D to B and wait for a response. While processing the
3505 // message, the browser process should create a proxy for D in B, which B will
3506 // use when replying.
3507 EXPECT_TRUE(
3508 ExecJs(popup_root->child_at(0), WaitForMessageScript("event.data")));
3509 EXPECT_TRUE(ExecJs(popup_root->child_at(0),
3510 "parent.opener.frames[0].postMessage('popup-ping', '*')"));
3511 EXPECT_EQ("popup-ping-reply",
3512 EvalJs(popup_root->child_at(0), "onMessagePromise"));
3513
3514 // Verify the final proxies. The proxies for the opener window shouldn't have
3515 // changed (A and B are still visible to all other frames).
3516 EXPECT_EQ(
3517 " Site A ------------ proxies for B C D\n"
3518 " +--Site B ------- proxies for A C D\n"
3519 "Where A = http://a.com/\n"
3520 " B = http://b.com/\n"
3521 " C = http://c.com/\n"
3522 " D = http://d.com/",
3523 DepictFrameTree(root));
3524 // Frames C and D should now have proxies in site B, since frame B can now
3525 // reach both of them (D via event.source, and C via event.source.top).
3526 EXPECT_EQ(
3527 " Site C ------------ proxies for A B D\n"
3528 " +--Site D ------- proxies for A B C\n"
3529 "Where A = http://a.com/\n"
3530 " B = http://b.com/\n"
3531 " C = http://c.com/\n"
3532 " D = http://d.com/",
3533 DepictFrameTree(popup_root));
3534}
3535
alexmos9f8705a2015-05-06 19:58:593536// Check that parent.frames[num] references correct sibling frames when the
3537// parent is remote. See https://crbug.com/478792.
Fergal Daly2e7e1e12020-06-24 09:18:283538IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, IndexedFrameAccess) {
alexmos9f8705a2015-05-06 19:58:593539 // Start on a page with three same-site subframes.
3540 GURL main_url(
3541 embedded_test_server()->GetURL("a.com", "/frame_tree/top.html"));
3542 EXPECT_TRUE(NavigateToURL(shell(), main_url));
3543
3544 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:553545 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos9f8705a2015-05-06 19:58:593546 ASSERT_EQ(3U, root->child_count());
3547 FrameTreeNode* child0 = root->child_at(0);
3548 FrameTreeNode* child1 = root->child_at(1);
3549 FrameTreeNode* child2 = root->child_at(2);
3550
3551 // Send each of the frames to a different site. Each new renderer will first
3552 // create proxies for the parent and two sibling subframes and then create
3553 // and insert the new RenderFrame into the frame tree.
3554 GURL b_url(embedded_test_server()->GetURL("b.com", "/post_message.html"));
3555 GURL c_url(embedded_test_server()->GetURL("c.com", "/post_message.html"));
3556 GURL d_url(embedded_test_server()->GetURL("d.com", "/post_message.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:203557 EXPECT_TRUE(NavigateToURLFromRenderer(child0, b_url));
3558 EXPECT_TRUE(NavigateToURLFromRenderer(child1, c_url));
3559 EXPECT_TRUE(NavigateToURLFromRenderer(child2, d_url));
alexmos9f8705a2015-05-06 19:58:593560
3561 EXPECT_EQ(
3562 " Site A ------------ proxies for B C D\n"
3563 " |--Site B ------- proxies for A C D\n"
3564 " |--Site C ------- proxies for A B D\n"
3565 " +--Site D ------- proxies for A B C\n"
3566 "Where A = http://a.com/\n"
3567 " B = http://b.com/\n"
3568 " C = http://c.com/\n"
3569 " D = http://d.com/",
3570 DepictFrameTree(root));
3571
3572 // Check that each subframe sees itself at correct index in parent.frames.
Nick Carterb7e71312018-08-03 23:36:133573 EXPECT_EQ(true, EvalJs(child0, "window === parent.frames[0];"));
3574 EXPECT_EQ(true, EvalJs(child1, "window === parent.frames[1];"));
3575 EXPECT_EQ(true, EvalJs(child2, "window === parent.frames[2];"));
alexmos9f8705a2015-05-06 19:58:593576
3577 // Send a postMessage from B to parent.frames[1], which should go to C, and
3578 // wait for reply.
3579 PostMessageAndWaitForReply(child0, "postToSibling('subframe-msg', 1)",
3580 "\"done-1-1-name\"");
3581
3582 // Send a postMessage from C to parent.frames[2], which should go to D, and
3583 // wait for reply.
3584 PostMessageAndWaitForReply(child1, "postToSibling('subframe-msg', 2)",
3585 "\"done-1-2-name\"");
3586
3587 // Verify the total number of received messages for each subframe.
alexmos58729042015-06-18 23:20:003588 EXPECT_EQ(1, GetReceivedMessages(child0));
3589 EXPECT_EQ(2, GetReceivedMessages(child1));
3590 EXPECT_EQ(1, GetReceivedMessages(child2));
alexmos9f8705a2015-05-06 19:58:593591}
3592
Fergal Daly2e7e1e12020-06-24 09:18:283593IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, RFPHDestruction) {
nick59dcb162015-04-09 20:29:013594 GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
davidsac6e6c35e42016-11-21 19:45:573595 EXPECT_TRUE(NavigateToURL(shell(), main_url));
nick59dcb162015-04-09 20:29:013596
3597 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:553598 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
nick59dcb162015-04-09 20:29:013599
3600 TestNavigationObserver observer(shell()->web_contents());
3601
3602 // Load cross-site page into iframe.
3603 FrameTreeNode* child = root->child_at(0);
3604 GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html");
creis29f85682016-11-08 01:52:423605 {
3606 RenderFrameDeletedObserver deleted_observer(child->current_frame_host());
Lukasz Anforowicz69c25dfd2020-11-12 21:50:203607 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), url));
creis29f85682016-11-08 01:52:423608 deleted_observer.WaitUntilDeleted();
3609 }
nick59dcb162015-04-09 20:29:013610 EXPECT_TRUE(observer.last_navigation_succeeded());
3611 EXPECT_EQ(url, observer.last_navigation_url());
nick44bacf32015-04-14 02:06:393612 EXPECT_EQ(
3613 " Site A ------------ proxies for B\n"
3614 " |--Site B ------- proxies for A\n"
3615 " +--Site A ------- proxies for B\n"
3616 " |--Site A -- proxies for B\n"
3617 " +--Site A -- proxies for B\n"
3618 " +--Site A -- proxies for B\n"
3619 "Where A = http://127.0.0.1/\n"
3620 " B = http://foo.com/",
3621 DepictFrameTree(root));
nick59dcb162015-04-09 20:29:013622
3623 // Load another cross-site page.
3624 url = embedded_test_server()->GetURL("bar.com", "/title3.html");
creis29f85682016-11-08 01:52:423625 {
3626 RenderFrameDeletedObserver deleted_observer(child->current_frame_host());
3627 NavigateIframeToURL(shell()->web_contents(), "test", url);
3628 deleted_observer.WaitUntilDeleted();
3629 }
nick59dcb162015-04-09 20:29:013630 EXPECT_TRUE(observer.last_navigation_succeeded());
3631 EXPECT_EQ(url, observer.last_navigation_url());
nick44bacf32015-04-14 02:06:393632 EXPECT_EQ(
3633 " Site A ------------ proxies for C\n"
3634 " |--Site C ------- proxies for A\n"
3635 " +--Site A ------- proxies for C\n"
3636 " |--Site A -- proxies for C\n"
3637 " +--Site A -- proxies for C\n"
3638 " +--Site A -- proxies for C\n"
3639 "Where A = http://127.0.0.1/\n"
3640 " C = http://bar.com/",
3641 DepictFrameTree(root));
nick59dcb162015-04-09 20:29:013642
3643 // Navigate back to the parent's origin.
creis29f85682016-11-08 01:52:423644 {
3645 RenderFrameDeletedObserver deleted_observer(child->current_frame_host());
3646 url = embedded_test_server()->GetURL("/title1.html");
Lukasz Anforowicz69c25dfd2020-11-12 21:50:203647 EXPECT_TRUE(NavigateToURLFromRenderer(child, url));
creis29f85682016-11-08 01:52:423648 // Wait for the old process to exit, to verify that the proxies go away.
3649 deleted_observer.WaitUntilDeleted();
3650 }
nick59dcb162015-04-09 20:29:013651 EXPECT_EQ(url, observer.last_navigation_url());
3652 EXPECT_TRUE(observer.last_navigation_succeeded());
lfgf2d4f912016-05-11 23:18:483653
nick44bacf32015-04-14 02:06:393654 EXPECT_EQ(
3655 " Site A\n"
3656 " |--Site A\n"
3657 " +--Site A\n"
3658 " |--Site A\n"
3659 " +--Site A\n"
3660 " +--Site A\n"
3661 "Where A = http://127.0.0.1/",
3662 DepictFrameTree(root));
nick59dcb162015-04-09 20:29:013663}
3664
Fergal Daly2e7e1e12020-06-24 09:18:283665IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, OpenPopupWithRemoteParent) {
alexmos4cf2aa32015-07-15 23:40:433666 GURL main_url(
3667 embedded_test_server()->GetURL("a.com", "/site_per_process_main.html"));
davidsac6e6c35e42016-11-21 19:45:573668 EXPECT_TRUE(NavigateToURL(shell(), main_url));
alexmos4cf2aa32015-07-15 23:40:433669
3670 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:553671 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos4cf2aa32015-07-15 23:40:433672
3673 // Navigate first child cross-site.
3674 GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:203675 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url));
alexmos4cf2aa32015-07-15 23:40:433676
3677 // Open a popup from the first child.
nickadef4a52016-06-09 18:45:543678 Shell* new_shell =
3679 OpenPopup(root->child_at(0), GURL(url::kAboutBlankURL), "");
alexmos4cf2aa32015-07-15 23:40:433680 EXPECT_TRUE(new_shell);
3681
3682 // Check that the popup's opener is correct on both the browser and renderer
3683 // sides.
3684 FrameTreeNode* popup_root =
3685 static_cast<WebContentsImpl*>(new_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:553686 ->GetPrimaryFrameTree()
3687 .root();
alexmos4cf2aa32015-07-15 23:40:433688 EXPECT_EQ(root->child_at(0), popup_root->opener());
3689
Nick Carterb7e71312018-08-03 23:36:133690 EXPECT_EQ(frame_url.spec(),
3691 EvalJs(popup_root, "window.opener.location.href;"));
alexmos4cf2aa32015-07-15 23:40:433692
3693 // Now try the same with a cross-site popup and make sure it ends up in a new
3694 // process and with a correct opener.
3695 GURL popup_url(embedded_test_server()->GetURL("c.com", "/title2.html"));
nickadef4a52016-06-09 18:45:543696 Shell* cross_site_popup = OpenPopup(root->child_at(0), popup_url, "");
alexmos4cf2aa32015-07-15 23:40:433697 EXPECT_TRUE(cross_site_popup);
3698
3699 FrameTreeNode* cross_site_popup_root =
3700 static_cast<WebContentsImpl*>(cross_site_popup->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:553701 ->GetPrimaryFrameTree()
3702 .root();
alexmos4cf2aa32015-07-15 23:40:433703 EXPECT_EQ(cross_site_popup_root->current_url(), popup_url);
3704
3705 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
3706 cross_site_popup->web_contents()->GetSiteInstance());
3707 EXPECT_NE(root->child_at(0)->current_frame_host()->GetSiteInstance(),
3708 cross_site_popup->web_contents()->GetSiteInstance());
3709
3710 EXPECT_EQ(root->child_at(0), cross_site_popup_root->opener());
3711
3712 // Ensure the popup's window.opener points to the right subframe. Note that
3713 // we can't check the opener's location as above since it's cross-origin.
Nick Carterb7e71312018-08-03 23:36:133714 EXPECT_EQ(true, EvalJs(cross_site_popup_root,
3715 "window.opener === window.opener.top.frames[0];"));
alexmos4cf2aa32015-07-15 23:40:433716}
3717
alexmos5b50b6742016-03-17 20:38:053718// Test that cross-process popups can't be navigated to disallowed URLs by
3719// their opener. This ensures that proper URL validation is performed when
3720// RenderFrameProxyHosts are navigated. See https://crbug.com/595339.
Fergal Daly2e7e1e12020-06-24 09:18:283721IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, NavigatePopupToIllegalURL) {
alexmos5b50b6742016-03-17 20:38:053722 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
3723 EXPECT_TRUE(NavigateToURL(shell(), main_url));
3724
3725 // Open a cross-site popup.
3726 GURL popup_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
nickadef4a52016-06-09 18:45:543727 Shell* popup = OpenPopup(shell(), popup_url, "foo");
alexmos5b50b6742016-03-17 20:38:053728 EXPECT_TRUE(popup);
3729 EXPECT_NE(popup->web_contents()->GetSiteInstance(),
3730 shell()->web_contents()->GetSiteInstance());
3731
Devlin Cronined376d82020-05-01 18:37:553732 WebContentsConsoleObserver console_observer(web_contents());
3733 console_observer.SetPattern("Not allowed to load local resource:*");
Alex Moshchuk5f8671e2018-10-19 02:10:113734
3735 // From the opener, navigate the popup to a file:/// URL. This should result
3736 // in a console error and stay on the old page.
alexmos5b50b6742016-03-17 20:38:053737 GURL file_url("file:///");
nickadef4a52016-06-09 18:45:543738 NavigateNamedFrame(shell(), file_url, "foo");
alexmos5b50b6742016-03-17 20:38:053739 EXPECT_TRUE(WaitForLoadStop(popup->web_contents()));
Alex Moshchuk5f8671e2018-10-19 02:10:113740 EXPECT_EQ(popup_url, popup->web_contents()->GetLastCommittedURL());
Devlin Cronined376d82020-05-01 18:37:553741 EXPECT_TRUE(base::MatchPattern(console_observer.GetMessageAt(0u),
Alex Moshchuk5f8671e2018-10-19 02:10:113742 "Not allowed to load local resource: file:*"));
alexmos5b50b6742016-03-17 20:38:053743
3744 // Now try the same test with a chrome:// URL.
3745 GURL chrome_url(std::string(kChromeUIScheme) + "://" +
3746 std::string(kChromeUIGpuHost));
nickadef4a52016-06-09 18:45:543747 NavigateNamedFrame(shell(), chrome_url, "foo");
alexmos5b50b6742016-03-17 20:38:053748 EXPECT_TRUE(WaitForLoadStop(popup->web_contents()));
Alex Moshchuk5f8671e2018-10-19 02:10:113749 EXPECT_EQ(popup_url, popup->web_contents()->GetLastCommittedURL());
3750 EXPECT_TRUE(
Devlin Cronined376d82020-05-01 18:37:553751 base::MatchPattern(console_observer.GetMessageAt(1u),
Matt Siembor76b485722019-05-04 01:17:323752 std::string("Not allowed to load local resource: ") +
3753 kChromeUIScheme + ":*"));
alexmos5b50b6742016-03-17 20:38:053754}
3755
alexmos646fec02015-07-25 00:11:493756// Verify that named frames are discoverable from their opener's ancestors.
3757// See https://crbug.com/511474.
Fergal Daly2e7e1e12020-06-24 09:18:283758IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmosb0f73be2015-09-30 22:11:333759 DiscoverNamedFrameFromAncestorOfOpener) {
alexmos646fec02015-07-25 00:11:493760 GURL main_url(
3761 embedded_test_server()->GetURL("a.com", "/site_per_process_main.html"));
davidsac6e6c35e42016-11-21 19:45:573762 EXPECT_TRUE(NavigateToURL(shell(), main_url));
alexmos646fec02015-07-25 00:11:493763
3764 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:553765 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos646fec02015-07-25 00:11:493766
3767 // Navigate first child cross-site.
3768 GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:203769 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url));
alexmos646fec02015-07-25 00:11:493770
3771 // Open a popup named "foo" from the first child.
nickadef4a52016-06-09 18:45:543772 Shell* foo_shell =
3773 OpenPopup(root->child_at(0), GURL(url::kAboutBlankURL), "foo");
alexmos646fec02015-07-25 00:11:493774 EXPECT_TRUE(foo_shell);
3775
3776 // Check that a proxy was created for the "foo" popup in a.com.
3777 FrameTreeNode* foo_root =
3778 static_cast<WebContentsImpl*>(foo_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:553779 ->GetPrimaryFrameTree()
3780 .root();
Sharon Yang7424bda2021-11-04 20:27:433781 SiteInstanceImpl* site_instance_a =
3782 root->current_frame_host()->GetSiteInstance();
alexmos646fec02015-07-25 00:11:493783 RenderFrameProxyHost* popup_rfph_for_a =
Harkiran Bolariad22a1dca2022-02-22 17:01:123784 foo_root->current_frame_host()
3785 ->browsing_context_state()
3786 ->GetRenderFrameProxyHost(site_instance_a->group());
alexmos646fec02015-07-25 00:11:493787 EXPECT_TRUE(popup_rfph_for_a);
3788
3789 // Verify that the main frame can find the "foo" popup by name. If
3790 // window.open targets the correct frame, the "foo" popup's current URL
3791 // should be updated to |named_frame_url|.
3792 GURL named_frame_url(embedded_test_server()->GetURL("c.com", "/title2.html"));
nickadef4a52016-06-09 18:45:543793 NavigateNamedFrame(shell(), named_frame_url, "foo");
alexmos646fec02015-07-25 00:11:493794 EXPECT_TRUE(WaitForLoadStop(foo_shell->web_contents()));
3795 EXPECT_EQ(named_frame_url, foo_root->current_url());
3796
3797 // Navigate the popup cross-site and ensure it's still reachable via
3798 // window.open from the main frame.
3799 GURL d_url(embedded_test_server()->GetURL("d.com", "/title3.html"));
Alex Moshchuk7e26eca2018-03-03 01:34:293800 EXPECT_TRUE(NavigateToURLFromRenderer(foo_shell, d_url));
alexmos646fec02015-07-25 00:11:493801 EXPECT_EQ(d_url, foo_root->current_url());
nickadef4a52016-06-09 18:45:543802 NavigateNamedFrame(shell(), named_frame_url, "foo");
alexmos646fec02015-07-25 00:11:493803 EXPECT_TRUE(WaitForLoadStop(foo_shell->web_contents()));
3804 EXPECT_EQ(named_frame_url, foo_root->current_url());
3805}
3806
Dominic Farolino0b067632022-11-11 02:57:493807class SitePerProcessFencedFrameTest : public SitePerProcessBrowserTestBase {
Hyowon Kim2f3bb5c2022-03-18 08:15:453808 public:
3809 SitePerProcessFencedFrameTest() {
Dominic Farolino0b067632022-11-11 02:57:493810 fenced_frame_helper_ =
3811 std::make_unique<content::test::FencedFrameTestHelper>();
Hyowon Kim2f3bb5c2022-03-18 08:15:453812 }
3813
Dominic Farolinoe724b5332022-04-26 05:00:063814 void SetUpOnMainThread() override {
3815 SitePerProcessBrowserTestBase::SetUpOnMainThread();
3816 https_server_.ServeFilesFromSourceDirectory(GetTestDataFilePath());
3817 ASSERT_TRUE(https_server_.Start());
3818 }
3819
Hyowon Kim2f3bb5c2022-03-18 08:15:453820 protected:
Dominic Farolinoe724b5332022-04-26 05:00:063821 net::EmbeddedTestServer& https_server() { return https_server_; }
3822
Hyowon Kim2f3bb5c2022-03-18 08:15:453823 content::RenderFrameHost* CreateFencedFrame(content::RenderFrameHost* parent,
3824 const GURL& url) {
3825 if (fenced_frame_helper_) {
3826 return fenced_frame_helper_->CreateFencedFrame(parent, url);
3827 }
3828
3829 // FencedFrameTestHelper only supports the MPArch version of fenced frames.
3830 // So need to maually create a fenced frame for the ShadowDOM version.
3831 content::TestNavigationManager navigation(web_contents(), url);
3832
3833 constexpr char kAddFencedFrameScript[] = R"({
3834 const fenced_frame = document.createElement('fencedframe');
3835 fenced_frame.src = $1;
3836 document.body.appendChild(fenced_frame);
3837 })";
3838 EXPECT_TRUE(ExecJs(parent, content::JsReplace(kAddFencedFrameScript, url)));
Fergal Daly83bc3cd2023-01-18 00:22:543839 EXPECT_TRUE(navigation.WaitForNavigationFinished());
Hyowon Kim2f3bb5c2022-03-18 08:15:453840
3841 return ChildFrameAt(parent, 0);
3842 }
3843
3844 private:
3845 base::test::ScopedFeatureList feature_list_;
3846 std::unique_ptr<content::test::FencedFrameTestHelper> fenced_frame_helper_;
Dominic Farolinoe724b5332022-04-26 05:00:063847 net::EmbeddedTestServer https_server_{net::EmbeddedTestServer::TYPE_HTTPS};
Hyowon Kim2f3bb5c2022-03-18 08:15:453848};
3849
Dominic Farolino0b067632022-11-11 02:57:493850IN_PROC_BROWSER_TEST_F(SitePerProcessFencedFrameTest,
Hyowon Kim2f3bb5c2022-03-18 08:15:453851 PopupFromFencedFrameDoesNotCreateProxy) {
3852 GURL main_url(embedded_test_server()->GetURL("/title1.html"));
3853 EXPECT_TRUE(NavigateToURL(shell(), main_url));
3854
3855 // It is safe to obtain the root frame tree node here, as it doesn't change.
3856 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
3857
3858 // Create a fenced frame.
Dominic Farolinoe724b5332022-04-26 05:00:063859 GURL fenced_frame_url(https_server().GetURL("/fenced_frames/title1.html"));
Dave Tapuska327c06c92022-06-13 20:31:513860 RenderFrameHost* fenced_frame_host = CreateFencedFrame(
3861 web_contents()->GetPrimaryMainFrame(), fenced_frame_url);
Hyowon Kim2f3bb5c2022-03-18 08:15:453862 EXPECT_NE(nullptr, fenced_frame_host);
3863
3864 // Open a popup named "foo" from the fenced frame.
3865 Shell* popup_shell =
3866 OpenPopup(fenced_frame_host, GURL(url::kAboutBlankURL), "foo", "", false);
3867 EXPECT_TRUE(popup_shell);
3868
3869 // Check that the popup from the fenced frame didn't create a proxy.
3870 // Opening popups from fenced frames forces noopener, which makes named
3871 // frames not discoverable.
3872 FrameTreeNode* popup_root =
3873 static_cast<WebContentsImpl*>(popup_shell->web_contents())
3874 ->GetPrimaryFrameTree()
3875 .root();
3876 EXPECT_EQ(nullptr, popup_root->opener());
3877
3878 SiteInstanceImpl* site_instance =
3879 root->current_frame_host()->GetSiteInstance();
3880 EXPECT_FALSE(popup_root->current_frame_host()
3881 ->browsing_context_state()
3882 ->GetRenderFrameProxyHost(site_instance->group()));
3883
3884 SiteInstanceImpl* embedder_site_instance =
3885 static_cast<RenderFrameHostImpl*>(fenced_frame_host)->GetSiteInstance();
3886 EXPECT_FALSE(popup_root->current_frame_host()
3887 ->browsing_context_state()
3888 ->GetRenderFrameProxyHost(embedder_site_instance->group()));
3889}
3890
alexmos646fec02015-07-25 00:11:493891// Similar to DiscoverNamedFrameFromAncestorOfOpener, but check that if a
3892// window is created without a name and acquires window.name later, it will
3893// still be discoverable from its opener's ancestors. Also, instead of using
3894// an opener's ancestor, this test uses a popup with same origin as that
3895// ancestor. See https://crbug.com/511474.
Fergal Daly2e7e1e12020-06-24 09:18:283896IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmos646fec02015-07-25 00:11:493897 DiscoverFrameAfterSettingWindowName) {
3898 GURL main_url(
3899 embedded_test_server()->GetURL("a.com", "/site_per_process_main.html"));
davidsac6e6c35e42016-11-21 19:45:573900 EXPECT_TRUE(NavigateToURL(shell(), main_url));
alexmos646fec02015-07-25 00:11:493901
3902 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:553903 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos646fec02015-07-25 00:11:493904
3905 // Open a same-site popup from the main frame.
3906 GURL a_com_url(embedded_test_server()->GetURL("a.com", "/title3.html"));
nickadef4a52016-06-09 18:45:543907 Shell* a_com_shell = OpenPopup(root->child_at(0), a_com_url, "");
alexmos646fec02015-07-25 00:11:493908 EXPECT_TRUE(a_com_shell);
3909
3910 // Navigate first child on main frame cross-site.
3911 GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:203912 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url));
alexmos646fec02015-07-25 00:11:493913
3914 // Open an unnamed popup from the first child frame.
nickadef4a52016-06-09 18:45:543915 Shell* foo_shell =
3916 OpenPopup(root->child_at(0), GURL(url::kAboutBlankURL), "");
alexmos646fec02015-07-25 00:11:493917 EXPECT_TRUE(foo_shell);
3918
3919 // There should be no proxy created for the "foo" popup in a.com, since
3920 // there's no way for the two a.com frames to access it yet.
3921 FrameTreeNode* foo_root =
3922 static_cast<WebContentsImpl*>(foo_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:553923 ->GetPrimaryFrameTree()
3924 .root();
Sharon Yang7424bda2021-11-04 20:27:433925 SiteInstanceImpl* site_instance_a =
3926 root->current_frame_host()->GetSiteInstance();
Harkiran Bolariad22a1dca2022-02-22 17:01:123927 EXPECT_FALSE(foo_root->current_frame_host()
3928 ->browsing_context_state()
3929 ->GetRenderFrameProxyHost(site_instance_a->group()));
alexmos646fec02015-07-25 00:11:493930
3931 // Set window.name in the popup's frame.
Avi Drissmanc91bd8e2021-04-19 23:58:443932 EXPECT_TRUE(ExecJs(foo_shell, "window.name = 'foo'"));
alexmos646fec02015-07-25 00:11:493933
3934 // A proxy for the popup should now exist in a.com.
Harkiran Bolariad22a1dca2022-02-22 17:01:123935 EXPECT_TRUE(foo_root->current_frame_host()
3936 ->browsing_context_state()
3937 ->GetRenderFrameProxyHost(site_instance_a->group()));
alexmos646fec02015-07-25 00:11:493938
3939 // Verify that the a.com popup can now find the "foo" popup by name.
3940 GURL named_frame_url(embedded_test_server()->GetURL("c.com", "/title2.html"));
nickadef4a52016-06-09 18:45:543941 NavigateNamedFrame(a_com_shell, named_frame_url, "foo");
alexmos646fec02015-07-25 00:11:493942 EXPECT_TRUE(WaitForLoadStop(foo_shell->web_contents()));
3943 EXPECT_EQ(named_frame_url, foo_root->current_url());
3944}
3945
alexmos95733002015-08-24 16:38:093946// Check that frame opener updates work with subframes. Set up a window with a
3947// popup and update openers for the popup's main frame and subframe to
3948// subframes on first window, as follows:
3949//
3950// foo +---- bar
3951// / \ | / \ .
3952// bar foo <-+ bar foo
3953// ^ |
3954// +--------------------+
3955//
3956// The sites are carefully set up so that both opener updates are cross-process
3957// but still allowed by Blink's navigation checks.
Fergal Daly2e7e1e12020-06-24 09:18:283958IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, UpdateSubframeOpener) {
alexmos95733002015-08-24 16:38:093959 GURL main_url = embedded_test_server()->GetURL(
3960 "foo.com", "/frame_tree/page_with_two_frames.html");
3961 EXPECT_TRUE(NavigateToURL(shell(), main_url));
3962
Carlos Caballero15caeeb2021-10-27 09:57:553963 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos95733002015-08-24 16:38:093964 EXPECT_EQ(2U, root->child_count());
3965
3966 // From the top frame, open a popup and navigate it to a cross-site page with
3967 // two subframes.
nickadef4a52016-06-09 18:45:543968 Shell* popup_shell = OpenPopup(shell(), GURL(url::kAboutBlankURL), "popup");
alexmos95733002015-08-24 16:38:093969 EXPECT_TRUE(popup_shell);
3970 GURL popup_url(embedded_test_server()->GetURL(
3971 "bar.com", "/frame_tree/page_with_post_message_frames.html"));
Alex Moshchuk7e26eca2018-03-03 01:34:293972 EXPECT_TRUE(NavigateToURLFromRenderer(popup_shell, popup_url));
alexmos95733002015-08-24 16:38:093973
3974 FrameTreeNode* popup_root =
3975 static_cast<WebContentsImpl*>(popup_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:553976 ->GetPrimaryFrameTree()
3977 .root();
alexmos95733002015-08-24 16:38:093978 EXPECT_EQ(2U, popup_root->child_count());
3979
3980 // Popup's opener should point to main frame to start with.
3981 EXPECT_EQ(root, popup_root->opener());
3982
3983 // Update the popup's opener to the second subframe on the main page (which
3984 // is same-origin with the top frame, i.e., foo.com).
Nick Carterb7e71312018-08-03 23:36:133985 EXPECT_EQ(true, EvalJs(root->child_at(1), "!!window.open('','popup');"));
alexmos95733002015-08-24 16:38:093986
3987 // Check that updated opener propagated to the browser process and the
3988 // popup's bar.com process.
3989 EXPECT_EQ(root->child_at(1), popup_root->opener());
3990
Nick Carterb7e71312018-08-03 23:36:133991 EXPECT_EQ(true,
3992 EvalJs(popup_shell,
3993 "window.opener === window.opener.parent.frames['frame2'];"));
alexmos95733002015-08-24 16:38:093994
3995 // Now update opener on the popup's second subframe (foo.com) to the main
3996 // page's first subframe (bar.com).
Nick Carterb7e71312018-08-03 23:36:133997 EXPECT_EQ(true, EvalJs(root->child_at(0), "!!window.open('','subframe2');"));
alexmos95733002015-08-24 16:38:093998
3999 // Check that updated opener propagated to the browser process and the
4000 // foo.com process.
4001 EXPECT_EQ(root->child_at(0), popup_root->child_at(1)->opener());
4002
Nick Carterb7e71312018-08-03 23:36:134003 EXPECT_EQ(true,
4004 EvalJs(popup_root->child_at(1),
4005 "window.opener === window.opener.parent.frames['frame1'];"));
alexmos95733002015-08-24 16:38:094006}
4007
alexmos90325cf2015-09-02 17:18:394008// Check that when a subframe navigates to a new SiteInstance, the new
4009// SiteInstance will get a proxy for the opener of subframe's parent. I.e.,
4010// accessing parent.opener from the subframe should still work after a
4011// cross-process navigation.
Fergal Daly2e7e1e12020-06-24 09:18:284012IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmos90325cf2015-09-02 17:18:394013 NavigatingSubframePreservesOpenerInParent) {
4014 GURL main_url = embedded_test_server()->GetURL("a.com", "/post_message.html");
4015 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4016
Carlos Caballero15caeeb2021-10-27 09:57:554017 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos90325cf2015-09-02 17:18:394018
4019 // Open a popup with a cross-site page that has a subframe.
4020 GURL popup_url(embedded_test_server()->GetURL(
4021 "b.com", "/cross_site_iframe_factory.html?b(b)"));
nickadef4a52016-06-09 18:45:544022 Shell* popup_shell = OpenPopup(shell(), popup_url, "popup");
alexmos90325cf2015-09-02 17:18:394023 EXPECT_TRUE(popup_shell);
4024 FrameTreeNode* popup_root =
4025 static_cast<WebContentsImpl*>(popup_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:554026 ->GetPrimaryFrameTree()
4027 .root();
alexmos90325cf2015-09-02 17:18:394028 EXPECT_EQ(1U, popup_root->child_count());
4029
4030 // Check that the popup's opener is correct in the browser process.
4031 EXPECT_EQ(root, popup_root->opener());
4032
4033 // Navigate popup's subframe to another site.
4034 GURL frame_url(embedded_test_server()->GetURL("c.com", "/post_message.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:204035 EXPECT_TRUE(NavigateToURLFromRenderer(popup_root->child_at(0), frame_url));
alexmos90325cf2015-09-02 17:18:394036
4037 // Check that the new subframe process still sees correct opener for its
4038 // parent by sending a postMessage to subframe's parent.opener.
Nick Carterb7e71312018-08-03 23:36:134039 EXPECT_EQ(true, EvalJs(popup_root->child_at(0), "!!parent.opener;"));
alexmos90325cf2015-09-02 17:18:394040
Jan Wilken Dörrie8aeb5742021-03-23 19:27:024041 std::u16string expected_title = u"msg";
alexmos90325cf2015-09-02 17:18:394042 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
Nick Carterb7e71312018-08-03 23:36:134043 EXPECT_EQ(true, EvalJs(popup_root->child_at(0),
4044 "postToOpenerOfParent('msg','*');"));
alexmos90325cf2015-09-02 17:18:394045 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
4046}
4047
alexmosa181efc02015-09-03 00:39:044048// Check that if a subframe has an opener, that opener is preserved when the
4049// subframe navigates cross-site.
Fergal Daly2e7e1e12020-06-24 09:18:284050IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, NavigateSubframeWithOpener) {
alexmosa181efc02015-09-03 00:39:044051 GURL main_url(embedded_test_server()->GetURL(
4052 "foo.com", "/frame_tree/page_with_two_frames.html"));
4053 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4054
Carlos Caballero15caeeb2021-10-27 09:57:554055 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmosa181efc02015-09-03 00:39:044056 EXPECT_EQ(
4057 " Site A ------------ proxies for B\n"
4058 " |--Site B ------- proxies for A\n"
4059 " +--Site A ------- proxies for B\n"
4060 "Where A = http://foo.com/\n"
4061 " B = http://bar.com/",
4062 DepictFrameTree(root));
4063
4064 // Update the first (cross-site) subframe's opener to root frame.
Nick Carterb7e71312018-08-03 23:36:134065 EXPECT_EQ(true, EvalJs(root, "!!window.open('','frame1');"));
alexmosa181efc02015-09-03 00:39:044066
4067 // Check that updated opener propagated to the browser process and subframe's
4068 // process.
4069 EXPECT_EQ(root, root->child_at(0)->opener());
4070
Nick Carterb7e71312018-08-03 23:36:134071 EXPECT_EQ(true,
4072 EvalJs(root->child_at(0), "window.opener === window.parent;"));
alexmosa181efc02015-09-03 00:39:044073
4074 // Navigate the subframe with opener to another site.
4075 GURL frame_url(embedded_test_server()->GetURL("baz.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:204076 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url));
alexmosa181efc02015-09-03 00:39:044077
4078 // Check that the subframe still sees correct opener in its new process.
Nick Carterb7e71312018-08-03 23:36:134079 EXPECT_EQ(true,
4080 EvalJs(root->child_at(0), "window.opener === window.parent;"));
alexmosa181efc02015-09-03 00:39:044081
4082 // Navigate second subframe to a new site. Check that the proxy that's
4083 // created for the first subframe in the new SiteInstance has correct opener.
4084 GURL frame2_url(embedded_test_server()->GetURL("qux.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:204085 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(1), frame2_url));
alexmosa181efc02015-09-03 00:39:044086
Nick Carterb7e71312018-08-03 23:36:134087 EXPECT_EQ(true, EvalJs(root->child_at(1),
4088 "parent.frames['frame1'].opener === parent;"));
alexmosa181efc02015-09-03 00:39:044089}
4090
4091// Check that if a subframe has an opener, that opener is preserved when a new
Dave Tapuska2402595f2022-08-03 16:24:214092// `blink::RemoteFrame` is created for that subframe in another renderer
4093// process. Similar to NavigateSubframeWithOpener, but this test verifies the
4094// subframe opener plumbing for blink::mojom::RemoteFrame::CreateRemoteChild(),
4095// whereas NavigateSubframeWithOpener targets mojom::Renderer::CreateFrame().
Fergal Daly2e7e1e12020-06-24 09:18:284096IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmosa181efc02015-09-03 00:39:044097 NewRenderFrameProxyPreservesOpener) {
4098 GURL main_url(
4099 embedded_test_server()->GetURL("foo.com", "/post_message.html"));
4100 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4101
Carlos Caballero15caeeb2021-10-27 09:57:554102 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmosa181efc02015-09-03 00:39:044103
4104 // Open a popup with a cross-site page that has two subframes.
4105 GURL popup_url(embedded_test_server()->GetURL(
4106 "bar.com", "/frame_tree/page_with_post_message_frames.html"));
nickadef4a52016-06-09 18:45:544107 Shell* popup_shell = OpenPopup(shell(), popup_url, "popup");
alexmosa181efc02015-09-03 00:39:044108 EXPECT_TRUE(popup_shell);
4109 FrameTreeNode* popup_root =
4110 static_cast<WebContentsImpl*>(popup_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:554111 ->GetPrimaryFrameTree()
4112 .root();
alexmosa181efc02015-09-03 00:39:044113 EXPECT_EQ(
4114 " Site A ------------ proxies for B\n"
4115 " |--Site A ------- proxies for B\n"
4116 " +--Site B ------- proxies for A\n"
4117 "Where A = http://bar.com/\n"
4118 " B = http://foo.com/",
4119 DepictFrameTree(popup_root));
4120
4121 // Update the popup's second subframe's opener to root frame. This is
4122 // allowed because that subframe is in the same foo.com SiteInstance as the
4123 // root frame.
Nick Carterb7e71312018-08-03 23:36:134124 EXPECT_EQ(true, EvalJs(root, "!!window.open('','subframe2');"));
alexmosa181efc02015-09-03 00:39:044125
4126 // Check that the opener update propagated to the browser process and bar.com
4127 // process.
4128 EXPECT_EQ(root, popup_root->child_at(1)->opener());
Nick Carterb7e71312018-08-03 23:36:134129 EXPECT_EQ(true,
4130 EvalJs(popup_root->child_at(0),
4131 "parent.frames['subframe2'].opener && "
4132 " parent.frames['subframe2'].opener === parent.opener;"));
alexmosa181efc02015-09-03 00:39:044133
4134 // Navigate the popup's first subframe to another site.
4135 GURL frame_url(
4136 embedded_test_server()->GetURL("baz.com", "/post_message.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:204137 EXPECT_TRUE(NavigateToURLFromRenderer(popup_root->child_at(0), frame_url));
alexmosa181efc02015-09-03 00:39:044138
4139 // Check that the second subframe's opener is still correct in the first
4140 // subframe's new process. Verify it both in JS and with a postMessage.
Nick Carterb7e71312018-08-03 23:36:134141 EXPECT_EQ(true,
4142 EvalJs(popup_root->child_at(0),
4143 "parent.frames['subframe2'].opener && "
4144 " parent.frames['subframe2'].opener === parent.opener;"));
alexmosa181efc02015-09-03 00:39:044145
Jan Wilken Dörrie8aeb5742021-03-23 19:27:024146 std::u16string expected_title = u"msg";
alexmosa181efc02015-09-03 00:39:044147 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
Nick Carterb7e71312018-08-03 23:36:134148 EXPECT_EQ(true, EvalJs(popup_root->child_at(0),
4149 "postToOpenerOfSibling('subframe2', 'msg', '*');"));
alexmosa181efc02015-09-03 00:39:044150 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
4151}
4152
arthursonzognia2754522019-07-03 18:25:364153// Test for https://crbug.com/515302. Perform two navigations, A1 -> B2 -> A3,
Dave Tapuskaca250702021-01-07 17:40:554154// and drop the mojo::AgentSchedulingGroupHost::DidUnloadRenderFrame from the A1
4155// -> B2 navigation, so that the second B2 -> A3 navigation is initiated before
4156// the first page receives the
4157// mojo::AgentSchedulingGroupHost::DidUnloadRenderFrame. Ensure that this
4158// doesn't crash and that the RVH(A1) is not reused in that case.
Xiaohan Wang1ecfd002022-01-19 22:33:104159#if BUILDFLAG(IS_MAC)
Nasko Oskov0f3cbb12020-01-07 17:52:144160#define MAYBE_RenderViewHostIsNotReusedAfterDelayedUnloadACK \
4161 DISABLED_RenderViewHostIsNotReusedAfterDelayedUnloadACK
Dominic Battre0abaec212018-06-05 10:22:424162#else
Nasko Oskov0f3cbb12020-01-07 17:52:144163#define MAYBE_RenderViewHostIsNotReusedAfterDelayedUnloadACK \
4164 RenderViewHostIsNotReusedAfterDelayedUnloadACK
Dominic Battre0abaec212018-06-05 10:22:424165#endif
Fergal Daly2e7e1e12020-06-24 09:18:284166IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Nasko Oskov0f3cbb12020-01-07 17:52:144167 MAYBE_RenderViewHostIsNotReusedAfterDelayedUnloadACK) {
alexmosb97d6bb62015-09-24 07:31:264168 GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
davidsac6e6c35e42016-11-21 19:45:574169 EXPECT_TRUE(NavigateToURL(shell(), a_url));
alexmosb97d6bb62015-09-24 07:31:264170
Carlos Caballero15caeeb2021-10-27 09:57:554171 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmosb97d6bb62015-09-24 07:31:264172 RenderFrameHostImpl* rfh = root->current_frame_host();
4173 RenderViewHostImpl* rvh = rfh->render_view_host();
creise73d58ef2016-03-29 22:12:154174 int rvh_routing_id = rvh->GetRoutingID();
Emily Andrewsd15fd762024-12-10 20:41:544175 int rvh_process_id = rvh->GetProcess()->GetDeprecatedID();
creise73d58ef2016-03-29 22:12:154176 SiteInstanceImpl* site_instance = rfh->GetSiteInstance();
alexmosb97d6bb62015-09-24 07:31:264177 RenderFrameDeletedObserver deleted_observer(rfh);
4178
Dave Tapuskaca250702021-01-07 17:40:554179 // Install a BrowserMessageFilter to drop
4180 // mojo::AgentSchedulingGroupHost::DidUnloadRenderFrame messages in A's
4181 // process.
4182 auto unload_ack_filter = base::BindRepeating([] { return true; });
4183 rfh->SetUnloadACKCallbackForTesting(unload_ack_filter);
Nasko Oskov0f3cbb12020-01-07 17:52:144184 rfh->DisableUnloadTimerForTesting();
alexmosb97d6bb62015-09-24 07:31:264185
alexmos9aa61232016-04-26 21:54:024186 // Navigate to B. This must wait for DidCommitProvisionalLoad and not
Nasko Oskov0f3cbb12020-01-07 17:52:144187 // DidStopLoading, so that the Unload timer doesn't call OnUnloaded and
alexmos9aa61232016-04-26 21:54:024188 // destroy |rfh| and |rvh| before they are checked in the test.
alexmosb97d6bb62015-09-24 07:31:264189 GURL b_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
4190 TestFrameNavigationObserver commit_observer(root);
Avi Drissmanc91bd8e2021-04-19 23:58:444191 EXPECT_TRUE(ExecJs(shell(), JsReplace("location = $1", b_url)));
nasko8206fa12016-03-22 02:24:134192 commit_observer.WaitForCommit();
creise73d58ef2016-03-29 22:12:154193 EXPECT_FALSE(deleted_observer.deleted());
alexmosb97d6bb62015-09-24 07:31:264194
Sreeja Kamishettyce8d5942020-08-19 11:25:514195 // The previous RFH should be either:
4196 // 1) In the BackForwardCache, if back-forward cache is enabled.
Dave Tapuskaca250702021-01-07 17:40:554197 // 2) Pending deletion otherwise, since the
4198 // mojo::AgentSchedulingGroupHost::DidUnloadRenderFrame for A->B is dropped.
Sreeja Kamishettyce8d5942020-08-19 11:25:514199 EXPECT_THAT(
4200 rfh->lifecycle_state(),
4201 testing::AnyOf(
4202 testing::Eq(
Sreeja Kamishetty299329ad2021-03-25 14:06:014203 RenderFrameHostImpl::LifecycleStateImpl::kRunningUnloadHandlers),
Sreeja Kamishettyce8d5942020-08-19 11:25:514204 testing::Eq(
Sreeja Kamishetty299329ad2021-03-25 14:06:014205 RenderFrameHostImpl::LifecycleStateImpl::kInBackForwardCache)));
alexmosb97d6bb62015-09-24 07:31:264206
Dave Tapuskaca250702021-01-07 17:40:554207 // Without the mojo::AgentSchedulingGroupHost::DidUnloadRenderFrame and timer,
4208 // the process A will never shutdown. Simulate the process being killed now.
Arthur Sonzogni4973b392018-08-20 18:14:454209 content::RenderProcessHostWatcher crash_observer(
4210 rvh->GetProcess(),
4211 content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
4212 EXPECT_TRUE(rvh->GetProcess()->Shutdown(0));
4213 crash_observer.Wait();
alexmosb97d6bb62015-09-24 07:31:264214
alexmos9aa61232016-04-26 21:54:024215 // Verify that the RVH and RFH for A were cleaned up.
Arthur Sonzognif6785ec2022-12-05 10:11:504216 EXPECT_FALSE(root->frame_tree().GetRenderViewHost(site_instance->group()));
alexmos9aa61232016-04-26 21:54:024217 EXPECT_TRUE(deleted_observer.deleted());
4218
Alex Moshchuk7e26eca2018-03-03 01:34:294219 // Start a navigation back to A, being careful to stay in the same
4220 // BrowsingInstance, and check that the RenderViewHost wasn't reused.
Jiacheng Guo4bdd0be2024-06-11 23:35:214221 TestNavigationManager navigation_manager(shell()->web_contents(), a_url);
Alex Moshchuk7e26eca2018-03-03 01:34:294222 shell()->LoadURLForFrame(a_url, std::string(),
4223 ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK));
Jiacheng Guo4bdd0be2024-06-11 23:35:214224 navigation_manager.WaitForSpeculativeRenderFrameHostCreation();
Aaron Colwellc4bd7d62021-01-29 04:23:134225 RenderFrameHostImpl* pending_rfh =
4226 root->render_manager()->speculative_frame_host();
4227 RenderViewHostImpl* pending_rvh = pending_rfh->render_view_host();
arthursonzognia2754522019-07-03 18:25:364228
4229 // When ProactivelySwapBrowsingInstance A1 and A3 aren't using the same
4230 // BrowsingInstance.
Rakina Zata Amni6d66b932020-07-21 05:44:174231 if (CanCrossSiteNavigationsProactivelySwapBrowsingInstances())
Aaron Colwellc4bd7d62021-01-29 04:23:134232 EXPECT_NE(site_instance, pending_rfh->GetSiteInstance());
arthursonzognia2754522019-07-03 18:25:364233 else
Aaron Colwellc4bd7d62021-01-29 04:23:134234 EXPECT_EQ(site_instance, pending_rfh->GetSiteInstance());
arthursonzognia2754522019-07-03 18:25:364235
Alex Moshchuk27caae82017-09-11 23:11:184236 EXPECT_FALSE(rvh_routing_id == pending_rvh->GetRoutingID() &&
Emily Andrewsd15fd762024-12-10 20:41:544237 rvh_process_id == pending_rvh->GetProcess()->GetDeprecatedID());
alexmosb97d6bb62015-09-24 07:31:264238
alexmosb97d6bb62015-09-24 07:31:264239 // Make sure the last navigation finishes without crashing.
Jiacheng Guo4bdd0be2024-06-11 23:35:214240 ASSERT_TRUE(navigation_manager.WaitForNavigationFinished());
alexmosb97d6bb62015-09-24 07:31:264241}
4242
creise73d58ef2016-03-29 22:12:154243// Test for https://crbug.com/591478, where navigating to a cross-site page with
alexmos9aa61232016-04-26 21:54:024244// a subframe on the old site caused a crash while trying to reuse the old
4245// RenderViewHost.
Fergal Daly2e7e1e12020-06-24 09:18:284246IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmos9aa61232016-04-26 21:54:024247 ReusePendingDeleteRenderViewHostForSubframe) {
creise73d58ef2016-03-29 22:12:154248 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
4249 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4250
creise73d58ef2016-03-29 22:12:154251 std::string script =
4252 "window.onunload = function() { "
4253 " var start = Date.now();"
4254 " while (Date.now() - start < 1000);"
4255 "}";
Avi Drissmanc91bd8e2021-04-19 23:58:444256 EXPECT_TRUE(ExecJs(shell(), script));
creise73d58ef2016-03-29 22:12:154257
creis05a1cb72016-03-31 21:26:244258 // Navigating cross-site with an iframe to the original site shouldn't crash.
creise73d58ef2016-03-29 22:12:154259 GURL second_url(embedded_test_server()->GetURL(
4260 "b.com", "/cross_site_iframe_factory.html?b(a)"));
4261 EXPECT_TRUE(NavigateToURL(shell(), second_url));
4262
creis05a1cb72016-03-31 21:26:244263 // If the subframe is created while the main frame is pending deletion, then
alexmos9aa61232016-04-26 21:54:024264 // the RVH will be reused. The main frame should've been swapped with a
4265 // proxy despite being the last active frame in the progress (see
4266 // https://crbug.com/568836), and this proxy should also be reused by the new
4267 // page.
4268 //
4269 // TODO(creis, alexmos): Find a way to assert this that isn't flaky. For now,
creis05a1cb72016-03-31 21:26:244270 // the test is just likely (not certain) to catch regressions by crashing.
creise73d58ef2016-03-29 22:12:154271}
4272
alexmosca2c6ba2015-10-01 21:52:254273// Check that when a cross-process frame acquires focus, the old focused frame
4274// loses focus and fires blur events. Starting on a page with a cross-site
4275// subframe, simulate mouse clicks to switch focus from root frame to subframe
4276// and then back to root frame.
Fergal Daly2e7e1e12020-06-24 09:18:284277IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmosca2c6ba2015-10-01 21:52:254278 CrossProcessFocusChangeFiresBlurEvents) {
4279 GURL main_url(
4280 embedded_test_server()->GetURL("a.com", "/page_with_input_field.html"));
4281 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4282
Mustaq Ahmedba573cf2025-05-28 17:06:184283 SimulateEndOfPaintHoldingOnPrimaryMainFrame(web_contents());
4284
Carlos Caballero15caeeb2021-10-27 09:57:554285 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmosca2c6ba2015-10-01 21:52:254286
4287 EXPECT_EQ(
4288 " Site A ------------ proxies for B\n"
4289 " +--Site B ------- proxies for A\n"
4290 "Where A = http://a.com/\n"
4291 " B = http://b.com/",
4292 DepictFrameTree(root));
4293
4294 // Focus the main frame's text field. The return value "input-focus"
4295 // indicates that the focus event was fired correctly.
Chris Fredricksonb854bbf2023-03-27 17:27:444296 EXPECT_EQ("input-focus", EvalJs(shell(), "focusInputField()"));
alexmosca2c6ba2015-10-01 21:52:254297
4298 // The main frame should be focused.
Arthur Sonzognif6785ec2022-12-05 10:11:504299 EXPECT_EQ(root, root->frame_tree().GetFocusedFrame());
alexmosca2c6ba2015-10-01 21:52:254300
Colin Blundellecd384f2022-05-11 08:58:304301 DOMMessageQueue msg_queue(web_contents());
alexmosca2c6ba2015-10-01 21:52:254302
4303 // Click on the cross-process subframe.
alexmosb1dc2162015-11-05 00:59:204304 SimulateMouseClick(
4305 root->child_at(0)->current_frame_host()->GetRenderWidgetHost(), 1, 1);
alexmosca2c6ba2015-10-01 21:52:254306
4307 // Check that the main frame lost focus and fired blur event on the input
4308 // text field.
Chris Fredricksonb854bbf2023-03-27 17:27:444309 EXPECT_EQ(true, EvalJs(shell(), "waitForBlur()"));
alexmosca2c6ba2015-10-01 21:52:254310
4311 // The subframe should now be focused.
Arthur Sonzognif6785ec2022-12-05 10:11:504312 EXPECT_EQ(root->child_at(0), root->frame_tree().GetFocusedFrame());
alexmosca2c6ba2015-10-01 21:52:254313
4314 // Click on the root frame.
Dave Tapuska327c06c92022-06-13 20:31:514315 SimulateMouseClick(shell()
4316 ->web_contents()
4317 ->GetPrimaryMainFrame()
4318 ->GetRenderViewHost()
4319 ->GetWidget(),
4320 1, 1);
alexmosca2c6ba2015-10-01 21:52:254321
4322 // Check that the subframe lost focus and fired blur event on its
4323 // document's body.
Chris Fredricksonb854bbf2023-03-27 17:27:444324 std::string status;
alexmosca2c6ba2015-10-01 21:52:254325 while (msg_queue.WaitForMessage(&status)) {
4326 if (status == "\"document-blur\"")
4327 break;
4328 }
4329
4330 // The root frame should be focused again.
Arthur Sonzognif6785ec2022-12-05 10:11:504331 EXPECT_EQ(root, root->frame_tree().GetFocusedFrame());
alexmosca2c6ba2015-10-01 21:52:254332}
4333
alexmosb1dc2162015-11-05 00:59:204334// Check that when a cross-process subframe is focused, its parent's
4335// document.activeElement correctly returns the corresponding <iframe> element.
4336// The test sets up an A-embed-B-embed-C page and shifts focus A->B->A->C,
4337// checking document.activeElement after each change.
Fergal Daly2e7e1e12020-06-24 09:18:284338IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, DocumentActiveElement) {
alexmosb1dc2162015-11-05 00:59:204339 GURL main_url(embedded_test_server()->GetURL(
4340 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
4341 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4342
Mustaq Ahmedba573cf2025-05-28 17:06:184343 SimulateEndOfPaintHoldingOnPrimaryMainFrame(web_contents());
4344
Carlos Caballero15caeeb2021-10-27 09:57:554345 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmosb1dc2162015-11-05 00:59:204346
4347 EXPECT_EQ(
4348 " Site A ------------ proxies for B C\n"
4349 " +--Site B ------- proxies for A C\n"
4350 " +--Site C -- proxies for A B\n"
4351 "Where A = http://a.com/\n"
4352 " B = http://b.com/\n"
4353 " C = http://c.com/",
4354 DepictFrameTree(root));
4355
4356 FrameTreeNode* child = root->child_at(0);
4357 FrameTreeNode* grandchild = root->child_at(0)->child_at(0);
4358
4359 // The main frame should be focused to start with.
Arthur Sonzognif6785ec2022-12-05 10:11:504360 EXPECT_EQ(root, root->frame_tree().GetFocusedFrame());
alexmosb1dc2162015-11-05 00:59:204361
alexmos21acae52015-11-07 01:04:434362 // Focus the b.com frame.
4363 FocusFrame(child);
Arthur Sonzognif6785ec2022-12-05 10:11:504364 EXPECT_EQ(child, root->frame_tree().GetFocusedFrame());
alexmosb1dc2162015-11-05 00:59:204365
alexmos21acae52015-11-07 01:04:434366 // Helper function to check a property of document.activeElement in the
4367 // specified frame.
alexmosb1dc2162015-11-05 00:59:204368 auto verify_active_element_property = [](RenderFrameHost* rfh,
4369 const std::string& property,
4370 const std::string& expected_value) {
4371 std::string script = base::StringPrintf(
Nick Carterb7e71312018-08-03 23:36:134372 "document.activeElement.%s.toLowerCase();", property.c_str());
4373 EXPECT_EQ(expected_value, EvalJs(rfh, script));
alexmosb1dc2162015-11-05 00:59:204374 };
4375
alexmos21acae52015-11-07 01:04:434376 // Verify that document.activeElement on main frame points to the <iframe>
4377 // element for the b.com frame.
alexmosb1dc2162015-11-05 00:59:204378 RenderFrameHost* root_rfh = root->current_frame_host();
4379 verify_active_element_property(root_rfh, "tagName", "iframe");
4380 verify_active_element_property(root_rfh, "src", child->current_url().spec());
4381
alexmos21acae52015-11-07 01:04:434382 // Focus the a.com main frame again.
4383 FocusFrame(root);
Arthur Sonzognif6785ec2022-12-05 10:11:504384 EXPECT_EQ(root, root->frame_tree().GetFocusedFrame());
alexmosb1dc2162015-11-05 00:59:204385
4386 // Main frame document's <body> should now be the active element.
4387 verify_active_element_property(root_rfh, "tagName", "body");
4388
alexmos21acae52015-11-07 01:04:434389 // Now shift focus from main frame to c.com frame.
4390 FocusFrame(grandchild);
alexmosb1dc2162015-11-05 00:59:204391
4392 // Check document.activeElement in main frame. It should still point to
4393 // <iframe> for the b.com frame, since Blink computes the focused iframe
4394 // element by walking the parent chain of the focused frame until it hits the
4395 // current frame. This logic should still work with remote frames.
4396 verify_active_element_property(root_rfh, "tagName", "iframe");
4397 verify_active_element_property(root_rfh, "src", child->current_url().spec());
4398
4399 // Check document.activeElement in b.com subframe. It should point to
4400 // <iframe> for the c.com frame. This is a tricky case where B needs to find
4401 // out that focus changed from one remote frame to another (A to C).
4402 RenderFrameHost* child_rfh = child->current_frame_host();
4403 verify_active_element_property(child_rfh, "tagName", "iframe");
4404 verify_active_element_property(child_rfh, "src",
4405 grandchild->current_url().spec());
4406}
4407
alexmos5357efb2015-12-16 21:44:004408// Check that window.focus works for cross-process subframes.
Fergal Daly2e7e1e12020-06-24 09:18:284409IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, SubframeWindowFocus) {
alexmos5357efb2015-12-16 21:44:004410 GURL main_url(embedded_test_server()->GetURL(
4411 "a.com", "/cross_site_iframe_factory.html?a(b,c)"));
4412 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4413
Carlos Caballero15caeeb2021-10-27 09:57:554414 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos5357efb2015-12-16 21:44:004415
4416 EXPECT_EQ(
4417 " Site A ------------ proxies for B C\n"
4418 " |--Site B ------- proxies for A C\n"
4419 " +--Site C ------- proxies for A B\n"
4420 "Where A = http://a.com/\n"
4421 " B = http://b.com/\n"
4422 " C = http://c.com/",
4423 DepictFrameTree(root));
4424
4425 FrameTreeNode* child1 = root->child_at(0);
4426 FrameTreeNode* child2 = root->child_at(1);
4427
4428 // The main frame should be focused to start with.
Arthur Sonzognif6785ec2022-12-05 10:11:504429 EXPECT_EQ(root, root->frame_tree().GetFocusedFrame());
alexmos5357efb2015-12-16 21:44:004430
alexmos5357efb2015-12-16 21:44:004431 // Register focus and blur events that will send messages when each frame's
Nick Carterb7e71312018-08-03 23:36:134432 // window gets or loses focus, and configure some utility functions useful for
4433 // waiting for these messages.
4434 const char kSetupFocusEvents[] = R"(
4435 window.addEventListener('focus', function() {
4436 window.top.postMessage('%s-got-focus', '*');
4437 });
4438 window.addEventListener('blur', function() {
4439 window.top.postMessage('%s-lost-focus', '*');
4440 });
4441 function onEvent(target, eventName, property, value) {
4442 return new Promise((resolve, reject) => {
4443 function listener(event) {
4444 if (event[property] == value) {
4445 resolve();
4446 target.removeEventListener(eventName, listener);
4447 }
4448 };
4449 target.addEventListener(eventName, listener);
4450 });
4451 }
4452 function expectMessages(messageList) {
4453 var promiseList = messageList.map(
4454 (dataValue) => onEvent(window, 'message', 'data', dataValue));
4455 return Promise.all(promiseList);
4456 }
4457 )";
alexmos5357efb2015-12-16 21:44:004458 std::string script = base::StringPrintf(kSetupFocusEvents, "main", "main");
lukaszac7d6bd32017-07-11 00:19:314459 ExecuteScriptAsync(shell(), script);
alexmos5357efb2015-12-16 21:44:004460 script = base::StringPrintf(kSetupFocusEvents, "child1", "child1");
lukaszac7d6bd32017-07-11 00:19:314461 ExecuteScriptAsync(child1, script);
alexmos5357efb2015-12-16 21:44:004462 script = base::StringPrintf(kSetupFocusEvents, "child2", "child2");
lukaszac7d6bd32017-07-11 00:19:314463 ExecuteScriptAsync(child2, script);
alexmos5357efb2015-12-16 21:44:004464
4465 // Execute window.focus on the B subframe from the A main frame.
alexmos5357efb2015-12-16 21:44:004466 // Process A should fire a blur event, and process B should fire a focus
4467 // event. Wait for both events.
Nick Carterb7e71312018-08-03 23:36:134468 EXPECT_EQ(true, EvalJs(root, R"((async function() {
4469 allMessages = [];
4470 window.addEventListener('message', (event) => {
4471 allMessages.push(event.data);
4472 });
alexmos5357efb2015-12-16 21:44:004473
Nick Carterb7e71312018-08-03 23:36:134474 var messages = expectMessages(['main-lost-focus', 'child1-got-focus']);
4475 frames[0].focus();
4476 await messages;
4477
4478 return allMessages.length == 2 || allMessages;
4479 })())"));
4480
Arthur Sonzognif6785ec2022-12-05 10:11:504481 EXPECT_EQ(child1, root->frame_tree().GetFocusedFrame());
alexmos5357efb2015-12-16 21:44:004482
4483 // Now, execute window.focus on the C subframe from A main frame. This
4484 // checks that we can shift focus from one remote frame to another.
Nick Carterb7e71312018-08-03 23:36:134485 //
alexmos5357efb2015-12-16 21:44:004486 // Wait for the two subframes (B and C) to fire blur and focus events.
Nick Carterb7e71312018-08-03 23:36:134487 EXPECT_EQ(true, EvalJs(root, R"((async function() {
4488 var messages = expectMessages(['child1-lost-focus', 'child2-got-focus']);
4489 frames[1].focus();
4490 await messages;
4491 return allMessages.length == 4 || allMessages;
4492 })())"));
alexmos5357efb2015-12-16 21:44:004493
4494 // The C subframe should now be focused.
Arthur Sonzognif6785ec2022-12-05 10:11:504495 EXPECT_EQ(child2, root->frame_tree().GetFocusedFrame());
alexmos5357efb2015-12-16 21:44:004496
Nick Carterb7e71312018-08-03 23:36:134497 // Install event listeners in the A main frame, expecting the main frame to
4498 // obtain focus.
4499 EXPECT_TRUE(
4500 ExecJs(root,
4501 "var messages = "
4502 " expectMessages(['child2-lost-focus', 'main-got-focus']);"));
4503
alexmos5357efb2015-12-16 21:44:004504 // window.focus the main frame from the C subframe.
lukaszac7d6bd32017-07-11 00:19:314505 ExecuteScriptAsync(child2, "parent.focus()");
alexmos5357efb2015-12-16 21:44:004506
Nick Carterb7e71312018-08-03 23:36:134507 // Wait for the messages to arrive in the A main frame.
4508 EXPECT_EQ(true, EvalJs(root, R"((async function() {
4509 await messages;
4510 return allMessages.length == 6 || allMessages;
4511 })())"));
alexmos5357efb2015-12-16 21:44:004512
4513 // The main frame should now be focused.
Arthur Sonzognif6785ec2022-12-05 10:11:504514 EXPECT_EQ(root, root->frame_tree().GetFocusedFrame());
alexmos5357efb2015-12-16 21:44:004515}
4516
Alex Moshchukf1d9af02018-01-18 00:54:244517// Check that when a subframe has focus, and another subframe navigates
4518// cross-site to a new renderer process, this doesn't reset the focused frame
4519// to the main frame. See https://crbug.com/802156.
Fergal Daly2e7e1e12020-06-24 09:18:284520IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Alex Moshchukf1d9af02018-01-18 00:54:244521 SubframeFocusNotLostWhenAnotherFrameNavigatesCrossSite) {
4522 GURL main_url(embedded_test_server()->GetURL(
4523 "a.com", "/cross_site_iframe_factory.html?a(a,a)"));
4524 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4525
Carlos Caballero15caeeb2021-10-27 09:57:554526 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchukf1d9af02018-01-18 00:54:244527 FrameTreeNode* child1 = root->child_at(0);
4528 FrameTreeNode* child2 = root->child_at(1);
4529
4530 // The main frame should be focused to start with.
Arthur Sonzognif6785ec2022-12-05 10:11:504531 EXPECT_EQ(root, root->frame_tree().GetFocusedFrame());
Alex Moshchukf1d9af02018-01-18 00:54:244532
4533 // Add an <input> element to the first subframe.
4534 ExecuteScriptAsync(
4535 child1, "document.body.appendChild(document.createElement('input'))");
4536
4537 // Focus the first subframe using window.focus().
4538 FrameFocusedObserver focus_observer(child1->current_frame_host());
4539 ExecuteScriptAsync(root, "frames[0].focus()");
4540 focus_observer.Wait();
Arthur Sonzognif6785ec2022-12-05 10:11:504541 EXPECT_EQ(child1, root->frame_tree().GetFocusedFrame());
Alex Moshchukf1d9af02018-01-18 00:54:244542
4543 // Give focus to the <input> element in the first subframe.
4544 ExecuteScriptAsync(child1, "document.querySelector('input').focus()");
4545
4546 // Now, navigate second subframe cross-site. Ensure that this won't change
4547 // the focused frame.
4548 GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:204549 EXPECT_TRUE(NavigateToURLFromRenderer(child2, b_url));
Alex Moshchukf1d9af02018-01-18 00:54:244550 // This is needed because the incorrect focused frame change as in
4551 // https://crbug.com/802156 requires an additional post-commit IPC roundtrip.
4552 base::RunLoop().RunUntilIdle();
Arthur Sonzognif6785ec2022-12-05 10:11:504553 EXPECT_EQ(child1, root->frame_tree().GetFocusedFrame());
Alex Moshchukf1d9af02018-01-18 00:54:244554
4555 // The <input> in first subframe should still be the activeElement.
Avi Drissmanc91bd8e2021-04-19 23:58:444556 EXPECT_EQ(
4557 "input",
4558 base::ToLowerASCII(
4559 EvalJs(child1, "document.activeElement.tagName").ExtractString()));
Alex Moshchukf1d9af02018-01-18 00:54:244560}
4561
Dave Tapuska2402595f2022-08-03 16:24:214562// Tests that we are using the correct `blink::RemoteFrame` when navigating an
lfg3faeb652015-10-21 16:34:384563// opener window.
Fergal Daly2e7e1e12020-06-24 09:18:284564IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, OpenerSetLocation) {
lfg3faeb652015-10-21 16:34:384565 // Navigate the main window.
4566 GURL main_url(embedded_test_server()->GetURL("/title1.html"));
4567 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4568 EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), main_url);
4569
4570 // Load cross-site page into a new window.
4571 GURL cross_url = embedded_test_server()->GetURL("foo.com", "/title1.html");
nickadef4a52016-06-09 18:45:544572 Shell* popup = OpenPopup(shell(), cross_url, "");
lfg3faeb652015-10-21 16:34:384573 EXPECT_EQ(popup->web_contents()->GetLastCommittedURL(), cross_url);
4574
4575 // Use new window to navigate main window.
Avi Drissmanc91bd8e2021-04-19 23:58:444576 EXPECT_TRUE(
4577 ExecJs(popup, JsReplace("window.opener.location.href = $1", cross_url)));
lfg3faeb652015-10-21 16:34:384578 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
4579 EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), cross_url);
4580}
4581
Asami Doi42d7fa7462021-12-21 07:30:064582// crbug.com/1281755
Xiaohan Wang1ecfd002022-01-19 22:33:104583#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
Asami Doi42d7fa7462021-12-21 07:30:064584#define MAYBE_NavigateProxyAndDetachBeforeProvisionalFrameCreation \
4585 DISABLED_NavigateProxyAndDetachBeforeProvisionalFrameCreation
4586#else
4587#define MAYBE_NavigateProxyAndDetachBeforeProvisionalFrameCreation \
4588 NavigateProxyAndDetachBeforeProvisionalFrameCreation
4589#endif
alexmosba1fb7152015-12-12 07:20:304590// Test for https://crbug.com/526304, where a parent frame executes a
4591// remote-to-local navigation on a child frame and immediately removes the same
4592// child frame. This test exercises the path where the detach happens before
4593// the provisional local frame is created.
Asami Doi42d7fa7462021-12-21 07:30:064594IN_PROC_BROWSER_TEST_P(
4595 SitePerProcessBrowserTest,
4596 MAYBE_NavigateProxyAndDetachBeforeProvisionalFrameCreation) {
alexmosba1fb7152015-12-12 07:20:304597 GURL main_url(embedded_test_server()->GetURL(
4598 "a.com", "/cross_site_iframe_factory.html?a(b,b)"));
4599 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4600
4601 WebContents* contents = shell()->web_contents();
4602 FrameTreeNode* root =
Carlos Caballero15caeeb2021-10-27 09:57:554603 static_cast<WebContentsImpl*>(contents)->GetPrimaryFrameTree().root();
alexmosba1fb7152015-12-12 07:20:304604 EXPECT_EQ(2U, root->child_count());
4605
4606 // Navigate the first child frame to 'about:blank' (which is a
4607 // remote-to-local transition), and then detach it.
Ria Jiang79f58ed922018-04-10 03:30:524608 FrameDeletedObserver observer(root->child_at(0)->current_frame_host());
alexmosba1fb7152015-12-12 07:20:304609 std::string script =
4610 "var f = document.querySelector('iframe');"
4611 "f.contentWindow.location.href = 'about:blank';"
4612 "setTimeout(function() { document.body.removeChild(f); }, 0);";
Avi Drissmanc91bd8e2021-04-19 23:58:444613 EXPECT_TRUE(ExecJs(root, script));
alexmosba1fb7152015-12-12 07:20:304614 observer.Wait();
4615 EXPECT_EQ(1U, root->child_count());
4616
4617 // Make sure the main frame renderer does not crash and ignores the
4618 // navigation to the frame that's already been deleted.
Nick Carterb7e71312018-08-03 23:36:134619 EXPECT_EQ(1, EvalJs(root, "frames.length"));
alexmosba1fb7152015-12-12 07:20:304620}
4621
4622// Test for a variation of https://crbug.com/526304, where a child frame does a
4623// remote-to-local navigation, and the parent frame removes that child frame
4624// after the provisional local frame is created and starts to navigate, but
4625// before it commits.
Fergal Daly2e7e1e12020-06-24 09:18:284626IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmosba1fb7152015-12-12 07:20:304627 NavigateProxyAndDetachBeforeCommit) {
4628 GURL main_url(embedded_test_server()->GetURL(
4629 "a.com", "/cross_site_iframe_factory.html?a(b,b)"));
4630 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4631
4632 WebContents* contents = shell()->web_contents();
4633 FrameTreeNode* root =
Carlos Caballero15caeeb2021-10-27 09:57:554634 static_cast<WebContentsImpl*>(contents)->GetPrimaryFrameTree().root();
alexmosba1fb7152015-12-12 07:20:304635 EXPECT_EQ(2U, root->child_count());
4636 FrameTreeNode* child = root->child_at(0);
4637
4638 // Start a remote-to-local navigation for the child, but don't wait for
4639 // commit.
4640 GURL same_site_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
4641 NavigationController::LoadURLParams params(same_site_url);
4642 params.transition_type = ui::PAGE_TRANSITION_LINK;
4643 params.frame_tree_node_id = child->frame_tree_node_id();
Carlos Caballero04aab362021-02-15 17:38:164644 child->navigator().controller().LoadURLWithParams(params);
alexmosba1fb7152015-12-12 07:20:304645
4646 // Tell parent to remove the first child. This should happen after the
4647 // previous navigation starts but before it commits.
Ria Jiang79f58ed922018-04-10 03:30:524648 FrameDeletedObserver observer(child->current_frame_host());
Avi Drissmanc91bd8e2021-04-19 23:58:444649 EXPECT_TRUE(ExecJs(
nickadef4a52016-06-09 18:45:544650 root, "document.body.removeChild(document.querySelector('iframe'));"));
alexmosba1fb7152015-12-12 07:20:304651 observer.Wait();
4652 EXPECT_EQ(1U, root->child_count());
4653
4654 // Make sure the a.com renderer does not crash.
Nick Carterb7e71312018-08-03 23:36:134655 EXPECT_EQ(1, EvalJs(root, "frames.length;"));
alexmosba1fb7152015-12-12 07:20:304656}
4657
nasko13b8e772016-03-03 19:41:354658// Similar to NavigateProxyAndDetachBeforeCommit, but uses a synchronous
4659// navigation to about:blank and the parent removes the child frame in a load
4660// event handler for the subframe.
Fergal Daly2e7e1e12020-06-24 09:18:284661IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, NavigateAboutBlankAndDetach) {
nasko13b8e772016-03-03 19:41:354662 GURL main_url(
4663 embedded_test_server()->GetURL("a.com", "/remove_frame_on_load.html"));
4664 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4665
4666 WebContents* contents = shell()->web_contents();
4667 FrameTreeNode* root =
Carlos Caballero15caeeb2021-10-27 09:57:554668 static_cast<WebContentsImpl*>(contents)->GetPrimaryFrameTree().root();
nasko13b8e772016-03-03 19:41:354669 EXPECT_EQ(1U, root->child_count());
4670 FrameTreeNode* child = root->child_at(0);
4671 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
4672 child->current_frame_host()->GetSiteInstance());
4673
alexmosf076d912017-01-23 22:27:574674 // Navigate the child frame to "about:blank" from the parent document and
4675 // wait for it to be removed.
Ria Jiang79f58ed922018-04-10 03:30:524676 FrameDeletedObserver observer(child->current_frame_host());
Avi Drissmanc91bd8e2021-04-19 23:58:444677 EXPECT_TRUE(
4678 ExecJs(root, base::StringPrintf("f.src = '%s'", url::kAboutBlankURL)));
nasko13b8e772016-03-03 19:41:354679 observer.Wait();
4680
4681 // Make sure the a.com renderer does not crash and the frame is removed.
Nick Carterb7e71312018-08-03 23:36:134682 EXPECT_EQ(0, EvalJs(root, "frames.length;"));
nasko13b8e772016-03-03 19:41:354683}
4684
naskoeab5c5582015-12-15 05:20:004685// This test ensures that the RenderFrame isn't leaked in the renderer process
4686// if a pending cross-process navigation is cancelled. The test works by trying
4687// to create a new RenderFrame with the same routing id. If there is an
4688// entry with the same routing ID, a CHECK is hit and the process crashes.
Fergal Daly2e7e1e12020-06-24 09:18:284689IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
naskoeab5c5582015-12-15 05:20:004690 SubframePendingAndBackToSameSiteInstance) {
4691 GURL main_url(embedded_test_server()->GetURL(
4692 "a.com", "/cross_site_iframe_factory.html?a(b)"));
davidsac6e6c35e42016-11-21 19:45:574693 EXPECT_TRUE(NavigateToURL(shell(), main_url));
naskoeab5c5582015-12-15 05:20:004694
4695 // Capture the FrameTreeNode this test will be navigating.
Carlos Caballero15caeeb2021-10-27 09:57:554696 FrameTreeNode* node =
4697 web_contents()->GetPrimaryFrameTree().root()->child_at(0);
naskoeab5c5582015-12-15 05:20:004698 EXPECT_TRUE(node);
4699 EXPECT_NE(node->current_frame_host()->GetSiteInstance(),
Alexander Timin381e7e182020-04-28 19:04:034700 node->parent()->GetSiteInstance());
naskoeab5c5582015-12-15 05:20:004701
4702 // Navigate to the site of the parent, but to a page that will not commit.
4703 GURL same_site_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
clamy0ffcb7d2018-09-17 13:24:244704 TestNavigationManager stalled_navigation(web_contents(), same_site_url);
naskoeab5c5582015-12-15 05:20:004705 {
4706 NavigationController::LoadURLParams params(same_site_url);
4707 params.transition_type = ui::PAGE_TRANSITION_LINK;
4708 params.frame_tree_node_id = node->frame_tree_node_id();
Carlos Caballero04aab362021-02-15 17:38:164709 node->navigator().controller().LoadURLWithParams(params);
clamy0ffcb7d2018-09-17 13:24:244710 EXPECT_TRUE(stalled_navigation.WaitForResponse());
naskoeab5c5582015-12-15 05:20:004711 }
4712
4713 // Grab the routing id of the pending RenderFrameHost and set up a process
4714 // observer to ensure there is no crash when a new RenderFrame creation is
4715 // attempted.
4716 RenderProcessHost* process =
clamy610c63b32017-12-22 15:05:184717 node->render_manager()->speculative_frame_host()->GetProcess();
Tal Pressman31a07372020-09-29 14:07:524718 AgentSchedulingGroupHost* agent_scheduling_group =
Sharon Yang57e10032021-11-24 22:02:374719 AgentSchedulingGroupHost::GetOrCreate(*node->render_manager()
4720 ->speculative_frame_host()
4721 ->GetSiteInstance()
4722 ->group(),
4723 *process);
naskoeab5c5582015-12-15 05:20:004724 RenderProcessHostWatcher watcher(
4725 process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
4726 int frame_routing_id =
clamy610c63b32017-12-22 15:05:184727 node->render_manager()->speculative_frame_host()->GetRoutingID();
Chris Hamilton2d147102021-02-22 20:44:204728 blink::LocalFrameToken frame_token =
Antonio Gomes5823ce62020-05-26 11:30:294729 node->render_manager()->speculative_frame_host()->GetFrameToken();
Dave Tapuskafd3d5ed2022-06-28 17:03:074730 blink::RemoteFrameToken previous_frame_token =
4731 node->render_manager()->GetProxyToParent()->GetFrameToken();
naskoeab5c5582015-12-15 05:20:004732
4733 // Now go to c.com so the navigation to a.com is cancelled and send an IPC
4734 // to create a new RenderFrame with the routing id of the previously pending
4735 // one.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:204736 EXPECT_TRUE(NavigateToURLFromRenderer(
4737 node, embedded_test_server()->GetURL("c.com", "/title2.html")));
naskoeab5c5582015-12-15 05:20:004738 {
danakj0bdfacd2021-01-20 19:27:184739 mojo::PendingAssociatedRemote<mojom::Frame> pending_frame;
4740
rockot53be7caf2016-10-04 20:17:084741 mojom::CreateFrameParamsPtr params = mojom::CreateFrameParams::New();
4742 params->routing_id = frame_routing_id;
danakj0bdfacd2021-01-20 19:27:184743 params->frame = pending_frame.InitWithNewEndpointAndPassReceiver();
Avi Drissman5d5d48d62022-01-07 20:23:584744 std::ignore = params->interface_broker.InitWithNewPipeAndPassReceiver();
Dominic Farolino12e06d72022-08-05 02:29:494745 std::ignore = params->associated_interface_provider_remote
4746 .InitWithNewEndpointAndPassReceiver();
Dave Tapuskafd3d5ed2022-06-28 17:03:074747 params->previous_frame_token = previous_frame_token;
Arthur Sonzognic686e8f2024-01-11 08:36:374748 params->opener_frame_token = std::nullopt;
Dave Tapuskafd3d5ed2022-06-28 17:03:074749 params->parent_frame_token =
4750 shell()->web_contents()->GetPrimaryMainFrame()->GetFrameToken();
Julie Jeongeun Kim70a2e4e2020-02-21 05:09:544751 params->frame_owner_properties = blink::mojom::FrameOwnerProperties::New();
Daniel Cheng284c38942022-09-22 23:30:344752 params->frame_token = frame_token;
Pavel Feldman25234722017-10-11 02:49:064753 params->devtools_frame_token = base::UnguessableToken::Create();
Daniel Cheng284c38942022-09-22 23:30:344754 params->document_token = blink::DocumentToken();
Antonio Sartori9290b6b2020-11-09 10:09:334755 params->policy_container = CreateStubPolicyContainer();
Gyuyoung Kimc16e52e92021-03-19 02:45:374756 params->replication_state = blink::mojom::FrameReplicationState::New();
Tal Pressman31a07372020-09-29 14:07:524757 agent_scheduling_group->CreateFrame(std::move(params));
naskoeab5c5582015-12-15 05:20:004758 }
4759
Sreeja Kamishettyce8d5942020-08-19 11:25:514760 // Disable the BackForwardCache to ensure the old process is going to be
4761 // released.
4762 DisableBackForwardCacheForTesting(web_contents(),
Rakina Zata Amni30af7062022-01-19 23:46:364763 BackForwardCache::TEST_REQUIRES_NO_CACHING);
Sreeja Kamishettyce8d5942020-08-19 11:25:514764
naskoeab5c5582015-12-15 05:20:004765 // The test must wait for the process to exit, but if there is no leak, the
4766 // RenderFrame will be properly created and there will be no crash.
4767 // Therefore, navigate the main frame to completely different site, which
4768 // will cause the original process to exit cleanly.
4769 EXPECT_TRUE(NavigateToURL(
4770 shell(), embedded_test_server()->GetURL("d.com", "/title3.html")));
4771 watcher.Wait();
4772 EXPECT_TRUE(watcher.did_exit_normally());
naskoeab5c5582015-12-15 05:20:004773}
4774
4775// This test ensures that the RenderFrame isn't leaked in the renderer process
4776// when a remote parent detaches a child frame. The test works by trying
4777// to create a new RenderFrame with the same routing id. If there is an
4778// entry with the same routing ID, a CHECK is hit and the process crashes.
Fergal Daly2e7e1e12020-06-24 09:18:284779IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, ParentDetachRemoteChild) {
naskoeab5c5582015-12-15 05:20:004780 GURL main_url(embedded_test_server()->GetURL(
4781 "a.com", "/cross_site_iframe_factory.html?a(b,b)"));
davidsac6e6c35e42016-11-21 19:45:574782 EXPECT_TRUE(NavigateToURL(shell(), main_url));
naskoeab5c5582015-12-15 05:20:004783
ekaramadfd1b5cfa2016-04-19 00:35:004784 WebContentsImpl* contents = web_contents();
Carlos Caballero15caeeb2021-10-27 09:57:554785 EXPECT_EQ(2U, contents->GetPrimaryFrameTree().root()->child_count());
naskoeab5c5582015-12-15 05:20:004786
4787 // Capture the FrameTreeNode this test will be navigating.
Carlos Caballero15caeeb2021-10-27 09:57:554788 FrameTreeNode* node = contents->GetPrimaryFrameTree().root()->child_at(0);
naskoeab5c5582015-12-15 05:20:004789 EXPECT_TRUE(node);
4790 EXPECT_NE(node->current_frame_host()->GetSiteInstance(),
Alexander Timin381e7e182020-04-28 19:04:034791 node->parent()->GetSiteInstance());
naskoeab5c5582015-12-15 05:20:004792
4793 // Grab the routing id of the first child RenderFrameHost and set up a process
4794 // observer to ensure there is no crash when a new RenderFrame creation is
4795 // attempted.
4796 RenderProcessHost* process = node->current_frame_host()->GetProcess();
Tal Pressman31a07372020-09-29 14:07:524797 AgentSchedulingGroupHost* agent_scheduling_group =
Dominic Farolino0e1f9a1a2020-11-25 23:25:004798 AgentSchedulingGroupHost::GetOrCreate(
Sharon Yang57e10032021-11-24 22:02:374799 *node->current_frame_host()->GetSiteInstance()->group(), *process);
naskoeab5c5582015-12-15 05:20:004800 RenderProcessHostWatcher watcher(
4801 process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
4802 int frame_routing_id = node->current_frame_host()->GetRoutingID();
Chris Hamilton2d147102021-02-22 20:44:204803 blink::LocalFrameToken frame_token =
Antonio Gomes5823ce62020-05-26 11:30:294804 node->current_frame_host()->GetFrameToken();
naskoeab5c5582015-12-15 05:20:004805 int widget_routing_id =
4806 node->current_frame_host()->GetRenderWidgetHost()->GetRoutingID();
Arthur Sonzognic686e8f2024-01-11 08:36:374807 std::optional<blink::FrameToken> parent_frame_token =
Alexander Timin381e7e182020-04-28 19:04:034808 node->parent()
4809 ->frame_tree_node()
4810 ->render_manager()
Dave Tapuskafd3d5ed2022-06-28 17:03:074811 ->GetFrameTokenForSiteInstanceGroup(
Sharon Yang571baee2022-03-18 19:01:544812 node->current_frame_host()->GetSiteInstance()->group());
naskoeab5c5582015-12-15 05:20:004813
4814 // Have the parent frame remove the child frame from its DOM. This should
4815 // result in the child RenderFrame being deleted in the remote process.
Avi Drissmanc91bd8e2021-04-19 23:58:444816 EXPECT_TRUE(ExecJs(contents,
4817 "document.body.removeChild("
4818 "document.querySelectorAll('iframe')[0])"));
Carlos Caballero15caeeb2021-10-27 09:57:554819 EXPECT_EQ(1U, contents->GetPrimaryFrameTree().root()->child_count());
naskoeab5c5582015-12-15 05:20:004820
4821 {
danakj0bdfacd2021-01-20 19:27:184822 mojo::PendingAssociatedRemote<mojom::Frame> pending_frame;
4823 mojo::PendingAssociatedRemote<blink::mojom::FrameWidget> blink_frame_widget;
4824 mojo::PendingAssociatedRemote<blink::mojom::Widget> blink_widget;
4825
rockot53be7caf2016-10-04 20:17:084826 mojom::CreateFrameParamsPtr params = mojom::CreateFrameParams::New();
4827 params->routing_id = frame_routing_id;
danakj0bdfacd2021-01-20 19:27:184828 params->frame = pending_frame.InitWithNewEndpointAndPassReceiver();
Avi Drissman5d5d48d62022-01-07 20:23:584829 std::ignore = params->interface_broker.InitWithNewPipeAndPassReceiver();
Dominic Farolino12e06d72022-08-05 02:29:494830 std::ignore = params->associated_interface_provider_remote
4831 .InitWithNewEndpointAndPassReceiver();
Arthur Sonzognic686e8f2024-01-11 08:36:374832 params->previous_frame_token = std::nullopt;
4833 params->opener_frame_token = std::nullopt;
Dave Tapuskafd3d5ed2022-06-28 17:03:074834 params->parent_frame_token = parent_frame_token;
Arthur Sonzognic686e8f2024-01-11 08:36:374835 params->previous_sibling_frame_token = std::nullopt;
Julie Jeongeun Kim70a2e4e2020-02-21 05:09:544836 params->frame_owner_properties = blink::mojom::FrameOwnerProperties::New();
rockot53be7caf2016-10-04 20:17:084837 params->widget_params = mojom::CreateFrameWidgetParams::New();
4838 params->widget_params->routing_id = widget_routing_id;
Dave Tapuska8499eec2020-03-16 17:54:304839 params->widget_params->frame_widget =
4840 blink_frame_widget.InitWithNewEndpointAndPassReceiver();
Dave Tapuska8499eec2020-03-16 17:54:304841 params->widget_params->widget =
4842 blink_widget.InitWithNewEndpointAndPassReceiver();
Avi Drissman5d5d48d62022-01-07 20:23:584843 std::ignore = params->widget_params->frame_widget_host
4844 .InitWithNewEndpointAndPassReceiver();
4845 std::ignore =
4846 params->widget_params->widget_host.InitWithNewEndpointAndPassReceiver();
Mike Wasserman15aa0f22021-02-27 01:55:524847 params->widget_params->visual_properties.screen_infos =
Adrienne Walkerfd7836b2021-06-18 00:10:144848 display::ScreenInfos(display::ScreenInfo());
Gyuyoung Kimc16e52e92021-03-19 02:45:374849 params->replication_state = blink::mojom::FrameReplicationState::New();
Antonio Sartori90f41212021-01-22 10:08:344850 params->replication_state->name = "name";
4851 params->replication_state->unique_name = "name";
Daniel Cheng284c38942022-09-22 23:30:344852 params->frame_token = frame_token;
Pavel Feldman25234722017-10-11 02:49:064853 params->devtools_frame_token = base::UnguessableToken::Create();
Daniel Cheng284c38942022-09-22 23:30:344854 params->document_token = blink::DocumentToken();
Antonio Sartori9290b6b2020-11-09 10:09:334855 params->policy_container = CreateStubPolicyContainer();
Tal Pressman31a07372020-09-29 14:07:524856 agent_scheduling_group->CreateFrame(std::move(params));
naskoeab5c5582015-12-15 05:20:004857 }
4858
4859 // The test must wait for the process to exit, but if there is no leak, the
4860 // RenderFrame will be properly created and there will be no crash.
4861 // Therefore, navigate the remaining subframe to completely different site,
4862 // which will cause the original process to exit cleanly.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:204863 EXPECT_TRUE(NavigateToURLFromRenderer(
Carlos Caballero15caeeb2021-10-27 09:57:554864 contents->GetPrimaryFrameTree().root()->child_at(0),
Lukasz Anforowicz69c25dfd2020-11-12 21:50:204865 embedded_test_server()->GetURL("d.com", "/title3.html")));
naskoeab5c5582015-12-15 05:20:004866 watcher.Wait();
4867 EXPECT_TRUE(watcher.did_exit_normally());
4868}
4869
alexmos6e940102016-01-19 22:47:254870// Verify that sandbox flags inheritance works across multiple levels of
4871// frames. See https://crbug.com/576845.
Fergal Daly2e7e1e12020-06-24 09:18:284872IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, SandboxFlagsInheritance) {
alexmos6e940102016-01-19 22:47:254873 GURL main_url(embedded_test_server()->GetURL(
4874 "a.com", "/cross_site_iframe_factory.html?a(a)"));
davidsac6e6c35e42016-11-21 19:45:574875 EXPECT_TRUE(NavigateToURL(shell(), main_url));
alexmos6e940102016-01-19 22:47:254876
4877 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:554878 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos6e940102016-01-19 22:47:254879
4880 // Set sandbox flags for child frame.
Avi Drissmanc91bd8e2021-04-19 23:58:444881 EXPECT_TRUE(ExecJs(
nickadef4a52016-06-09 18:45:544882 root, "document.querySelector('iframe').sandbox = 'allow-scripts';"));
alexmos6e940102016-01-19 22:47:254883
4884 // Calculate expected flags. Note that "allow-scripts" resets both
arthursonzognib93a4472020-04-10 07:38:004885 // network::mojom::WebSandboxFlags::Scripts and
4886 // network::mojom::WebSandboxFlags::AutomaticFeatures bits per
alexmos6e940102016-01-19 22:47:254887 // blink::parseSandboxPolicy().
arthursonzognib93a4472020-04-10 07:38:004888 network::mojom::WebSandboxFlags expected_flags =
4889 network::mojom::WebSandboxFlags::kAll &
4890 ~network::mojom::WebSandboxFlags::kScripts &
4891 ~network::mojom::WebSandboxFlags::kAutomaticFeatures;
Ian Clellandcdc4f312017-10-13 22:24:124892 EXPECT_EQ(expected_flags,
4893 root->child_at(0)->pending_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:004894 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:124895 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmos6e940102016-01-19 22:47:254896
4897 // Navigate child frame so that the sandbox flags take effect. Use a page
4898 // with three levels of frames and make sure all frames properly inherit
4899 // sandbox flags.
4900 GURL frame_url(embedded_test_server()->GetURL(
4901 "b.com", "/cross_site_iframe_factory.html?b(c(d))"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:204902 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url));
nasko8206fa12016-03-22 02:24:134903
alexmos6e940102016-01-19 22:47:254904 // Wait for subframes to load as well.
4905 ASSERT_TRUE(WaitForLoadStop(shell()->web_contents()));
4906
4907 // Check each new frame's sandbox flags on the browser process side.
4908 FrameTreeNode* b_child = root->child_at(0);
4909 FrameTreeNode* c_child = b_child->child_at(0);
4910 FrameTreeNode* d_child = c_child->child_at(0);
Ian Clellandcdc4f312017-10-13 22:24:124911 EXPECT_EQ(expected_flags, b_child->effective_frame_policy().sandbox_flags);
4912 EXPECT_EQ(expected_flags, c_child->effective_frame_policy().sandbox_flags);
4913 EXPECT_EQ(expected_flags, d_child->effective_frame_policy().sandbox_flags);
alexmos6e940102016-01-19 22:47:254914
4915 // Check whether each frame is sandboxed on the renderer side, by seeing if
4916 // each frame's origin is unique ("null").
Philip Jägenstedt67302a22018-09-14 09:58:054917 EXPECT_EQ("null", GetOriginFromRenderer(b_child));
4918 EXPECT_EQ("null", GetOriginFromRenderer(c_child));
4919 EXPECT_EQ("null", GetOriginFromRenderer(d_child));
alexmos6e940102016-01-19 22:47:254920}
4921
4922// Check that sandbox flags are not inherited before they take effect. Create
4923// a child frame, update its sandbox flags but don't navigate the frame, and
4924// ensure that a new cross-site grandchild frame doesn't inherit the new flags
4925// (which shouldn't have taken effect).
Fergal Daly2e7e1e12020-06-24 09:18:284926IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmos6e940102016-01-19 22:47:254927 SandboxFlagsNotInheritedBeforeNavigation) {
4928 GURL main_url(embedded_test_server()->GetURL(
4929 "a.com", "/cross_site_iframe_factory.html?a(a)"));
davidsac6e6c35e42016-11-21 19:45:574930 EXPECT_TRUE(NavigateToURL(shell(), main_url));
alexmos6e940102016-01-19 22:47:254931
4932 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:554933 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos6e940102016-01-19 22:47:254934
4935 // Set sandbox flags for child frame.
Avi Drissmanc91bd8e2021-04-19 23:58:444936 EXPECT_TRUE(ExecJs(
nickadef4a52016-06-09 18:45:544937 root, "document.querySelector('iframe').sandbox = 'allow-scripts';"));
alexmos6e940102016-01-19 22:47:254938
4939 // These flags should be pending but not take effect, since there's been no
4940 // navigation.
arthursonzognib93a4472020-04-10 07:38:004941 network::mojom::WebSandboxFlags expected_flags =
4942 network::mojom::WebSandboxFlags::kAll &
4943 ~network::mojom::WebSandboxFlags::kScripts &
4944 ~network::mojom::WebSandboxFlags::kAutomaticFeatures;
alexmos6e940102016-01-19 22:47:254945 FrameTreeNode* child = root->child_at(0);
Ian Clellandcdc4f312017-10-13 22:24:124946 EXPECT_EQ(expected_flags, child->pending_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:004947 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:124948 child->effective_frame_policy().sandbox_flags);
alexmos6e940102016-01-19 22:47:254949
4950 // Add a new grandchild frame and navigate it cross-site.
4951 RenderFrameHostCreatedObserver frame_observer(shell()->web_contents(), 1);
Avi Drissmanc91bd8e2021-04-19 23:58:444952 EXPECT_TRUE(ExecJs(
nickadef4a52016-06-09 18:45:544953 child, "document.body.appendChild(document.createElement('iframe'));"));
alexmos6e940102016-01-19 22:47:254954 frame_observer.Wait();
4955
4956 FrameTreeNode* grandchild = child->child_at(0);
4957 GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
4958 TestFrameNavigationObserver navigation_observer(grandchild);
Lukasz Anforowicz69c25dfd2020-11-12 21:50:204959 EXPECT_TRUE(NavigateToURLFromRenderer(grandchild, frame_url));
alexmos6e940102016-01-19 22:47:254960 navigation_observer.Wait();
4961
4962 // Since the update flags haven't yet taken effect in its parent, this
4963 // grandchild frame should not be sandboxed.
arthursonzognib93a4472020-04-10 07:38:004964 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:124965 grandchild->pending_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:004966 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:124967 grandchild->effective_frame_policy().sandbox_flags);
alexmos6e940102016-01-19 22:47:254968
4969 // Check that the grandchild frame isn't sandboxed on the renderer side. If
4970 // sandboxed, its origin would be unique ("null").
Lukasz Anforowicze9db2652021-01-27 00:24:124971 EXPECT_EQ(GetExpectedOrigin("b.com"), GetOriginFromRenderer(grandchild));
alexmos6e940102016-01-19 22:47:254972}
4973
alexmosaedfc6f2016-01-21 23:57:384974// Verify that popups opened from sandboxed frames inherit sandbox flags from
4975// their opener, and that they keep these inherited flags after being navigated
4976// cross-site. See https://crbug.com/483584.
Fergal Daly2e7e1e12020-06-24 09:18:284977IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmosaedfc6f2016-01-21 23:57:384978 NewPopupInheritsSandboxFlagsFromOpener) {
4979 GURL main_url(embedded_test_server()->GetURL(
4980 "a.com", "/cross_site_iframe_factory.html?a(a)"));
4981 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4982
4983 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:554984 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmosaedfc6f2016-01-21 23:57:384985
4986 // Set sandbox flags for child frame.
Nasko Oskov039121012019-01-11 00:21:324987 EXPECT_TRUE(ExecJs(root,
4988 "document.querySelector('iframe').sandbox = "
4989 " 'allow-scripts allow-popups';"));
alexmosaedfc6f2016-01-21 23:57:384990
4991 // Calculate expected flags. Note that "allow-scripts" resets both
arthursonzognib93a4472020-04-10 07:38:004992 // network::mojom::WebSandboxFlags::Scripts and
4993 // network::mojom::WebSandboxFlags::AutomaticFeatures bits per
alexmosaedfc6f2016-01-21 23:57:384994 // blink::parseSandboxPolicy().
arthursonzognib93a4472020-04-10 07:38:004995 network::mojom::WebSandboxFlags expected_flags =
4996 network::mojom::WebSandboxFlags::kAll &
arthursonzognib93a4472020-04-10 07:38:004997 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
Arthur Sonzogni4ba3104f2022-03-09 09:04:394998 ~network::mojom::WebSandboxFlags::kPopups &
4999 ~network::mojom::WebSandboxFlags::kScripts &
5000 ~network::mojom::WebSandboxFlags::kTopNavigationToCustomProtocols;
Ian Clellandcdc4f312017-10-13 22:24:125001 EXPECT_EQ(expected_flags,
5002 root->child_at(0)->pending_frame_policy().sandbox_flags);
alexmosaedfc6f2016-01-21 23:57:385003
5004 // Navigate child frame cross-site. The sandbox flags should take effect.
5005 GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
5006 TestFrameNavigationObserver frame_observer(root->child_at(0));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:205007 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url));
alexmosaedfc6f2016-01-21 23:57:385008 frame_observer.Wait();
Ian Clellandcdc4f312017-10-13 22:24:125009 EXPECT_EQ(expected_flags,
5010 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmosaedfc6f2016-01-21 23:57:385011
5012 // Verify that they've also taken effect on the renderer side. The sandboxed
Nasko Oskov039121012019-01-11 00:21:325013 // frame's origin should be opaque.
Philip Jägenstedt67302a22018-09-14 09:58:055014 EXPECT_EQ("null", GetOriginFromRenderer(root->child_at(0)));
Nasko Oskov039121012019-01-11 00:21:325015 const url::SchemeHostPort tuple_b(frame_url);
5016 const url::Origin sandbox_origin_b = root->child_at(0)->current_origin();
5017 EXPECT_TRUE(sandbox_origin_b.opaque());
5018 EXPECT_EQ(tuple_b, sandbox_origin_b.GetTupleOrPrecursorTupleIfOpaque());
alexmosaedfc6f2016-01-21 23:57:385019
5020 // Open a popup named "foo" from the sandboxed child frame.
nickadef4a52016-06-09 18:45:545021 Shell* foo_shell =
5022 OpenPopup(root->child_at(0), GURL(url::kAboutBlankURL), "foo");
alexmosaedfc6f2016-01-21 23:57:385023 EXPECT_TRUE(foo_shell);
5024
5025 FrameTreeNode* foo_root =
5026 static_cast<WebContentsImpl*>(foo_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:555027 ->GetPrimaryFrameTree()
5028 .root();
alexmosaedfc6f2016-01-21 23:57:385029
5030 // Check that the sandbox flags for new popup are correct in the browser
5031 // process.
Ian Clellandcdc4f312017-10-13 22:24:125032 EXPECT_EQ(expected_flags, foo_root->effective_frame_policy().sandbox_flags);
alexmosaedfc6f2016-01-21 23:57:385033
Nasko Oskov039121012019-01-11 00:21:325034 // The popup's origin should be opaque, since it's sandboxed, but cross-origin
5035 // from its opener.
Philip Jägenstedt67302a22018-09-14 09:58:055036 EXPECT_EQ("null", GetOriginFromRenderer(foo_root));
Nasko Oskov039121012019-01-11 00:21:325037 url::Origin sandbox_origin_b2 = foo_root->current_origin();
5038 EXPECT_NE(sandbox_origin_b2, sandbox_origin_b);
5039 EXPECT_TRUE(sandbox_origin_b2.opaque());
5040 EXPECT_EQ(tuple_b, sandbox_origin_b2.GetTupleOrPrecursorTupleIfOpaque());
alexmosaedfc6f2016-01-21 23:57:385041
Nasko Oskov039121012019-01-11 00:21:325042 // Navigate the popup cross-site. This should be placed in an opaque origin
5043 // derived from c.com, and retain the inherited sandbox flags.
alexmosaedfc6f2016-01-21 23:57:385044 GURL c_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
Nasko Oskov039121012019-01-11 00:21:325045 const url::SchemeHostPort tuple_c(c_url);
Alex Moshchuk1ef8e9e2017-08-30 18:26:525046 {
5047 TestFrameNavigationObserver popup_observer(foo_root);
Nasko Oskov039121012019-01-11 00:21:325048 EXPECT_TRUE(ExecJs(foo_root, JsReplace("location.href = $1", c_url)));
Alex Moshchuk1ef8e9e2017-08-30 18:26:525049 popup_observer.Wait();
5050 EXPECT_EQ(c_url, foo_shell->web_contents()->GetLastCommittedURL());
5051 }
5052
5053 // Confirm that the popup is still sandboxed, both on browser and renderer
5054 // sides.
Ian Clellandcdc4f312017-10-13 22:24:125055 EXPECT_EQ(expected_flags, foo_root->effective_frame_policy().sandbox_flags);
Philip Jägenstedt67302a22018-09-14 09:58:055056 EXPECT_EQ("null", GetOriginFromRenderer(foo_root));
Nasko Oskov039121012019-01-11 00:21:325057 const url::Origin sandbox_origin_c = foo_root->current_origin();
5058 EXPECT_NE(sandbox_origin_b, sandbox_origin_c);
5059 EXPECT_TRUE(sandbox_origin_c.opaque());
5060 EXPECT_EQ(tuple_c, sandbox_origin_c.GetTupleOrPrecursorTupleIfOpaque());
Alex Moshchuk1ef8e9e2017-08-30 18:26:525061
5062 // Navigate the popup back to b.com. The popup should perform a
Nasko Oskov039121012019-01-11 00:21:325063 // remote-to-local navigation in the b.com process, and keep an opaque
Alex Moshchuk1ef8e9e2017-08-30 18:26:525064 // origin and the inherited sandbox flags.
5065 {
5066 TestFrameNavigationObserver popup_observer(foo_root);
Nasko Oskov039121012019-01-11 00:21:325067 EXPECT_TRUE(ExecJs(foo_root, JsReplace("location.href = $1", frame_url)));
Alex Moshchuk1ef8e9e2017-08-30 18:26:525068 popup_observer.Wait();
5069 EXPECT_EQ(frame_url, foo_shell->web_contents()->GetLastCommittedURL());
5070 }
alexmosaedfc6f2016-01-21 23:57:385071
5072 // Confirm that the popup is still sandboxed, both on browser and renderer
Nasko Oskov039121012019-01-11 00:21:325073 // sides. This navigation should result in a new opaque origin derived
5074 // from b.com.
Ian Clellandcdc4f312017-10-13 22:24:125075 EXPECT_EQ(expected_flags, foo_root->effective_frame_policy().sandbox_flags);
Philip Jägenstedt67302a22018-09-14 09:58:055076 EXPECT_EQ("null", GetOriginFromRenderer(foo_root));
Nasko Oskov039121012019-01-11 00:21:325077 url::Origin sandbox_origin_b3 = foo_root->current_origin();
5078 EXPECT_TRUE(sandbox_origin_b3.opaque());
5079 EXPECT_EQ(tuple_b, sandbox_origin_b3.GetTupleOrPrecursorTupleIfOpaque());
5080 EXPECT_NE(sandbox_origin_b, sandbox_origin_b3);
5081 EXPECT_NE(sandbox_origin_b2, sandbox_origin_b3);
alexmosaedfc6f2016-01-21 23:57:385082}
5083
5084// Verify that popups opened from frames sandboxed with the
5085// "allow-popups-to-escape-sandbox" directive do *not* inherit sandbox flags
5086// from their opener.
Fergal Daly2e7e1e12020-06-24 09:18:285087IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmosaedfc6f2016-01-21 23:57:385088 OpenUnsandboxedPopupFromSandboxedFrame) {
5089 GURL main_url(embedded_test_server()->GetURL(
5090 "a.com", "/cross_site_iframe_factory.html?a(a)"));
5091 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5092
5093 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:555094 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmosaedfc6f2016-01-21 23:57:385095
5096 // Set sandbox flags for child frame, specifying that popups opened from it
5097 // should not be sandboxed.
Avi Drissmanc91bd8e2021-04-19 23:58:445098 EXPECT_TRUE(ExecJs(
nickadef4a52016-06-09 18:45:545099 root,
alexmosaedfc6f2016-01-21 23:57:385100 "document.querySelector('iframe').sandbox = "
5101 " 'allow-scripts allow-popups allow-popups-to-escape-sandbox';"));
5102
5103 // Set expected flags for the child frame. Note that "allow-scripts" resets
arthursonzognib93a4472020-04-10 07:38:005104 // both network::mojom::WebSandboxFlags::Scripts and
5105 // network::mojom::WebSandboxFlags::AutomaticFeatures bits per
5106 // blink::parseSandboxPolicy().
5107 network::mojom::WebSandboxFlags expected_flags =
5108 network::mojom::WebSandboxFlags::kAll &
5109 ~network::mojom::WebSandboxFlags::kScripts &
5110 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
5111 ~network::mojom::WebSandboxFlags::kPopups &
Arthur Sonzogni4ba3104f2022-03-09 09:04:395112 ~network::mojom::WebSandboxFlags::kTopNavigationToCustomProtocols &
arthursonzognib93a4472020-04-10 07:38:005113 ~network::mojom::WebSandboxFlags::kPropagatesToAuxiliaryBrowsingContexts;
Ian Clellandcdc4f312017-10-13 22:24:125114 EXPECT_EQ(expected_flags,
5115 root->child_at(0)->pending_frame_policy().sandbox_flags);
alexmosaedfc6f2016-01-21 23:57:385116
5117 // Navigate child frame cross-site. The sandbox flags should take effect.
5118 GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
5119 TestFrameNavigationObserver frame_observer(root->child_at(0));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:205120 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url));
alexmosaedfc6f2016-01-21 23:57:385121 frame_observer.Wait();
Ian Clellandcdc4f312017-10-13 22:24:125122 EXPECT_EQ(expected_flags,
5123 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmosaedfc6f2016-01-21 23:57:385124
5125 // Open a cross-site popup named "foo" from the child frame.
5126 GURL b_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
nickadef4a52016-06-09 18:45:545127 Shell* foo_shell = OpenPopup(root->child_at(0), b_url, "foo");
alexmosaedfc6f2016-01-21 23:57:385128 EXPECT_TRUE(foo_shell);
5129
5130 FrameTreeNode* foo_root =
5131 static_cast<WebContentsImpl*>(foo_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:555132 ->GetPrimaryFrameTree()
5133 .root();
alexmosaedfc6f2016-01-21 23:57:385134
5135 // Check that the sandbox flags for new popup are correct in the browser
5136 // process. They should not have been inherited.
arthursonzognib93a4472020-04-10 07:38:005137 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:125138 foo_root->effective_frame_policy().sandbox_flags);
Pâris Meuleman687f7002022-02-16 11:11:345139 // Check that the sandbox flags for the popup document are correct in the
5140 // browser process: None are set from the frame, none are set from the
5141 // navigation.
5142 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
5143 foo_root->current_frame_host()->active_sandbox_flags());
alexmosaedfc6f2016-01-21 23:57:385144
5145 // The popup's origin should match |b_url|, since it's not sandboxed.
Nick Carterb7e71312018-08-03 23:36:135146 EXPECT_EQ(url::Origin::Create(b_url).Serialize(),
Philip Jägenstedt67302a22018-09-14 09:58:055147 EvalJs(foo_root, "self.origin;"));
alexmosaedfc6f2016-01-21 23:57:385148}
5149
Pâris Meuleman687f7002022-02-16 11:11:345150// Verify that popup frames opened from sandboxed documents with the
5151// "allow-popups-to-escape-sandbox" directive do *not* inherit sandbox flags AND
Pâris Meulemana8a41022022-05-12 13:01:585152// that local scheme documents do *not* inherit flags from the opener/initiator.
Pâris Meuleman687f7002022-02-16 11:11:345153IN_PROC_BROWSER_TEST_P(
5154 SitePerProcessBrowserTest,
5155 OpenSandboxedDocumentInUnsandboxedPopupFromSandboxedFrame) {
5156 GURL main_url(embedded_test_server()->GetURL(
5157 "a.com", "/cross_site_iframe_factory.html?a(a)"));
5158 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5159
5160 // It is safe to obtain the root frame tree node here, as it doesn't change.
5161 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
5162
5163 // Set sandbox flags for child frame, specifying that popups opened from it
5164 // should not be sandboxed.
5165 EXPECT_TRUE(ExecJs(
5166 root,
5167 "document.querySelector('iframe').sandbox = "
5168 " 'allow-scripts allow-popups allow-popups-to-escape-sandbox';"));
5169
5170 // Set expected flags for the child frame. Note that "allow-scripts" resets
5171 // both network::mojom::WebSandboxFlags::Scripts and
5172 // network::mojom::WebSandboxFlags::AutomaticFeatures bits per
5173 // blink::parseSandboxPolicy().
5174 network::mojom::WebSandboxFlags expected_flags =
5175 network::mojom::WebSandboxFlags::kAll &
5176 ~network::mojom::WebSandboxFlags::kScripts &
5177 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
5178 ~network::mojom::WebSandboxFlags::kPopups &
Arthur Sonzogni4ba3104f2022-03-09 09:04:395179 ~network::mojom::WebSandboxFlags::kTopNavigationToCustomProtocols &
Pâris Meuleman687f7002022-02-16 11:11:345180 ~network::mojom::WebSandboxFlags::kPropagatesToAuxiliaryBrowsingContexts;
5181 EXPECT_EQ(expected_flags,
5182 root->child_at(0)->pending_frame_policy().sandbox_flags);
5183
5184 // Navigate child frame cross-site. The sandbox flags should take effect.
5185 GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
5186 TestFrameNavigationObserver frame_observer(root->child_at(0));
5187 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url));
5188 frame_observer.Wait();
5189 EXPECT_EQ(expected_flags,
5190 root->child_at(0)->effective_frame_policy().sandbox_flags);
5191
Pâris Meulemana8a41022022-05-12 13:01:585192 // Open a popup named "foo" from the child frame on about:blank.
5193 GURL foo_url("about:blank");
5194 Shell* foo_shell = OpenPopup(root->child_at(0), foo_url, "foo");
Pâris Meuleman687f7002022-02-16 11:11:345195 EXPECT_TRUE(foo_shell);
5196
5197 FrameTreeNode* foo_root =
5198 static_cast<WebContentsImpl*>(foo_shell->web_contents())
5199 ->GetPrimaryFrameTree()
5200 .root();
5201
5202 // Check that the sandbox flags for new popup frame are correct in the browser
5203 // process. They should not have been inherited.
5204 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
5205 foo_root->effective_frame_policy().sandbox_flags);
5206 // Check that the sandbox flags for the popup document are correct in the
Pâris Meulemana8a41022022-05-12 13:01:585207 // browser process. They should not have been inherited (for about:blank).
5208 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
5209 foo_root->current_frame_host()->active_sandbox_flags());
5210}
5211
5212// Verify that popup frames opened from sandboxed documents with the
5213// "allow-popups-to-escape-sandbox" directive do *not* inherit sandbox flags AND
5214// that local scheme documents do inherit CSP sandbox flags from the
5215// opener/initiator.
5216IN_PROC_BROWSER_TEST_P(
5217 SitePerProcessBrowserTest,
5218 OpenSandboxedDocumentInUnsandboxedPopupFromCSPSandboxedDocument) {
5219 GURL main_url = embedded_test_server()->GetURL(
5220 "a.test",
5221 "/set-header?"
5222 "Content-Security-Policy: sandbox "
5223 "allow-scripts allow-popups allow-popups-to-escape-sandbox");
5224
5225 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5226
5227 // It is safe to obtain the root frame tree node here, as it doesn't change.
5228 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
5229
5230 // Set expected flags for the child frame. Note that "allow-scripts" resets
5231 // both network::mojom::WebSandboxFlags::Scripts and
5232 // network::mojom::WebSandboxFlags::AutomaticFeatures bits per
5233 // blink::parseSandboxPolicy().
5234 network::mojom::WebSandboxFlags expected_flags =
5235 network::mojom::WebSandboxFlags::kAll &
5236 ~network::mojom::WebSandboxFlags::kScripts &
5237 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
5238 ~network::mojom::WebSandboxFlags::kPopups &
5239 ~network::mojom::WebSandboxFlags::kTopNavigationToCustomProtocols &
5240 ~network::mojom::WebSandboxFlags::kPropagatesToAuxiliaryBrowsingContexts;
5241
5242 EXPECT_EQ(expected_flags, root->current_frame_host()->active_sandbox_flags());
5243
5244 // Open a popup named "foo" from the child frame on about:blank.
5245 GURL foo_url("about:blank");
5246 Shell* foo_shell = OpenPopup(root, foo_url, "foo");
5247 EXPECT_TRUE(foo_shell);
5248
5249 FrameTreeNode* foo_root =
5250 static_cast<WebContentsImpl*>(foo_shell->web_contents())
5251 ->GetPrimaryFrameTree()
5252 .root();
5253
5254 // Check that the sandbox flags for new popup frame are correct in the browser
5255 // process. They should not have been inherited.
5256 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
5257 foo_root->effective_frame_policy().sandbox_flags);
5258 // Check that the sandbox flags for the popup document are correct in the
5259 // browser process. They should have been inherited.
Pâris Meuleman3eeb90b92022-05-18 09:33:505260 EXPECT_EQ(expected_flags,
Pâris Meuleman687f7002022-02-16 11:11:345261 foo_root->current_frame_host()->active_sandbox_flags());
5262}
5263
estarkcd2e30c2016-08-12 06:51:155264// Test that subresources with certificate errors get reported to the
5265// browser. That is, if https://example.test frames https://a.com which
5266// loads an image with certificate errors, the browser should be
5267// notified about the subresource with certificate errors and downgrade
5268// the UI appropriately.
Alison Gale81f4f2c72024-04-22 19:33:315269// TODO(crbug.com/40705650): Flaky.
Fergal Daly2e7e1e12020-06-24 09:18:285270IN_PROC_BROWSER_TEST_P(SitePerProcessIgnoreCertErrorsBrowserTest,
Morten Stenshorne47fdd002020-08-03 11:42:005271 DISABLED_SubresourceWithCertificateErrors) {
estarkaeda1422016-05-10 02:31:125272 net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
Alan Cutterfb5689192019-03-28 08:39:195273 https_server.ServeFilesFromSourceDirectory(GetTestDataFilePath());
estarkaeda1422016-05-10 02:31:125274 SetupCrossSiteRedirector(&https_server);
martijna4dce162016-11-16 17:25:045275 ASSERT_TRUE(https_server.Start());
estarkaeda1422016-05-10 02:31:125276
5277 GURL url(https_server.GetURL(
estarkcd2e30c2016-08-12 06:51:155278 "example.test",
estarkaeda1422016-05-10 02:31:125279 "/mixed-content/non-redundant-cert-error-in-iframe.html"));
Pavol Markof2575552020-07-25 18:19:135280
5281 // The update of the security state can happen asynchronously after the
5282 // navigation finished, see https://crbug.com/1105145.
5283 VisibleSecurityStateObserver displayed_content_with_cert_errors_observer(
5284 shell()->web_contents(),
5285 base::BindRepeating([](WebContents* web_contents) {
5286 NavigationEntry* entry =
5287 web_contents->GetController().GetLastCommittedEntry();
5288 // The image that the iframe loaded had certificate errors also, so
5289 // the page should be marked as having displayed subresources with
5290 // cert errors.
5291 return entry && (entry->GetSSL().content_status &
5292 SSLStatus::DISPLAYED_CONTENT_WITH_CERT_ERRORS) != 0;
5293 }));
estarkaeda1422016-05-10 02:31:125294 EXPECT_TRUE(NavigateToURL(shell(), url));
Pavol Markof2575552020-07-25 18:19:135295 displayed_content_with_cert_errors_observer.Wait();
estarkaeda1422016-05-10 02:31:125296
5297 NavigationEntry* entry =
5298 shell()->web_contents()->GetController().GetLastCommittedEntry();
5299 ASSERT_TRUE(entry);
5300
5301 // The main page was loaded with certificate errors.
estark47ba9c7d2016-10-11 15:40:115302 EXPECT_TRUE(net::IsCertStatusError(entry->GetSSL().cert_status));
estarkaeda1422016-05-10 02:31:125303}
5304
lfg43e08e62016-02-03 18:51:375305// Test setting a cross-origin iframe to display: none.
Fergal Daly2e7e1e12020-06-24 09:18:285306IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, CrossSiteIframeDisplayNone) {
lfg43e08e62016-02-03 18:51:375307 GURL main_url(embedded_test_server()->GetURL(
5308 "a.com", "/cross_site_iframe_factory.html?a(b)"));
davidsac6e6c35e42016-11-21 19:45:575309 EXPECT_TRUE(NavigateToURL(shell(), main_url));
lfg43e08e62016-02-03 18:51:375310
Carlos Caballero15caeeb2021-10-27 09:57:555311 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
lfg43e08e62016-02-03 18:51:375312 RenderWidgetHost* root_render_widget_host =
5313 root->current_frame_host()->GetRenderWidgetHost();
5314
5315 // Set the iframe to display: none.
Avi Drissmanc91bd8e2021-04-19 23:58:445316 EXPECT_TRUE(ExecJs(
nickadef4a52016-06-09 18:45:545317 shell(), "document.querySelector('iframe').style.display = 'none'"));
lfg43e08e62016-02-03 18:51:375318
5319 // Waits until pending frames are done.
dcheng59716272016-04-09 05:19:085320 std::unique_ptr<MainThreadFrameObserver> observer(
lfg43e08e62016-02-03 18:51:375321 new MainThreadFrameObserver(root_render_widget_host));
5322 observer->Wait();
5323
5324 // Force the renderer to generate a new frame.
Avi Drissmanc91bd8e2021-04-19 23:58:445325 EXPECT_TRUE(ExecJs(shell(), "document.body.style.background = 'black'"));
lfg43e08e62016-02-03 18:51:375326
5327 // Waits for the next frame.
5328 observer->Wait();
5329}
5330
alexmos4170f6e82016-03-03 22:42:045331// Test that a cross-origin iframe can be blocked by X-Frame-Options and CSP
5332// frame-ancestors.
Fergal Daly2e7e1e12020-06-24 09:18:285333IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmos4170f6e82016-03-03 22:42:045334 CrossSiteIframeBlockedByXFrameOptionsOrCSP) {
5335 GURL main_url(embedded_test_server()->GetURL(
5336 "a.com", "/cross_site_iframe_factory.html?a(a)"));
davidsac6e6c35e42016-11-21 19:45:575337 EXPECT_TRUE(NavigateToURL(shell(), main_url));
alexmos4170f6e82016-03-03 22:42:045338
Carlos Caballero15caeeb2021-10-27 09:57:555339 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos4170f6e82016-03-03 22:42:045340
5341 // Add a load event handler for the iframe element.
Avi Drissmanc91bd8e2021-04-19 23:58:445342 EXPECT_TRUE(ExecJs(shell(),
5343 "document.querySelector('iframe').onload = "
5344 " function() { document.title = 'loaded'; };"));
alexmos4170f6e82016-03-03 22:42:045345
Antonio Sartori93ce5602020-11-18 09:33:555346 // The blocked url reported in the console message should only contain the
5347 // origin, in order to avoid sensitive data being leaked to the parent frame.
5348 //
Alison Gale59c007a2024-04-20 03:05:405349 // TODO(crbug.com/40053800): We should not leak any information at all
Antonio Sartori93ce5602020-11-18 09:33:555350 // to the parent frame. Instead, we should send a message directly to Devtools
5351 // (without passing through a renderer): that can also contain more
5352 // information (like the full blocked url).
5353 GURL reported_blocked_url = embedded_test_server()->GetURL("b.com", "/");
Arthur Sonzogni144eff602018-08-13 10:37:155354 const struct {
5355 const char* url;
5356 bool use_error_page;
Antonio Sartori93ce5602020-11-18 09:33:555357 std::string expected_console_message;
Arthur Sonzogni144eff602018-08-13 10:37:155358 } kTestCases[] = {
Antonio Sartori93ce5602020-11-18 09:33:555359 {"/frame-ancestors-none.html", false,
5360 "Refused to frame '" + reported_blocked_url.spec() +
5361 "' because an ancestor violates the following Content Security "
5362 "Policy directive: \"frame-ancestors 'none'\".\n"},
5363 {"/x-frame-options-deny.html", true,
5364 "Refused to display '" + reported_blocked_url.spec() +
5365 "' in a frame because it set 'X-Frame-Options' to 'deny'."},
alexmos4170f6e82016-03-03 22:42:045366 };
5367
Arthur Sonzogni144eff602018-08-13 10:37:155368 for (const auto& test : kTestCases) {
5369 GURL blocked_url = embedded_test_server()->GetURL("b.com", test.url);
Avi Drissmanc91bd8e2021-04-19 23:58:445370 EXPECT_TRUE(ExecJs(shell(), "document.title = 'not loaded';"));
Jan Wilken Dörrie2c470ea2021-03-22 22:26:245371 std::u16string expected_title(u"loaded");
alexmos4170f6e82016-03-03 22:42:045372 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
5373
Antonio Sartori93ce5602020-11-18 09:33:555374 WebContentsConsoleObserver console_observer(shell()->web_contents());
5375 console_observer.SetPattern("Refused to*");
5376
alexmos4170f6e82016-03-03 22:42:045377 // Navigate the subframe to a blocked URL.
5378 TestNavigationObserver load_observer(shell()->web_contents());
Avi Drissmanc91bd8e2021-04-19 23:58:445379 EXPECT_TRUE(ExecJs(shell(),
5380 JsReplace("frames[0].location.href = $1", blocked_url)));
alexmos4170f6e82016-03-03 22:42:045381 load_observer.Wait();
5382
5383 // The blocked frame's origin should become unique.
Aaron Colwella35c84f2019-09-06 08:33:295384 const url::Origin child_origin =
5385 root->child_at(0)->current_frame_host()->GetLastCommittedOrigin();
5386 EXPECT_TRUE(child_origin.opaque());
Mike West800532c2021-10-14 09:26:525387 EXPECT_EQ(url::Origin::Create(blocked_url.DeprecatedGetOriginAsURL())
Aaron Colwella35c84f2019-09-06 08:33:295388 .GetTupleOrPrecursorTupleIfOpaque(),
5389 child_origin.GetTupleOrPrecursorTupleIfOpaque());
alexmos4170f6e82016-03-03 22:42:045390
Arthur Sonzogni144eff602018-08-13 10:37:155391 // X-Frame-Options and CSP frame-ancestors behave differently. XFO commits
5392 // an error page, while CSP commits a "data:," URL.
Alison Galed94ce4f2024-04-22 15:20:395393 // TODO(crbug.com/41405925): Use an error page for both.
Lucas Furukawa Gadani878c61b2020-06-22 17:58:085394 EXPECT_FALSE(load_observer.last_navigation_succeeded());
5395 EXPECT_EQ(net::ERR_BLOCKED_BY_RESPONSE,
5396 load_observer.last_net_error_code());
5397 EXPECT_EQ(root->child_at(0)->current_frame_host()->GetLastCommittedURL(),
5398 blocked_url);
5399 EXPECT_EQ("Error", EvalJs(root->child_at(0), "document.title"));
alexmos30535f72016-06-23 18:48:235400
alexmos4170f6e82016-03-03 22:42:045401 // The blocked frame should still fire a load event in its parent's process.
5402 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
5403
Antonio Sartori93ce5602020-11-18 09:33:555404 EXPECT_EQ(console_observer.GetMessageAt(0u), test.expected_console_message);
5405
alexmos4170f6e82016-03-03 22:42:045406 // Check that the current RenderFrameHost has stopped loading.
5407 EXPECT_FALSE(root->child_at(0)->current_frame_host()->is_loading());
5408
alexmos4170f6e82016-03-03 22:42:045409 // Navigate the subframe to another cross-origin page and ensure that this
5410 // navigation succeeds. Use a renderer-initiated navigation to test the
5411 // transfer logic, which used to have some issues with this.
5412 GURL c_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
5413 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "child-0", c_url));
5414 EXPECT_EQ(c_url, root->child_at(0)->current_url());
5415
5416 // When a page gets blocked due to XFO or CSP, it is sandboxed with the
5417 // SandboxOrigin flag (i.e., its origin is set to be unique) to ensure that
5418 // the blocked page is seen as cross-origin. However, those flags shouldn't
5419 // affect future navigations for a frame. Verify this for the above
5420 // navigation.
Mike West800532c2021-10-14 09:26:525421 EXPECT_EQ(c_url.DeprecatedGetOriginAsURL().spec(),
alexmos4170f6e82016-03-03 22:42:045422 root->child_at(0)->current_origin().Serialize() + "/");
arthursonzognib93a4472020-04-10 07:38:005423 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:125424 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmos4170f6e82016-03-03 22:42:045425 }
5426}
5427
lukasza8e1c02e42016-05-17 20:05:105428// Test that a cross-origin frame's navigation can be blocked by CSP frame-src.
5429// In this version of a test, CSP comes from HTTP headers.
Fergal Daly2e7e1e12020-06-24 09:18:285430IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
lukasza8e1c02e42016-05-17 20:05:105431 CrossSiteIframeBlockedByParentCSPFromHeaders) {
5432 GURL main_url(
5433 embedded_test_server()->GetURL("a.com", "/frame-src-self-and-b.html"));
5434 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5435
Carlos Caballero15caeeb2021-10-27 09:57:555436 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
lukasza8e1c02e42016-05-17 20:05:105437
5438 // Sanity-check that the test page has the expected shape for testing.
5439 GURL old_subframe_url(
5440 embedded_test_server()->GetURL("b.com", "/title2.html"));
5441 EXPECT_FALSE(root->child_at(0)->HasSameOrigin(*root));
5442 EXPECT_EQ(old_subframe_url, root->child_at(0)->current_url());
Antonio Sartori8f5b7ee2021-01-29 13:00:545443 const std::vector<network::mojom::ContentSecurityPolicyPtr>& root_csp =
Antonio Sartorif45b2fc2021-03-04 10:15:075444 root->current_frame_host()
5445 ->policy_container_host()
5446 ->policies()
5447 .content_security_policies;
lukasza8e1c02e42016-05-17 20:05:105448 EXPECT_EQ(1u, root_csp.size());
Antonio Sartori8f5b7ee2021-01-29 13:00:545449 EXPECT_EQ("frame-src 'self' http://b.com:*",
5450 root_csp[0]->header->header_value);
lukasza8e1c02e42016-05-17 20:05:105451
5452 // Monitor subframe's load events via main frame's title.
Avi Drissmanc91bd8e2021-04-19 23:58:445453 EXPECT_TRUE(ExecJs(shell(),
5454 "document.querySelector('iframe').onload = "
5455 " function() { document.title = 'loaded'; };"));
5456 EXPECT_TRUE(ExecJs(shell(), "document.title = 'not loaded';"));
Jan Wilken Dörrie2c470ea2021-03-22 22:26:245457 std::u16string expected_title(u"loaded");
lukasza8e1c02e42016-05-17 20:05:105458 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
5459
5460 // Try to navigate the subframe to a blocked URL.
5461 TestNavigationObserver load_observer(shell()->web_contents());
5462 GURL blocked_url = embedded_test_server()->GetURL("c.com", "/title3.html");
Avi Drissmanc91bd8e2021-04-19 23:58:445463 EXPECT_TRUE(ExecJs(root->child_at(0),
5464 JsReplace("window.location.href = $1", blocked_url)));
lukasza8e1c02e42016-05-17 20:05:105465
5466 // The blocked frame should still fire a load event in its parent's process.
5467 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
5468
5469 // Check that the current RenderFrameHost has stopped loading.
Nasko Oskov63dda2952018-02-09 19:35:425470 if (root->child_at(0)->current_frame_host()->is_loading())
lukasza8e1c02e42016-05-17 20:05:105471 load_observer.Wait();
lukasza8e1c02e42016-05-17 20:05:105472
arthursonzogni7fed384c2017-03-18 03:07:345473 // The last successful url shouldn't be the blocked url.
Nasko Oskov0401d812021-02-05 22:20:085474 EXPECT_NE(blocked_url,
arthursonzogni7fed384c2017-03-18 03:07:345475 root->child_at(0)->current_frame_host()->last_successful_url());
lukasza8e1c02e42016-05-17 20:05:105476
Nasko Oskov63dda2952018-02-09 19:35:425477 // The blocked frame should go to an error page. Errors currently commit
5478 // with the URL of the blocked page.
5479 EXPECT_EQ(blocked_url, root->child_at(0)->current_url());
arthursonzogni7fed384c2017-03-18 03:07:345480
Nasko Oskov63dda2952018-02-09 19:35:425481 // The page should get the title of an error page (i.e "Error") and not the
5482 // title of the blocked page.
Nick Carterb7e71312018-08-03 23:36:135483 EXPECT_EQ("Error", EvalJs(root->child_at(0), "document.title"));
lukasza8e1c02e42016-05-17 20:05:105484
5485 // Navigate to a URL without CSP.
5486 EXPECT_TRUE(NavigateToURL(
5487 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
lukasza8e1c02e42016-05-17 20:05:105488}
5489
5490// Test that a cross-origin frame's navigation can be blocked by CSP frame-src.
5491// In this version of a test, CSP comes from a <meta> element added after the
5492// page has already loaded.
Fergal Daly2e7e1e12020-06-24 09:18:285493IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
lukasza8e1c02e42016-05-17 20:05:105494 CrossSiteIframeBlockedByParentCSPFromMeta) {
5495 GURL main_url(embedded_test_server()->GetURL(
5496 "a.com", "/cross_site_iframe_factory.html?a(a)"));
5497 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5498
Carlos Caballero15caeeb2021-10-27 09:57:555499 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
lukasza8e1c02e42016-05-17 20:05:105500
5501 // Navigate the subframe to a location we will disallow in the future.
5502 GURL old_subframe_url(
5503 embedded_test_server()->GetURL("b.com", "/title2.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:205504 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), old_subframe_url));
lukasza8e1c02e42016-05-17 20:05:105505
5506 // Add frame-src CSP via a new <meta> element.
Nick Carterb7e71312018-08-03 23:36:135507 EXPECT_TRUE(
5508 ExecJs(shell(),
5509 "var meta = document.createElement('meta');"
5510 "meta.httpEquiv = 'Content-Security-Policy';"
5511 "meta.content = 'frame-src https://a.com:*';"
5512 "document.getElementsByTagName('head')[0].appendChild(meta);"));
lukasza8e1c02e42016-05-17 20:05:105513
5514 // Sanity-check that the test page has the expected shape for testing.
5515 // (the CSP should not have an effect on the already loaded frames).
5516 EXPECT_FALSE(root->child_at(0)->HasSameOrigin(*root));
5517 EXPECT_EQ(old_subframe_url, root->child_at(0)->current_url());
Antonio Sartori8f5b7ee2021-01-29 13:00:545518 const std::vector<network::mojom::ContentSecurityPolicyPtr>& root_csp =
Antonio Sartorif45b2fc2021-03-04 10:15:075519 root->current_frame_host()
5520 ->policy_container_host()
5521 ->policies()
5522 .content_security_policies;
lukasza8e1c02e42016-05-17 20:05:105523 EXPECT_EQ(1u, root_csp.size());
Antonio Sartori8f5b7ee2021-01-29 13:00:545524 EXPECT_EQ("frame-src https://a.com:*", root_csp[0]->header->header_value);
lukasza8e1c02e42016-05-17 20:05:105525
5526 // Monitor subframe's load events via main frame's title.
Nick Carterb7e71312018-08-03 23:36:135527 EXPECT_TRUE(ExecJs(shell(),
5528 "document.querySelector('iframe').onload = "
5529 " function() { document.title = 'loaded'; };"));
5530 EXPECT_TRUE(ExecJs(shell(), "document.title = 'not loaded';"));
Jan Wilken Dörrie2c470ea2021-03-22 22:26:245531 std::u16string expected_title(u"loaded");
lukasza8e1c02e42016-05-17 20:05:105532 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
5533
5534 // Try to navigate the subframe to a blocked URL.
5535 TestNavigationObserver load_observer2(shell()->web_contents());
5536 GURL blocked_url = embedded_test_server()->GetURL("c.com", "/title3.html");
Nick Carterb7e71312018-08-03 23:36:135537 EXPECT_TRUE(ExecJs(root->child_at(0),
5538 JsReplace("window.location.href = $1;", blocked_url)));
lukasza8e1c02e42016-05-17 20:05:105539
5540 // The blocked frame should still fire a load event in its parent's process.
5541 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
5542
5543 // Check that the current RenderFrameHost has stopped loading.
Nasko Oskov63dda2952018-02-09 19:35:425544 if (root->child_at(0)->current_frame_host()->is_loading())
lukasza8e1c02e42016-05-17 20:05:105545 load_observer2.Wait();
lukasza8e1c02e42016-05-17 20:05:105546
arthursonzogni7fed384c2017-03-18 03:07:345547 // The last successful url shouldn't be the blocked url.
Nasko Oskov0401d812021-02-05 22:20:085548 EXPECT_NE(blocked_url,
arthursonzogni7fed384c2017-03-18 03:07:345549 root->child_at(0)->current_frame_host()->last_successful_url());
lukasza8e1c02e42016-05-17 20:05:105550
Nasko Oskov63dda2952018-02-09 19:35:425551 // The blocked frame should go to an error page. Errors currently commit
5552 // with the URL of the blocked page.
5553 EXPECT_EQ(blocked_url, root->child_at(0)->current_url());
arthursonzogni7fed384c2017-03-18 03:07:345554
Nasko Oskov63dda2952018-02-09 19:35:425555 // The page should get the title of an error page (i.e "Error") and not the
5556 // title of the blocked page.
Nick Carterb7e71312018-08-03 23:36:135557 EXPECT_EQ("Error", EvalJs(root->child_at(0), "document.title"));
lukasza8e1c02e42016-05-17 20:05:105558}
5559
5560// Test that a cross-origin frame's navigation can be blocked by CSP frame-src.
5561// In this version of a test, CSP is inherited by srcdoc iframe from a parent
5562// that declared CSP via HTTP headers. Cross-origin frame navigating to a
5563// blocked location is a child of the srcdoc iframe.
Fergal Daly2e7e1e12020-06-24 09:18:285564IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
lukasza8e1c02e42016-05-17 20:05:105565 CrossSiteIframeBlockedByCSPInheritedBySrcDocParent) {
5566 GURL main_url(
5567 embedded_test_server()->GetURL("a.com", "/frame-src-self-and-b.html"));
5568 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5569
Carlos Caballero15caeeb2021-10-27 09:57:555570 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
lukasza8e1c02e42016-05-17 20:05:105571 FrameTreeNode* srcdoc_frame = root->child_at(1);
5572 EXPECT_TRUE(srcdoc_frame != nullptr);
5573 FrameTreeNode* navigating_frame = srcdoc_frame->child_at(0);
5574 EXPECT_TRUE(navigating_frame != nullptr);
5575
5576 // Sanity-check that the test page has the expected shape for testing.
5577 // (the CSP should not have an effect on the already loaded frames).
5578 GURL old_subframe_url(
5579 embedded_test_server()->GetURL("b.com", "/title2.html"));
5580 EXPECT_TRUE(srcdoc_frame->HasSameOrigin(*root));
5581 EXPECT_FALSE(srcdoc_frame->HasSameOrigin(*navigating_frame));
5582 EXPECT_EQ(old_subframe_url, navigating_frame->current_url());
Antonio Sartori8f5b7ee2021-01-29 13:00:545583 const std::vector<network::mojom::ContentSecurityPolicyPtr>& srcdoc_csp =
Antonio Sartorif45b2fc2021-03-04 10:15:075584 srcdoc_frame->current_frame_host()
5585 ->policy_container_host()
5586 ->policies()
5587 .content_security_policies;
lukasza8e1c02e42016-05-17 20:05:105588 EXPECT_EQ(1u, srcdoc_csp.size());
Antonio Sartori8f5b7ee2021-01-29 13:00:545589 EXPECT_EQ("frame-src 'self' http://b.com:*",
5590 srcdoc_csp[0]->header->header_value);
lukasza8e1c02e42016-05-17 20:05:105591
5592 // Monitor navigating_frame's load events via srcdoc_frame posting
5593 // a message to the parent frame.
Nick Carterb7e71312018-08-03 23:36:135594 EXPECT_TRUE(ExecJs(root,
5595 "window.addEventListener('message', function(event) {"
5596 " document.title = event.data;"
5597 "});"));
lukasza8e1c02e42016-05-17 20:05:105598 EXPECT_TRUE(
Nick Carterb7e71312018-08-03 23:36:135599 ExecJs(srcdoc_frame,
5600 "document.querySelector('iframe').onload = "
5601 " function() { window.top.postMessage('loaded', '*'); };"));
5602 EXPECT_TRUE(ExecJs(shell(), "document.title = 'not loaded';"));
Jan Wilken Dörrie2c470ea2021-03-22 22:26:245603 std::u16string expected_title(u"loaded");
lukasza8e1c02e42016-05-17 20:05:105604 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
5605
5606 // Try to navigate the subframe to a blocked URL.
5607 TestNavigationObserver load_observer2(shell()->web_contents());
5608 GURL blocked_url = embedded_test_server()->GetURL("c.com", "/title3.html");
Nick Carterb7e71312018-08-03 23:36:135609 EXPECT_TRUE(ExecJs(navigating_frame,
5610 JsReplace("window.location.href = $1;", blocked_url)));
lukasza8e1c02e42016-05-17 20:05:105611
5612 // The blocked frame should still fire a load event in its parent's process.
5613 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
5614
5615 // Check that the current RenderFrameHost has stopped loading.
Nasko Oskov63dda2952018-02-09 19:35:425616 if (navigating_frame->current_frame_host()->is_loading())
lukasza8e1c02e42016-05-17 20:05:105617 load_observer2.Wait();
lukasza8e1c02e42016-05-17 20:05:105618
arthursonzogni7fed384c2017-03-18 03:07:345619 // The last successful url shouldn't be the blocked url.
Nasko Oskov0401d812021-02-05 22:20:085620 EXPECT_NE(blocked_url,
arthursonzogni7fed384c2017-03-18 03:07:345621 navigating_frame->current_frame_host()->last_successful_url());
lukasza8e1c02e42016-05-17 20:05:105622
Nasko Oskov63dda2952018-02-09 19:35:425623 // The blocked frame should go to an error page. Errors currently commit
5624 // with the URL of the blocked page.
5625 EXPECT_EQ(blocked_url, navigating_frame->current_url());
arthursonzogni7fed384c2017-03-18 03:07:345626
Nasko Oskov63dda2952018-02-09 19:35:425627 // The page should get the title of an error page (i.e "Error") and not the
5628 // title of the blocked page.
Nick Carterb7e71312018-08-03 23:36:135629 EXPECT_EQ("Error", EvalJs(navigating_frame, "document.title"));
lukasza8e1c02e42016-05-17 20:05:105630
5631 // Navigate the subframe to a URL without CSP.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:205632 EXPECT_TRUE(NavigateToURLFromRenderer(
5633 srcdoc_frame, embedded_test_server()->GetURL("a.com", "/title1.html")));
lukasza8e1c02e42016-05-17 20:05:105634
5635 // Verify that the frame's CSP got correctly reset to an empty set.
Antonio Sartorif45b2fc2021-03-04 10:15:075636 EXPECT_EQ(0u, srcdoc_frame->current_frame_host()
5637 ->policy_container_host()
5638 ->policies()
5639 .content_security_policies.size());
lukasza8e1c02e42016-05-17 20:05:105640}
5641
Charlie Reis22719772020-01-10 16:19:115642// Tests that the state of the RenderViewHost is properly reset when the main
5643// frame is navigated to the same SiteInstance as one of its child frames.
Fergal Daly2e7e1e12020-06-24 09:18:285644IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
nasko7ecc9a62016-03-17 00:22:225645 NavigateMainFrameToChildSite) {
5646 GURL main_url(embedded_test_server()->GetURL(
5647 "a.com", "/cross_site_iframe_factory.html?a(b)"));
5648 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5649
Mustaq Ahmedba573cf2025-05-28 17:06:185650 SimulateEndOfPaintHoldingOnPrimaryMainFrame(web_contents());
5651
ekaramadfd1b5cfa2016-04-19 00:35:005652 WebContentsImpl* contents = web_contents();
Carlos Caballero15caeeb2021-10-27 09:57:555653 FrameTreeNode* root = contents->GetPrimaryFrameTree().root();
nasko7ecc9a62016-03-17 00:22:225654 EXPECT_EQ(1U, root->child_count());
5655
arthursonzognia2754522019-07-03 18:25:365656 // The test expect the BrowsingInstance to be kept across cross-site main
5657 // frame navigations. ProactivelySwapBrowsingInstance will provide a new one.
5658 // To prevent this, a popup is opened.
Rakina Zata Amni6d66b932020-07-21 05:44:175659 if (CanCrossSiteNavigationsProactivelySwapBrowsingInstances()) {
arthursonzognia2754522019-07-03 18:25:365660 GURL popup_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
5661 EXPECT_TRUE(OpenPopup(root, popup_url, "foo"));
5662 }
5663
nasko7ecc9a62016-03-17 00:22:225664 // Ensure the RenderViewHost for the SiteInstance of the child is considered
Charlie Reis22719772020-01-10 16:19:115665 // inactive.
Sharon Yang57bde122022-03-01 20:01:125666 RenderViewHostImpl* rvh = contents->GetPrimaryFrameTree()
5667 .GetRenderViewHost(root->child_at(0)
5668 ->current_frame_host()
5669 ->GetSiteInstance()
5670 ->group())
5671 .get();
Charlie Reis22719772020-01-10 16:19:115672 EXPECT_FALSE(rvh->is_active());
nasko7ecc9a62016-03-17 00:22:225673
5674 // Have the child frame navigate its parent to its SiteInstance.
5675 GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
danakj824a7ff2019-02-07 20:34:025676 auto script = JsReplace("parent.location = $1", b_url);
nasko7ecc9a62016-03-17 00:22:225677
japhet5d8d5ce2016-10-26 17:38:145678 // Ensure the child has received a user gesture, so that it has permission
5679 // to framebust.
5680 SimulateMouseClick(
5681 root->child_at(0)->current_frame_host()->GetRenderWidgetHost(), 1, 1);
nasko7ecc9a62016-03-17 00:22:225682 TestFrameNavigationObserver frame_observer(root);
Avi Drissmanc91bd8e2021-04-19 23:58:445683 EXPECT_TRUE(ExecJs(root->child_at(0), script));
nasko7ecc9a62016-03-17 00:22:225684 frame_observer.Wait();
5685 EXPECT_EQ(b_url, root->current_url());
5686
Charlie Reis22719772020-01-10 16:19:115687 // Verify that the same RenderViewHost is preserved and that it is now active.
Carlos Caballero15caeeb2021-10-27 09:57:555688 EXPECT_EQ(rvh, contents->GetPrimaryFrameTree().GetRenderViewHost(
Sharon Yang57bde122022-03-01 20:01:125689 root->current_frame_host()->GetSiteInstance()->group()));
Charlie Reis22719772020-01-10 16:19:115690 EXPECT_TRUE(rvh->is_active());
nasko7ecc9a62016-03-17 00:22:225691}
5692
alexmos9aa61232016-04-26 21:54:025693// Test for https://crbug.com/568836. From an A-embed-B page, navigate the
5694// subframe from B to A. This cleans up the process for B, but the test delays
5695// the browser side from killing the B process right away. This allows the
Dave Tapuska031bbc662022-08-02 17:28:235696// B process to process the subframe's detached event and the disconnect
5697// of the blink::WebView's blink::mojom::PageBroadcast mojo channel. In the bug,
5698// the latter crashed while detaching the subframe's LocalFrame (triggered as
Dave Tapuska2cf1f532022-08-10 15:30:495699// part of closing the `blink::WebView`), because this tried to access the
5700// subframe's WebFrameWidget (from RenderFrameImpl::didChangeSelection), which
5701// had already been cleared by the former.
Fergal Daly2e7e1e12020-06-24 09:18:285702IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmos9aa61232016-04-26 21:54:025703 CloseSubframeWidgetAndViewOnProcessExit) {
5704 GURL main_url(embedded_test_server()->GetURL(
5705 "a.com", "/cross_site_iframe_factory.html?a(b)"));
5706 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5707
5708 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:555709 ->GetPrimaryFrameTree()
5710 .root();
alexmos9aa61232016-04-26 21:54:025711
5712 // "Select all" in the subframe. The bug only happens if there's a selection
5713 // change, which triggers the path through didChangeSelection.
Dave Tapuska58a099e2020-06-08 21:48:405714 root->child_at(0)
5715 ->current_frame_host()
5716 ->GetRenderWidgetHost()
5717 ->GetFrameWidgetInputHandler()
5718 ->SelectAll();
alexmos9aa61232016-04-26 21:54:025719
5720 // Prevent b.com process from terminating right away once the subframe
5721 // navigates away from b.com below. This is necessary so that the renderer
Dave Tapuska2cf1f532022-08-10 15:30:495722 // process has time to process the closings of RenderWidget and
5723 // `blink::WebView`, which is where the original bug was triggered.
Nan Lin359184e2025-04-03 19:45:225724 // Incrementing the worker ref count will cause
Dave Tapuska2cf1f532022-08-10 15:30:495725 // RenderProcessHostImpl::Cleanup to forego process termination.
Sharon Yangc700dc8a2024-04-04 21:51:575726 RenderProcessHostImpl* subframe_process = static_cast<RenderProcessHostImpl*>(
5727 root->child_at(0)->current_frame_host()->GetProcess());
Nan Lin359184e2025-04-03 19:45:225728 subframe_process->IncrementWorkerRefCount();
alexmos9aa61232016-04-26 21:54:025729
5730 // Navigate the subframe away from b.com. Since this is the last active
Dave Tapuska2cf1f532022-08-10 15:30:495731 // frame in the b.com process, this causes the RenderWidget and
5732 // `blink::WebView` to be closed.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:205733 EXPECT_TRUE(NavigateToURLFromRenderer(
5734 root->child_at(0),
5735 embedded_test_server()->GetURL("a.com", "/title1.html")));
alexmos9aa61232016-04-26 21:54:025736
Arthur Sonzogni4973b392018-08-20 18:14:455737 // Release the process.
5738 RenderProcessHostWatcher process_shutdown_observer(
5739 subframe_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Nan Lin359184e2025-04-03 19:45:225740 subframe_process->DecrementWorkerRefCount();
Arthur Sonzogni4973b392018-08-20 18:14:455741 process_shutdown_observer.Wait();
alexmos9aa61232016-04-26 21:54:025742}
5743
kenrb19221852016-04-29 17:21:405744// Tests that an input event targeted to a out-of-process iframe correctly
5745// triggers a user interaction notification for WebContentsObservers.
5746// This is used for browser features such as download request limiting and
5747// launching multiple external protocol handlers, which can block repeated
5748// actions from a page when a user is not interacting with the page.
Fergal Daly2e7e1e12020-06-24 09:18:285749IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
kenrb19221852016-04-29 17:21:405750 UserInteractionForChildFrameTest) {
5751 GURL main_url(embedded_test_server()->GetURL(
5752 "a.com", "/cross_site_iframe_factory.html?a(b)"));
5753 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5754
Mustaq Ahmedba573cf2025-05-28 17:06:185755 SimulateEndOfPaintHoldingOnPrimaryMainFrame(web_contents());
5756
kenrb19221852016-04-29 17:21:405757 UserInteractionObserver observer(web_contents());
5758
5759 // Target an event to the child frame's RenderWidgetHostView.
Carlos Caballero15caeeb2021-10-27 09:57:555760 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
kenrb19221852016-04-29 17:21:405761 SimulateMouseClick(
5762 root->child_at(0)->current_frame_host()->GetRenderWidgetHost(), 5, 5);
5763
5764 EXPECT_TRUE(observer.WasUserInteractionReceived());
5765
5766 // Target an event to the main frame.
5767 observer.Reset();
5768 SimulateMouseClick(root->current_frame_host()->GetRenderWidgetHost(), 1, 1);
5769
5770 EXPECT_TRUE(observer.WasUserInteractionReceived());
5771}
5772
nasko58b07f52016-05-09 22:38:355773// Ensures that navigating to data: URLs present in session history will
Charlie Reis0bb3f5c72018-08-06 22:46:015774// correctly commit the navigation in the same process as the one used for the
5775// original navigation. See https://crbug.com/606996.
Fergal Daly2e7e1e12020-06-24 09:18:285776IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
nasko58b07f52016-05-09 22:38:355777 NavigateSubframeToDataUrlInSessionHistory) {
5778 GURL main_url(embedded_test_server()->GetURL(
5779 "a.com", "/cross_site_iframe_factory.html?a(b,b)"));
5780 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5781
Carlos Caballero15caeeb2021-10-27 09:57:555782 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
nasko58b07f52016-05-09 22:38:355783 EXPECT_EQ(2U, root->child_count());
5784 EXPECT_EQ(
5785 " Site A ------------ proxies for B\n"
5786 " |--Site B ------- proxies for A\n"
5787 " +--Site B ------- proxies for A\n"
5788 "Where A = http://a.com/\n"
5789 " B = http://b.com/",
5790 DepictFrameTree(root));
5791
5792 TestNavigationObserver observer(shell()->web_contents());
5793 FrameTreeNode* child = root->child_at(0);
5794
5795 // Navigate iframe to a data URL, which will commit in a new SiteInstance.
5796 GURL data_url("data:text/html,dataurl");
Lukasz Anforowicz69c25dfd2020-11-12 21:50:205797 EXPECT_TRUE(NavigateToURLFromRenderer(child, data_url));
nasko58b07f52016-05-09 22:38:355798 EXPECT_TRUE(observer.last_navigation_succeeded());
5799 EXPECT_EQ(data_url, observer.last_navigation_url());
5800 scoped_refptr<SiteInstanceImpl> orig_site_instance =
Charlie Reis0bb3f5c72018-08-06 22:46:015801 child->current_frame_host()->GetSiteInstance();
nasko58b07f52016-05-09 22:38:355802 EXPECT_NE(root->current_frame_host()->GetSiteInstance(), orig_site_instance);
5803
5804 // Navigate it to another cross-site url.
5805 GURL cross_site_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:205806 EXPECT_TRUE(NavigateToURLFromRenderer(child, cross_site_url));
nasko58b07f52016-05-09 22:38:355807 EXPECT_TRUE(observer.last_navigation_succeeded());
5808 EXPECT_EQ(cross_site_url, observer.last_navigation_url());
5809 EXPECT_EQ(3, web_contents()->GetController().GetEntryCount());
5810 EXPECT_NE(orig_site_instance, child->current_frame_host()->GetSiteInstance());
5811
5812 // Go back and ensure the data: URL committed in the same SiteInstance as the
5813 // original navigation.
5814 EXPECT_TRUE(web_contents()->GetController().CanGoBack());
5815 TestFrameNavigationObserver frame_observer(child);
5816 web_contents()->GetController().GoBack();
5817 frame_observer.WaitForCommit();
5818 EXPECT_EQ(orig_site_instance, child->current_frame_host()->GetSiteInstance());
5819}
5820
Sharon Yangb670da6b2023-11-21 02:01:205821// The site URL for a data: URL is the scheme + the serialized nonce from the
5822// origin. This means that two data: URLs with the same body will have different
5823// site URLs.
5824IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, DataUrlsHaveUniqueSiteURLs) {
5825 // Force process reuse for same-site URLs, to test whether identical data:
5826 // URLs share a process with each other.
5827 RenderProcessHost::SetMaxRendererProcessCount(1);
5828
5829 // Load a main frame data: URL.
5830 GURL data_url("data:text/html,dataurl");
5831 EXPECT_TRUE(NavigateToURL(shell(), data_url));
5832
5833 // Open another tab, then load the same data: URL in that tab. We need to
5834 // first navigate the new tab to a different page, a_url.
5835 // Shell::CreateNewWindow opens a new tab to about:blank, then loads the URL
5836 // passed in. Since the about:blank is in a new tab, it gets a new process,
5837 // and the passed-in URL keeps using that about:blank process. By navigating
5838 // from a_url to the data: URL, we exercise the flow that will reuse the
5839 // existing data: URL process, if possible.
5840 GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
5841 ShellAddedObserver new_shell_observer;
5842 Shell* new_shell =
5843 Shell::CreateNewWindow(static_cast<NavigationControllerImpl&>(
5844 shell()->web_contents()->GetController())
5845 .GetBrowserContext(),
5846 a_url, nullptr, gfx::Size());
5847 auto* new_contents = static_cast<WebContentsImpl*>(new_shell->web_contents());
5848 EXPECT_TRUE(WaitForLoadStop(new_contents));
5849 EXPECT_TRUE(NavigateToURL(new_shell, data_url));
5850
5851 auto* main_frame = shell()->web_contents()->GetPrimaryMainFrame();
5852 auto* new_frame = new_shell->web_contents()->GetPrimaryMainFrame();
5853 GURL main_url = main_frame->GetSiteInstance()->GetSiteURL();
5854 GURL new_url = new_frame->GetSiteInstance()->GetSiteURL();
5855 EXPECT_NE(new_frame->GetSiteInstance(), main_frame->GetSiteInstance());
Sharon Yangb670da6b2023-11-21 02:01:205856
Sharon Yangee4f9fdcf2024-08-14 19:10:495857 // The site URL is the data scheme followed by a serialized nonce, which is
5858 // unique for every data: URL instance.
5859 EXPECT_NE(main_url, new_url);
5860 EXPECT_TRUE(main_url.SchemeIs(url::kDataScheme));
5861 EXPECT_EQ(new_url.GetContent().length(),
5862 base::UnguessableToken::Create().ToString().length());
5863 EXPECT_NE(new_frame->GetProcess(), main_frame->GetProcess());
Sharon Yangb670da6b2023-11-21 02:01:205864}
5865
Charlie Reis0bb3f5c72018-08-06 22:46:015866// Ensures that subframes navigated to data: URLs start in a process based on
5867// their creator, but end up in unique processes after a restore (since
5868// SiteInstance relationships are not preserved on restore, until
5869// https://crbug.com/14987 is fixed). This is better than restoring into the
5870// parent process, per https://crbug.com/863069.
Fergal Daly2e7e1e12020-06-24 09:18:285871IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Charlie Reis0bb3f5c72018-08-06 22:46:015872 SubframeDataUrlsAfterRestore) {
5873 // We must use a page that has iframes in the HTML here, unlike
5874 // cross_site_iframe_factory.html which loads them dynamically. In the latter
5875 // case, Chrome will not restore subframe URLs from history, which is needed
5876 // for this test.
5877 GURL main_url(embedded_test_server()->GetURL(
5878 "a.com", "/frame_tree/page_with_two_iframes.html"));
5879 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5880
Carlos Caballero15caeeb2021-10-27 09:57:555881 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Charlie Reis0bb3f5c72018-08-06 22:46:015882 EXPECT_EQ(2U, root->child_count());
5883 EXPECT_EQ(
5884 " Site A ------------ proxies for B C\n"
5885 " |--Site B ------- proxies for A C\n"
5886 " +--Site C ------- proxies for A B\n"
5887 "Where A = http://a.com/\n"
5888 " B = http://bar.com/\n"
5889 " C = http://baz.com/",
5890 DepictFrameTree(root));
5891
5892 FrameTreeNode* child_0 = root->child_at(0);
5893 FrameTreeNode* child_1 = root->child_at(1);
5894 scoped_refptr<SiteInstanceImpl> child_site_instance_0 =
5895 child_0->current_frame_host()->GetSiteInstance();
5896 scoped_refptr<SiteInstanceImpl> child_site_instance_1 =
5897 child_1->current_frame_host()->GetSiteInstance();
5898
5899 // Navigate the iframes to data URLs via renderer initiated navigations, which
Sharon Yang85d8ee72024-11-13 00:52:225900 // will commit in the existing SiteInstanceGroups.
Charlie Reis0bb3f5c72018-08-06 22:46:015901 TestNavigationObserver observer(shell()->web_contents());
5902 GURL data_url_0("data:text/html,dataurl_0");
5903 {
5904 TestFrameNavigationObserver commit_observer(child_0);
Avi Drissmanc91bd8e2021-04-19 23:58:445905 EXPECT_TRUE(ExecJs(child_0, JsReplace("location.href = $1", data_url_0)));
Charlie Reis0bb3f5c72018-08-06 22:46:015906 commit_observer.WaitForCommit();
5907 }
5908 EXPECT_TRUE(observer.last_navigation_succeeded());
5909 EXPECT_EQ(data_url_0, observer.last_navigation_url());
Sharon Yang85d8ee72024-11-13 00:52:225910
5911 if (ShouldCreateSiteInstanceForDataUrls()) {
5912 EXPECT_NE(child_site_instance_0,
5913 child_0->current_frame_host()->GetSiteInstance());
5914 EXPECT_EQ(child_site_instance_0->group(),
5915 child_0->current_frame_host()->GetSiteInstance()->group());
5916 } else {
5917 EXPECT_EQ(child_site_instance_0,
5918 child_0->current_frame_host()->GetSiteInstance());
5919 }
Charlie Reis0bb3f5c72018-08-06 22:46:015920
5921 GURL data_url_1("data:text/html,dataurl_1");
5922 {
5923 TestFrameNavigationObserver commit_observer(child_1);
Avi Drissmanc91bd8e2021-04-19 23:58:445924 EXPECT_TRUE(ExecJs(child_1, JsReplace("location.href = $1", data_url_1)));
Charlie Reis0bb3f5c72018-08-06 22:46:015925 commit_observer.WaitForCommit();
5926 }
5927 EXPECT_TRUE(observer.last_navigation_succeeded());
5928 EXPECT_EQ(data_url_1, observer.last_navigation_url());
Sharon Yang85d8ee72024-11-13 00:52:225929
5930 if (ShouldCreateSiteInstanceForDataUrls()) {
5931 EXPECT_NE(child_site_instance_1,
5932 child_1->current_frame_host()->GetSiteInstance());
5933 EXPECT_EQ(child_site_instance_1->group(),
5934 child_1->current_frame_host()->GetSiteInstance()->group());
5935 } else {
5936 EXPECT_EQ(child_site_instance_1,
5937 child_1->current_frame_host()->GetSiteInstance());
5938 }
Charlie Reis0bb3f5c72018-08-06 22:46:015939
5940 // Grab the NavigationEntry and clone its PageState into a new entry for
5941 // restoring into a new tab.
Lucas Furukawa Gadani5553a1582019-01-08 18:55:575942 NavigationControllerImpl& controller = static_cast<NavigationControllerImpl&>(
5943 shell()->web_contents()->GetController());
Charlie Reis0bb3f5c72018-08-06 22:46:015944 NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
5945 std::unique_ptr<NavigationEntryImpl> restored_entry =
5946 NavigationEntryImpl::FromNavigationEntry(
5947 NavigationController::CreateNavigationEntry(
Arthur Sonzognic686e8f2024-01-11 08:36:375948 main_url, Referrer(), /* initiator_origin= */ std::nullopt,
5949 /* initiator_base_url= */ std::nullopt,
W. James MacLean23e90a12022-12-21 04:38:215950 ui::PAGE_TRANSITION_RELOAD, false, std::string(),
5951 controller.GetBrowserContext(),
Charlie Reis0bb3f5c72018-08-06 22:46:015952 nullptr /* blob_url_loader_factory */));
5953 EXPECT_EQ(0U, restored_entry->root_node()->children.size());
Kevin McNee433daf242023-10-31 20:15:595954 NavigationEntryRestoreContextImpl context;
5955 restored_entry->SetPageState(entry->GetPageState(), &context);
Charlie Reis0bb3f5c72018-08-06 22:46:015956 ASSERT_EQ(2U, restored_entry->root_node()->children.size());
5957
5958 // Restore the NavigationEntry into a new tab and check that the data URLs are
5959 // not loaded into the parent's SiteInstance.
5960 std::vector<std::unique_ptr<NavigationEntry>> entries;
5961 entries.push_back(std::move(restored_entry));
Peter Kasting8104ba82024-01-31 15:23:405962 Shell* new_shell = Shell::CreateNewWindow(controller.GetBrowserContext(),
5963 GURL(), nullptr, gfx::Size());
Charlie Reis0bb3f5c72018-08-06 22:46:015964 FrameTreeNode* new_root =
5965 static_cast<WebContentsImpl*>(new_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:555966 ->GetPrimaryFrameTree()
5967 .root();
Charlie Reis0bb3f5c72018-08-06 22:46:015968 NavigationControllerImpl& new_controller =
5969 static_cast<NavigationControllerImpl&>(
5970 new_shell->web_contents()->GetController());
Lukasz Anforowicz0de0f452020-12-02 19:57:155971 new_controller.Restore(entries.size() - 1, RestoreType::kRestored, &entries);
Charlie Reis0bb3f5c72018-08-06 22:46:015972 ASSERT_EQ(0u, entries.size());
5973 {
5974 TestNavigationObserver restore_observer(new_shell->web_contents());
5975 new_controller.LoadIfNecessary();
5976 restore_observer.Wait();
5977 }
5978 ASSERT_EQ(2U, new_root->child_count());
5979 EXPECT_EQ(main_url, new_root->current_url());
5980 EXPECT_EQ("data", new_root->child_at(0)->current_url().scheme());
5981 EXPECT_EQ("data", new_root->child_at(1)->current_url().scheme());
5982
5983 EXPECT_NE(new_root->current_frame_host()->GetSiteInstance(),
5984 new_root->child_at(0)->current_frame_host()->GetSiteInstance());
5985 EXPECT_NE(new_root->current_frame_host()->GetSiteInstance(),
5986 new_root->child_at(1)->current_frame_host()->GetSiteInstance());
5987 EXPECT_NE(new_root->child_at(0)->current_frame_host()->GetSiteInstance(),
5988 new_root->child_at(1)->current_frame_host()->GetSiteInstance());
5989}
5990
Rakina Zata Amni15de0d32020-01-28 11:38:405991// Similar to SubframeDataUrlsAfterRestore. Ensures that about:blank frames
5992// are not put into their parent process after restore if their initiator origin
5993// is different from the parent.
Fergal Daly2e7e1e12020-06-24 09:18:285994IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Charlie Reis0bb3f5c72018-08-06 22:46:015995 SubframeBlankUrlsAfterRestore) {
5996 // We must use a page that has iframes in the HTML here, unlike
5997 // cross_site_iframe_factory.html which loads them dynamically. In the latter
5998 // case, Chrome will not restore subframe URLs from history, which is needed
5999 // for this test.
6000 GURL main_url(embedded_test_server()->GetURL(
6001 "a.com", "/frame_tree/page_with_two_iframes.html"));
6002 EXPECT_TRUE(NavigateToURL(shell(), main_url));
6003
Carlos Caballero15caeeb2021-10-27 09:57:556004 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Charlie Reis0bb3f5c72018-08-06 22:46:016005 EXPECT_EQ(2U, root->child_count());
6006 EXPECT_EQ(
6007 " Site A ------------ proxies for B C\n"
6008 " |--Site B ------- proxies for A C\n"
6009 " +--Site C ------- proxies for A B\n"
6010 "Where A = http://a.com/\n"
6011 " B = http://bar.com/\n"
6012 " C = http://baz.com/",
6013 DepictFrameTree(root));
6014
6015 FrameTreeNode* child_0 = root->child_at(0);
6016 FrameTreeNode* child_1 = root->child_at(1);
6017 scoped_refptr<SiteInstanceImpl> child_site_instance_0 =
6018 child_0->current_frame_host()->GetSiteInstance();
6019 scoped_refptr<SiteInstanceImpl> child_site_instance_1 =
6020 child_1->current_frame_host()->GetSiteInstance();
6021
6022 // Navigate the iframes to about:blank URLs via renderer initiated
6023 // navigations, which will commit in the existing SiteInstances.
6024 TestNavigationObserver observer(shell()->web_contents());
6025 GURL blank_url("about:blank");
6026 {
6027 TestFrameNavigationObserver commit_observer(child_0);
Avi Drissmanc91bd8e2021-04-19 23:58:446028 EXPECT_TRUE(ExecJs(child_0, JsReplace("location.href = $1", blank_url)));
Charlie Reis0bb3f5c72018-08-06 22:46:016029 commit_observer.WaitForCommit();
6030 }
6031 EXPECT_TRUE(observer.last_navigation_succeeded());
6032 EXPECT_EQ(blank_url, observer.last_navigation_url());
6033 EXPECT_EQ(child_site_instance_0,
6034 child_0->current_frame_host()->GetSiteInstance());
6035
6036 GURL blank_url_ref("about:blank#1");
6037 {
6038 TestFrameNavigationObserver commit_observer(child_1);
danakj824a7ff2019-02-07 20:34:026039 EXPECT_TRUE(
Avi Drissmanc91bd8e2021-04-19 23:58:446040 ExecJs(child_1, JsReplace("location.href = $1", blank_url_ref)));
Charlie Reis0bb3f5c72018-08-06 22:46:016041 commit_observer.WaitForCommit();
6042 }
6043 EXPECT_TRUE(observer.last_navigation_succeeded());
6044 EXPECT_EQ(blank_url_ref, observer.last_navigation_url());
6045 EXPECT_EQ(child_site_instance_1,
6046 child_1->current_frame_host()->GetSiteInstance());
6047
6048 // Grab the NavigationEntry and clone its PageState into a new entry for
6049 // restoring into a new tab.
Lucas Furukawa Gadani5553a1582019-01-08 18:55:576050 NavigationControllerImpl& controller = static_cast<NavigationControllerImpl&>(
6051 shell()->web_contents()->GetController());
Charlie Reis0bb3f5c72018-08-06 22:46:016052 NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
6053 std::unique_ptr<NavigationEntryImpl> restored_entry =
6054 NavigationEntryImpl::FromNavigationEntry(
6055 NavigationController::CreateNavigationEntry(
Arthur Sonzognic686e8f2024-01-11 08:36:376056 main_url, Referrer(), /* initiator_origin= */ std::nullopt,
6057 /* initiator_base_url= */ std::nullopt,
W. James MacLean23e90a12022-12-21 04:38:216058 ui::PAGE_TRANSITION_RELOAD, false, std::string(),
6059 controller.GetBrowserContext(),
Charlie Reis0bb3f5c72018-08-06 22:46:016060 nullptr /* blob_url_loader_factory */));
6061 EXPECT_EQ(0U, restored_entry->root_node()->children.size());
Kevin McNee433daf242023-10-31 20:15:596062 NavigationEntryRestoreContextImpl context;
6063 restored_entry->SetPageState(entry->GetPageState(), &context);
Charlie Reis0bb3f5c72018-08-06 22:46:016064 ASSERT_EQ(2U, restored_entry->root_node()->children.size());
6065
Rakina Zata Amni15de0d32020-01-28 11:38:406066 // Restore the NavigationEntry into a new tab and check that the about:blank
6067 // URLs are not loaded into the parent's SiteInstance.
Charlie Reis0bb3f5c72018-08-06 22:46:016068 std::vector<std::unique_ptr<NavigationEntry>> entries;
6069 entries.push_back(std::move(restored_entry));
Peter Kasting8104ba82024-01-31 15:23:406070 Shell* new_shell = Shell::CreateNewWindow(controller.GetBrowserContext(),
6071 GURL(), nullptr, gfx::Size());
Charlie Reis0bb3f5c72018-08-06 22:46:016072 FrameTreeNode* new_root =
6073 static_cast<WebContentsImpl*>(new_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:556074 ->GetPrimaryFrameTree()
6075 .root();
Charlie Reis0bb3f5c72018-08-06 22:46:016076 NavigationControllerImpl& new_controller =
6077 static_cast<NavigationControllerImpl&>(
6078 new_shell->web_contents()->GetController());
Lukasz Anforowicz0de0f452020-12-02 19:57:156079 new_controller.Restore(entries.size() - 1, RestoreType::kRestored, &entries);
Charlie Reis0bb3f5c72018-08-06 22:46:016080 ASSERT_EQ(0u, entries.size());
6081 {
6082 TestNavigationObserver restore_observer(new_shell->web_contents());
6083 new_controller.LoadIfNecessary();
6084 restore_observer.Wait();
6085 }
6086 ASSERT_EQ(2U, new_root->child_count());
6087 EXPECT_EQ(main_url, new_root->current_url());
Rakina Zata Amni15de0d32020-01-28 11:38:406088 auto* new_child_0 = new_root->child_at(0);
6089 auto* new_child_1 = new_root->child_at(1);
6090 EXPECT_TRUE(new_child_0->current_url().IsAboutBlank());
6091 EXPECT_TRUE(new_child_1->current_url().IsAboutBlank());
Charlie Reis0bb3f5c72018-08-06 22:46:016092
Rakina Zata Amni15de0d32020-01-28 11:38:406093 // Restored frames should retain the origin from before restoring.
6094 EXPECT_EQ(new_root->current_frame_host()->GetLastCommittedOrigin(),
6095 root->current_frame_host()->GetLastCommittedOrigin());
6096 EXPECT_EQ(new_child_0->current_frame_host()
6097 ->GetLastCommittedOrigin()
6098 .GetTupleOrPrecursorTupleIfOpaque(),
6099 child_0->current_frame_host()
6100 ->GetLastCommittedOrigin()
6101 .GetTupleOrPrecursorTupleIfOpaque());
6102 EXPECT_EQ(new_child_1->current_frame_host()
6103 ->GetLastCommittedOrigin()
6104 .GetTupleOrPrecursorTupleIfOpaque(),
6105 child_1->current_frame_host()
6106 ->GetLastCommittedOrigin()
6107 .GetTupleOrPrecursorTupleIfOpaque());
6108 EXPECT_NE(child_0->current_frame_host()
6109 ->GetLastCommittedOrigin()
6110 .GetTupleOrPrecursorTupleIfOpaque(),
6111 child_1->current_frame_host()
6112 ->GetLastCommittedOrigin()
6113 .GetTupleOrPrecursorTupleIfOpaque());
6114
Lukasz Anforowicze9db2652021-01-27 00:24:126115 // Origin for child frames should match the navigation initiators.
Rakina Zata Amni15de0d32020-01-28 11:38:406116 EXPECT_EQ(
6117 new_root->current_frame_host()->GetLastCommittedOrigin().Serialize(),
6118 GetOriginFromRenderer(new_root));
Lukasz Anforowicze9db2652021-01-27 00:24:126119 EXPECT_EQ(GetExpectedOrigin("bar.com"), GetOriginFromRenderer(new_child_0));
6120 EXPECT_EQ(GetExpectedOrigin("baz.com"), GetOriginFromRenderer(new_child_1));
Rakina Zata Amni15de0d32020-01-28 11:38:406121
6122 // Since the origin for the frames are different, they all end up in different
6123 // SiteInstances.
6124 EXPECT_NE(new_root->current_frame_host()->GetSiteInstance(),
6125 new_child_0->current_frame_host()->GetSiteInstance());
6126 EXPECT_NE(new_root->current_frame_host()->GetSiteInstance(),
6127 new_child_1->current_frame_host()->GetSiteInstance());
6128 EXPECT_NE(new_child_0->current_frame_host()->GetSiteInstance(),
6129 new_child_1->current_frame_host()->GetSiteInstance());
Charlie Reis0bb3f5c72018-08-06 22:46:016130}
6131
6132// Similar to SubframeBlankUrlsAfterRestore, but ensures that about:srcdoc ends
6133// up in its parent's process after restore, since that's where its content
6134// comes from.
Fergal Daly2e7e1e12020-06-24 09:18:286135IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Charlie Reis0bb3f5c72018-08-06 22:46:016136 SubframeSrcdocUrlAfterRestore) {
6137 // Load a page that uses iframe srcdoc.
6138 GURL main_url(embedded_test_server()->GetURL(
6139 "a.com", "/frame_tree/page_with_srcdoc_frame.html"));
6140 EXPECT_TRUE(NavigateToURL(shell(), main_url));
6141
Carlos Caballero15caeeb2021-10-27 09:57:556142 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Charlie Reis0bb3f5c72018-08-06 22:46:016143 EXPECT_EQ(1U, root->child_count());
6144 FrameTreeNode* child = root->child_at(0);
6145 scoped_refptr<SiteInstanceImpl> child_site_instance =
6146 child->current_frame_host()->GetSiteInstance();
6147 EXPECT_EQ(child_site_instance, root->current_frame_host()->GetSiteInstance());
6148
6149 // Grab the NavigationEntry and clone its PageState into a new entry for
6150 // restoring into a new tab.
Lucas Furukawa Gadani5553a1582019-01-08 18:55:576151 NavigationControllerImpl& controller = static_cast<NavigationControllerImpl&>(
6152 shell()->web_contents()->GetController());
Charlie Reis0bb3f5c72018-08-06 22:46:016153 NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
6154 std::unique_ptr<NavigationEntryImpl> restored_entry =
6155 NavigationEntryImpl::FromNavigationEntry(
6156 NavigationController::CreateNavigationEntry(
Arthur Sonzognic686e8f2024-01-11 08:36:376157 main_url, Referrer(), /* initiator_origin= */ std::nullopt,
6158 /* initiator_base_url= */ std::nullopt,
W. James MacLean23e90a12022-12-21 04:38:216159 ui::PAGE_TRANSITION_RELOAD, false, std::string(),
6160 controller.GetBrowserContext(),
Charlie Reis0bb3f5c72018-08-06 22:46:016161 nullptr /* blob_url_loader_factory */));
6162 EXPECT_EQ(0U, restored_entry->root_node()->children.size());
Kevin McNee433daf242023-10-31 20:15:596163 NavigationEntryRestoreContextImpl context;
6164 restored_entry->SetPageState(entry->GetPageState(), &context);
Charlie Reis0bb3f5c72018-08-06 22:46:016165 ASSERT_EQ(1U, restored_entry->root_node()->children.size());
6166
6167 // Restore the NavigationEntry into a new tab and check that the srcdoc URLs
6168 // are still loaded into the parent's SiteInstance.
6169 std::vector<std::unique_ptr<NavigationEntry>> entries;
6170 entries.push_back(std::move(restored_entry));
Peter Kasting8104ba82024-01-31 15:23:406171 Shell* new_shell = Shell::CreateNewWindow(controller.GetBrowserContext(),
6172 GURL(), nullptr, gfx::Size());
Charlie Reis0bb3f5c72018-08-06 22:46:016173 FrameTreeNode* new_root =
6174 static_cast<WebContentsImpl*>(new_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:556175 ->GetPrimaryFrameTree()
6176 .root();
Charlie Reis0bb3f5c72018-08-06 22:46:016177 NavigationControllerImpl& new_controller =
6178 static_cast<NavigationControllerImpl&>(
6179 new_shell->web_contents()->GetController());
Lukasz Anforowicz0de0f452020-12-02 19:57:156180 new_controller.Restore(entries.size() - 1, RestoreType::kRestored, &entries);
Charlie Reis0bb3f5c72018-08-06 22:46:016181 ASSERT_EQ(0u, entries.size());
6182 {
6183 TestNavigationObserver restore_observer(new_shell->web_contents());
6184 new_controller.LoadIfNecessary();
6185 restore_observer.Wait();
6186 }
6187 ASSERT_EQ(1U, new_root->child_count());
6188 EXPECT_EQ(main_url, new_root->current_url());
Lukasz Anforowicz42d3d07f2019-06-19 01:06:426189 EXPECT_TRUE(new_root->child_at(0)->current_url().IsAboutSrcdoc());
W. James MacLeanf26c6612024-04-08 20:10:176190 // Not only should the srcdoc inherit its base url from its initiator, but it
6191 // should also be properly restored from the session history.
6192 EXPECT_EQ(
6193 main_url,
6194 GURL(EvalJs(new_root->child_at(0), "document.baseURI").ExtractString()));
Charlie Reis0bb3f5c72018-08-06 22:46:016195
6196 EXPECT_EQ(new_root->current_frame_host()->GetSiteInstance(),
6197 new_root->child_at(0)->current_frame_host()->GetSiteInstance());
6198}
6199
nasko58b07f52016-05-09 22:38:356200// Ensures that navigating to about:blank URLs present in session history will
6201// correctly commit the navigation in the same process as the one used for
6202// the original navigation.
Fergal Daly2e7e1e12020-06-24 09:18:286203IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
nasko58b07f52016-05-09 22:38:356204 NavigateSubframeToAboutBlankInSessionHistory) {
6205 GURL main_url(embedded_test_server()->GetURL(
6206 "a.com", "/cross_site_iframe_factory.html?a(b,b)"));
6207 EXPECT_TRUE(NavigateToURL(shell(), main_url));
6208
Carlos Caballero15caeeb2021-10-27 09:57:556209 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
nasko58b07f52016-05-09 22:38:356210 EXPECT_EQ(2U, root->child_count());
6211 EXPECT_EQ(
6212 " Site A ------------ proxies for B\n"
6213 " |--Site B ------- proxies for A\n"
6214 " +--Site B ------- proxies for A\n"
6215 "Where A = http://a.com/\n"
6216 " B = http://b.com/",
6217 DepictFrameTree(root));
6218
6219 TestNavigationObserver observer(shell()->web_contents());
6220 FrameTreeNode* child = root->child_at(0);
6221
6222 // Navigate iframe to about:blank, which will commit in a new SiteInstance.
6223 GURL about_blank_url("about:blank");
Lukasz Anforowicz69c25dfd2020-11-12 21:50:206224 EXPECT_TRUE(NavigateToURLFromRenderer(child, about_blank_url));
nasko58b07f52016-05-09 22:38:356225 EXPECT_TRUE(observer.last_navigation_succeeded());
6226 EXPECT_EQ(about_blank_url, observer.last_navigation_url());
6227 scoped_refptr<SiteInstanceImpl> orig_site_instance =
6228 child->current_frame_host()->GetSiteInstance();
6229 EXPECT_NE(root->current_frame_host()->GetSiteInstance(), orig_site_instance);
6230
6231 // Navigate it to another cross-site url.
6232 GURL cross_site_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:206233 EXPECT_TRUE(NavigateToURLFromRenderer(child, cross_site_url));
nasko58b07f52016-05-09 22:38:356234 EXPECT_TRUE(observer.last_navigation_succeeded());
6235 EXPECT_EQ(cross_site_url, observer.last_navigation_url());
6236 EXPECT_EQ(3, web_contents()->GetController().GetEntryCount());
6237 EXPECT_NE(orig_site_instance, child->current_frame_host()->GetSiteInstance());
6238
6239 // Go back and ensure the about:blank URL committed in the same SiteInstance
6240 // as the original navigation.
6241 EXPECT_TRUE(web_contents()->GetController().CanGoBack());
6242 TestFrameNavigationObserver frame_observer(child);
6243 web_contents()->GetController().GoBack();
6244 frame_observer.WaitForCommit();
6245 EXPECT_EQ(orig_site_instance, child->current_frame_host()->GetSiteInstance());
6246}
6247
Dave Tapuska437e5d92020-10-26 17:59:366248// Intercepts calls to LocalMainFrame's ShowCreatedWindow mojo method, and
Lowell Manners88da5ec2019-06-18 09:46:176249// invokes the provided callback.
6250class ShowCreatedWindowInterceptor
Dave Tapuska437e5d92020-10-26 17:59:366251 : public blink::mojom::LocalMainFrameHostInterceptorForTesting {
Lowell Manners88da5ec2019-06-18 09:46:176252 public:
Will Harriseb6cca012022-02-11 01:23:176253 // The caller has to guarantee that `render_frame_host` lives at least as long
6254 // as ShowCreatedWindowInterceptor.
Lowell Manners88da5ec2019-06-18 09:46:176255 ShowCreatedWindowInterceptor(
6256 RenderFrameHostImpl* render_frame_host,
Dave Tapuska437e5d92020-10-26 17:59:366257 base::OnceCallback<void(int32_t pending_widget_routing_id)> test_callback)
Lowell Manners88da5ec2019-06-18 09:46:176258 : render_frame_host_(render_frame_host),
Will Harriseb6cca012022-02-11 01:23:176259 test_callback_(std::move(test_callback)),
6260 swapped_impl_(
6261 render_frame_host_->local_main_frame_host_receiver_for_testing(),
6262 this) {}
Lowell Manners88da5ec2019-06-18 09:46:176263
François Doraya53c4a472021-03-04 21:39:216264 ~ShowCreatedWindowInterceptor() override = default;
Lowell Manners88da5ec2019-06-18 09:46:176265
Dave Tapuska437e5d92020-10-26 17:59:366266 blink::mojom::LocalMainFrameHost* GetForwardingInterface() override {
Daniel Chengf693d882024-05-07 16:48:376267 return swapped_impl_.old_impl();
Dave Tapuska437e5d92020-10-26 17:59:366268 }
Lowell Manners88da5ec2019-06-18 09:46:176269
Chris Hamilton2ea20312021-02-24 17:20:146270 void ShowCreatedWindow(const blink::LocalFrameToken& opener_frame_token,
Lowell Manners88da5ec2019-06-18 09:46:176271 WindowOpenDisposition disposition,
Brad Triebwasser767c27a2022-08-25 22:56:056272 blink::mojom::WindowFeaturesPtr window_features,
Dave Tapuska437e5d92020-10-26 17:59:366273 bool user_gesture,
6274 ShowCreatedWindowCallback callback) override {
6275 show_callback_ = std::move(callback);
6276 opener_frame_token_ = opener_frame_token;
6277 user_gesture_ = user_gesture;
Brad Triebwasser767c27a2022-08-25 22:56:056278 window_features_ = std::move(window_features);
Dave Tapuska437e5d92020-10-26 17:59:366279 disposition_ = disposition;
6280 std::move(test_callback_)
6281 .Run(render_frame_host_->GetRenderWidgetHost()->GetRoutingID());
6282 }
6283
6284 void ResumeShowCreatedWindow() {
6285 GetForwardingInterface()->ShowCreatedWindow(
Brad Triebwasser767c27a2022-08-25 22:56:056286 opener_frame_token_, disposition_, std::move(window_features_),
6287 user_gesture_, std::move(show_callback_));
Lowell Manners88da5ec2019-06-18 09:46:176288 }
6289
6290 private:
Keishi Hattori0e45c022021-11-27 09:25:526291 raw_ptr<RenderFrameHostImpl> render_frame_host_;
Dave Tapuska437e5d92020-10-26 17:59:366292 base::OnceCallback<void(int32_t pending_widget_routing_id)> test_callback_;
6293 ShowCreatedWindowCallback show_callback_;
Chris Hamilton2ea20312021-02-24 17:20:146294 blink::LocalFrameToken opener_frame_token_;
Brad Triebwasser767c27a2022-08-25 22:56:056295 blink::mojom::WindowFeaturesPtr window_features_;
Dave Tapuska437e5d92020-10-26 17:59:366296 bool user_gesture_ = false;
6297 WindowOpenDisposition disposition_;
Daniel Cheng304b8f52024-05-07 16:48:116298 mojo::test::ScopedSwapImplForTesting<blink::mojom::LocalMainFrameHost>
Will Harriseb6cca012022-02-11 01:23:176299 swapped_impl_;
Dave Tapuska437e5d92020-10-26 17:59:366300};
6301
6302// Listens for the source WebContents opening the new WebContents then attaches
6303// a show listener to the widget.
6304class NewWindowCreatedObserver : public WebContentsObserver {
6305 public:
6306 NewWindowCreatedObserver(
6307 WebContents* web_contents,
6308 base::OnceCallback<void(int32_t pending_widget_routing_id)> test_callback)
6309 : WebContentsObserver(web_contents),
6310 test_callback_(std::move(test_callback)) {}
6311
6312 // WebContentsObserver overrides.
6313 void DidOpenRequestedURL(WebContents* new_contents,
6314 RenderFrameHost* source_render_frame_host,
6315 const GURL& url,
6316 const Referrer& referrer,
6317 WindowOpenDisposition disposition,
6318 ui::PageTransition transition,
6319 bool started_from_context_menu,
6320 bool renderer_initiated) override {
6321 show_interceptor_ = std::make_unique<ShowCreatedWindowInterceptor>(
Dave Tapuska327c06c92022-06-13 20:31:516322 static_cast<RenderFrameHostImpl*>(new_contents->GetPrimaryMainFrame()),
Dave Tapuska437e5d92020-10-26 17:59:366323 std::move(test_callback_));
6324
6325 // Stop observing now.
6326 Observe(nullptr);
6327 }
6328
6329 void ResumeShowCreatedWindow() {
6330 show_interceptor_->ResumeShowCreatedWindow();
6331 }
6332
6333 private:
6334 std::unique_ptr<ShowCreatedWindowInterceptor> show_interceptor_;
6335 base::OnceCallback<void(int32_t pending_widget_routing_id)> test_callback_;
Lowell Manners88da5ec2019-06-18 09:46:176336};
6337
alexmosc2a8cec2016-05-23 22:19:536338// Test for https://crbug.com/612276. Simultaneously open two new windows from
6339// two subframes in different processes, where each subframe process's next
6340// routing ID is the same. Make sure that both windows are created properly.
6341//
6342// Each new window requires two IPCs to first create it (handled by
6343// CreateNewWindow) and then show it (ShowCreatedWindow). In the bug, both
6344// CreateNewWindow calls arrived before the ShowCreatedWindow calls, resulting
6345// in the two pending windows colliding in the pending WebContents map, which
6346// used to be keyed only by routing_id.
Fergal Daly2e7e1e12020-06-24 09:18:286347IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmosc2a8cec2016-05-23 22:19:536348 TwoSubframesCreatePopupsSimultaneously) {
Stefan Zager6f6e57c2025-03-19 22:03:456349 // This test covers a scenario which can only happen when creating and showing
6350 // a new window is split between to IPC's and some conflicting update happens
6351 // between them. kCombineNewWindowIPCs eliminates this possibility by
6352 // combining the function of the two IPC's into one.
6353 if (base::FeatureList::IsEnabled(blink::features::kCombineNewWindowIPCs)) {
6354 return;
6355 }
alexmosc2a8cec2016-05-23 22:19:536356 GURL main_url(embedded_test_server()->GetURL(
6357 "a.com", "/cross_site_iframe_factory.html?a(b,c)"));
6358 EXPECT_TRUE(NavigateToURL(shell(), main_url));
6359
Carlos Caballero15caeeb2021-10-27 09:57:556360 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmosc2a8cec2016-05-23 22:19:536361 FrameTreeNode* child1 = root->child_at(0);
6362 FrameTreeNode* child2 = root->child_at(1);
Lowell Manners88da5ec2019-06-18 09:46:176363 RenderFrameHostImpl* frame1 = child1->current_frame_host();
6364 RenderFrameHostImpl* frame2 = child2->current_frame_host();
6365 RenderProcessHost* process1 = frame1->GetProcess();
6366 RenderProcessHost* process2 = frame2->GetProcess();
alexmosc2a8cec2016-05-23 22:19:536367
6368 // Call window.open simultaneously in both subframes to create two popups.
Lowell Manners88da5ec2019-06-18 09:46:176369 // Wait for and then drop both ShowCreatedWindow messages. This will ensure
6370 // that both CreateNewWindow calls happen before either ShowCreatedWindow
6371 // call.
6372 base::RunLoop run_loop1;
6373 int32_t routing_id1;
Dave Tapuska437e5d92020-10-26 17:59:366374 NewWindowCreatedObserver interceptor1(
6375 web_contents(),
Lowell Manners88da5ec2019-06-18 09:46:176376 base::BindLambdaForTesting([&](int32_t pending_widget_routing_id) {
6377 routing_id1 = pending_widget_routing_id;
6378 run_loop1.Quit();
6379 }));
Avi Drissmanc91bd8e2021-04-19 23:58:446380 EXPECT_TRUE(ExecJs(child1, "window.open();"));
Lowell Manners88da5ec2019-06-18 09:46:176381 run_loop1.Run();
alexmosc2a8cec2016-05-23 22:19:536382
Lowell Manners88da5ec2019-06-18 09:46:176383 base::RunLoop run_loop2;
6384 int32_t routing_id2;
Dave Tapuska437e5d92020-10-26 17:59:366385 NewWindowCreatedObserver interceptor2(
6386 web_contents(),
Lowell Manners88da5ec2019-06-18 09:46:176387 base::BindLambdaForTesting([&](int32_t pending_widget_routing_id) {
6388 routing_id2 = pending_widget_routing_id;
6389 run_loop2.Quit();
6390 }));
6391
Avi Drissmanc91bd8e2021-04-19 23:58:446392 EXPECT_TRUE(ExecJs(child2, "window.open();"));
Lowell Manners88da5ec2019-06-18 09:46:176393 run_loop2.Run();
alexmosc2a8cec2016-05-23 22:19:536394
6395 // At this point, we should have two pending WebContents.
Emily Andrewsd15fd762024-12-10 20:41:546396 EXPECT_TRUE(base::Contains(
6397 web_contents()->pending_contents_,
6398 GlobalRoutingID(process1->GetDeprecatedID(), routing_id1)));
6399 EXPECT_TRUE(base::Contains(
6400 web_contents()->pending_contents_,
6401 GlobalRoutingID(process2->GetDeprecatedID(), routing_id2)));
alexmosc2a8cec2016-05-23 22:19:536402
6403 // Both subframes were set up in the same way, so the next routing ID for the
6404 // new popup windows should match up (this led to the collision in the
6405 // pending contents map in the original bug).
Lowell Manners88da5ec2019-06-18 09:46:176406 EXPECT_EQ(routing_id1, routing_id2);
alexmosc2a8cec2016-05-23 22:19:536407
Dave Tapuska437e5d92020-10-26 17:59:366408 // Now, resuming processing the show messages.
6409 interceptor1.ResumeShowCreatedWindow();
6410 interceptor2.ResumeShowCreatedWindow();
alexmosc2a8cec2016-05-23 22:19:536411
6412 // Verify that both shells were properly created.
6413 EXPECT_EQ(3u, Shell::windows().size());
6414}
6415
Dave Tapuska42f9b902020-10-23 21:57:066416// Intercepts calls to PopupWidgetHost's ShowPopup mojo method, and
Will Harriseb6cca012022-02-11 01:23:176417// invokes the provided callback. The caller has to guarantee that
6418// `render_widget_host` lives at least as long as
6419// ShowCreatedPopupWidgetInterceptor.
Dave Tapuska42f9b902020-10-23 21:57:066420class ShowCreatedPopupWidgetInterceptor
6421 : public blink::mojom::PopupWidgetHostInterceptorForTesting {
6422 public:
6423 ShowCreatedPopupWidgetInterceptor(
6424 RenderWidgetHostImpl* render_widget_host,
6425 base::OnceCallback<void(int32_t pending_widget_routing_id)> test_callback)
6426 : render_widget_host_(render_widget_host),
Will Harriseb6cca012022-02-11 01:23:176427 test_callback_(std::move(test_callback)),
6428 swapped_impl_(
6429 render_widget_host_->popup_widget_host_receiver_for_testing(),
6430 this) {}
Dave Tapuska42f9b902020-10-23 21:57:066431
6432 ~ShowCreatedPopupWidgetInterceptor() override = default;
6433
6434 blink::mojom::PopupWidgetHost* GetForwardingInterface() override {
Daniel Chengf693d882024-05-07 16:48:376435 return swapped_impl_.old_impl();
Dave Tapuska42f9b902020-10-23 21:57:066436 }
6437
6438 void ShowPopup(const gfx::Rect& initial_rect,
Maksim Sisov113ea342021-08-26 16:19:316439 const gfx::Rect& initial_anchor_rect,
Dave Tapuska42f9b902020-10-23 21:57:066440 ShowPopupCallback callback) override {
6441 show_callback_ = std::move(callback);
6442 initial_rect_ = initial_rect;
6443 std::move(test_callback_).Run(render_widget_host_->GetRoutingID());
6444 }
6445
6446 void ResumeShowPopupWidget() {
Maksim Sisov113ea342021-08-26 16:19:316447 // Let anchor have same origin as bounds, but its width and height should be
6448 // 1,1 as RenderWidgetHostViewAura sets OwnedWindowAnchorPosition as
6449 // kBottomLeft. Otherwise, the bottom left point of the |initial_rect|'s
6450 // size is going to be used as the origin of a popup.
6451 gfx::Rect anchor = initial_rect_;
6452 anchor.set_size({1, 1});
6453 GetForwardingInterface()->ShowPopup(initial_rect_, anchor,
Dave Tapuska42f9b902020-10-23 21:57:066454 std::move(show_callback_));
6455 }
6456
6457 private:
Keishi Hattori0e45c022021-11-27 09:25:526458 raw_ptr<RenderWidgetHostImpl> render_widget_host_;
Dave Tapuska42f9b902020-10-23 21:57:066459 base::OnceCallback<void(int32_t pending_widget_routing_id)> test_callback_;
6460 ShowPopupCallback show_callback_;
6461 gfx::Rect initial_rect_;
Daniel Cheng304b8f52024-05-07 16:48:116462 mojo::test::ScopedSwapImplForTesting<blink::mojom::PopupWidgetHost>
Will Harriseb6cca012022-02-11 01:23:176463 swapped_impl_;
Dave Tapuska42f9b902020-10-23 21:57:066464};
6465
6466// Listens for the source RenderFrameHost opening the new popup widget then
6467// attaches a show listener to the widget.
6468class NewPopupWidgetCreatedObserver {
6469 public:
6470 NewPopupWidgetCreatedObserver(
6471 RenderFrameHostImpl* frame_host,
6472 base::OnceCallback<void(int32_t pending_widget_routing_id)> test_callback)
Daniel Cheng9a3437b32024-05-08 16:47:026473 : create_new_popup_widget_interceptor_(
6474 frame_host,
6475 base::BindOnce(&NewPopupWidgetCreatedObserver::DidCreatePopupWidget,
6476 base::Unretained(this))),
6477 test_callback_(std::move(test_callback)) {}
Dave Tapuska42f9b902020-10-23 21:57:066478
6479 void ResumeShowPopupWidget() { show_interceptor_->ResumeShowPopupWidget(); }
6480
6481 private:
William Liu044b79a82025-07-29 21:05:106482 void DidCreatePopupWidget(RenderWidgetHost* widget) {
Dave Tapuska42f9b902020-10-23 21:57:066483 show_interceptor_ = std::make_unique<ShowCreatedPopupWidgetInterceptor>(
William Liu044b79a82025-07-29 21:05:106484 static_cast<RenderWidgetHostImpl*>(widget), std::move(test_callback_));
Dave Tapuska42f9b902020-10-23 21:57:066485 }
6486
Daniel Cheng9a3437b32024-05-08 16:47:026487 CreateNewPopupWidgetInterceptor create_new_popup_widget_interceptor_;
Dave Tapuska42f9b902020-10-23 21:57:066488 std::unique_ptr<ShowCreatedPopupWidgetInterceptor> show_interceptor_;
6489 base::OnceCallback<void(int32_t pending_widget_routing_id)> test_callback_;
6490};
6491
alexmosc2a8cec2016-05-23 22:19:536492// Test for https://crbug.com/612276. Similar to
6493// TwoSubframesOpenWindowsSimultaneously, but use popup menu widgets instead of
6494// windows.
6495//
6496// The plumbing that this test is verifying is not utilized on Mac/Android,
6497// where popup menus don't create a popup RenderWidget, but rather they trigger
6498// a FrameHostMsg_ShowPopup to ask the browser to build and display the actual
6499// popup using native controls.
Xiaohan Wang1ecfd002022-01-19 22:33:106500#if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_ANDROID)
Owen Mind28a6862020-09-08 21:09:296501// Disable the test due to flaky: https://crbug.com/1126165
Xiaohan Wang1ecfd002022-01-19 22:33:106502#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
Owen Mind28a6862020-09-08 21:09:296503#define MAYBE_TwoSubframesCreatePopupMenuWidgetsSimultaneously \
6504 DISABLED_TwoSubframesCreatePopupMenuWidgetsSimultaneously
6505#else
6506#define MAYBE_TwoSubframesCreatePopupMenuWidgetsSimultaneously \
6507 TwoSubframesCreatePopupMenuWidgetsSimultaneously
6508#endif
Fergal Daly2e7e1e12020-06-24 09:18:286509IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Owen Mind28a6862020-09-08 21:09:296510 MAYBE_TwoSubframesCreatePopupMenuWidgetsSimultaneously) {
alexmosc2a8cec2016-05-23 22:19:536511 GURL main_url(embedded_test_server()->GetURL(
6512 "a.com", "/cross_site_iframe_factory.html?a(b,c)"));
6513 EXPECT_TRUE(NavigateToURL(shell(), main_url));
6514
Carlos Caballero15caeeb2021-10-27 09:57:556515 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmosc2a8cec2016-05-23 22:19:536516 FrameTreeNode* child1 = root->child_at(0);
6517 FrameTreeNode* child2 = root->child_at(1);
6518 RenderProcessHost* process1 = child1->current_frame_host()->GetProcess();
6519 RenderProcessHost* process2 = child2->current_frame_host()->GetProcess();
6520
6521 // Navigate both subframes to a page with a <select> element.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:206522 EXPECT_TRUE(NavigateToURLFromRenderer(
6523 child1, embedded_test_server()->GetURL(
6524 "b.com", "/site_isolation/page-with-select.html")));
6525 EXPECT_TRUE(NavigateToURLFromRenderer(
6526 child2, embedded_test_server()->GetURL(
6527 "c.com", "/site_isolation/page-with-select.html")));
alexmosc2a8cec2016-05-23 22:19:536528
dtapuskae0f8ed72016-06-17 08:23:416529 // Open both <select> menus by focusing each item and sending a space key
6530 // at the focused node. This creates a popup widget in both processes.
alexmosc2a8cec2016-05-23 22:19:536531 // Wait for and then drop the ViewHostMsg_ShowWidget messages, so that both
6532 // widgets are left in pending-but-not-shown state.
Kartar Singh5c8e0b22024-05-30 10:32:146533 input::NativeWebKeyboardEvent event(
Dave Tapuska347d60a2020-04-21 23:55:476534 blink::WebKeyboardEvent::Type::kChar, blink::WebInputEvent::kNoModifiers,
Daniel Cheng93c80a92018-02-14 19:02:436535 blink::WebInputEvent::GetStaticTimeStampForTests());
dtapuskae0f8ed72016-06-17 08:23:416536 event.text[0] = ' ';
dtapuskae0f8ed72016-06-17 08:23:416537
Dave Tapuska42f9b902020-10-23 21:57:066538 base::RunLoop run_loop1;
6539 int32_t routing_id1;
6540 NewPopupWidgetCreatedObserver interceptor1(
6541 child1->current_frame_host(),
6542 base::BindLambdaForTesting([&](int32_t pending_widget_routing_id) {
6543 routing_id1 = pending_widget_routing_id;
6544 run_loop1.Quit();
6545 }));
Avi Drissmanc91bd8e2021-04-19 23:58:446546 EXPECT_TRUE(ExecJs(child1, "focusSelectMenu();"));
dtapuskae0f8ed72016-06-17 08:23:416547 child1->current_frame_host()->GetRenderWidgetHost()->ForwardKeyboardEvent(
6548 event);
Dave Tapuska42f9b902020-10-23 21:57:066549 run_loop1.Run();
alexmosc2a8cec2016-05-23 22:19:536550
Emily Andrewsd15fd762024-12-10 20:41:546551 auto first_popup_global_id =
6552 GlobalRoutingID(process1->GetDeprecatedID(), routing_id1);
Avi Drissman783c1232021-02-05 01:27:056553 // Add an interceptor for first popup widget so it doesn't get closed
Dave Tapuskac63607a2020-10-22 18:44:246554 // immediately while the other one is being opened.
Avi Drissman783c1232021-02-05 01:27:056555 EXPECT_TRUE(
6556 base::Contains(web_contents()->pending_widgets_, first_popup_global_id));
Dave Tapuskac63607a2020-10-22 18:44:246557
6558 RequestCloseWidgetInterceptor child1_popup_widget_interceptor(
6559 static_cast<RenderWidgetHostImpl*>(
Avi Drissman783c1232021-02-05 01:27:056560 web_contents()->pending_widgets_[first_popup_global_id]));
Dave Tapuskac63607a2020-10-22 18:44:246561
Dave Tapuska42f9b902020-10-23 21:57:066562 base::RunLoop run_loop2;
6563 int32_t routing_id2;
6564 NewPopupWidgetCreatedObserver interceptor2(
6565 child2->current_frame_host(),
6566 base::BindLambdaForTesting([&](int32_t pending_widget_routing_id) {
6567 routing_id2 = pending_widget_routing_id;
6568 run_loop2.Quit();
6569 }));
Avi Drissmanc91bd8e2021-04-19 23:58:446570 EXPECT_TRUE(ExecJs(child2, "focusSelectMenu();"));
dtapuskae0f8ed72016-06-17 08:23:416571 child2->current_frame_host()->GetRenderWidgetHost()->ForwardKeyboardEvent(
6572 event);
Dave Tapuska42f9b902020-10-23 21:57:066573 run_loop2.Run();
alexmosc2a8cec2016-05-23 22:19:536574
6575 // At this point, we should have two pending widgets.
Avi Drissman783c1232021-02-05 01:27:056576 EXPECT_TRUE(
6577 base::Contains(web_contents()->pending_widgets_, first_popup_global_id));
Emily Andrewsd15fd762024-12-10 20:41:546578 EXPECT_TRUE(base::Contains(
6579 web_contents()->pending_widgets_,
6580 GlobalRoutingID(process2->GetDeprecatedID(), routing_id2)));
alexmosc2a8cec2016-05-23 22:19:536581
6582 // Both subframes were set up in the same way, so the next routing ID for the
6583 // new popup widgets should match up (this led to the collision in the
6584 // pending widgets map in the original bug).
Dave Tapuska42f9b902020-10-23 21:57:066585 EXPECT_EQ(routing_id1, routing_id2);
alexmosc2a8cec2016-05-23 22:19:536586
6587 // Now simulate both widgets being shown.
Dave Tapuska42f9b902020-10-23 21:57:066588 interceptor1.ResumeShowPopupWidget();
6589 interceptor2.ResumeShowPopupWidget();
Emily Andrewsd15fd762024-12-10 20:41:546590 EXPECT_FALSE(base::Contains(
6591 web_contents()->pending_widgets_,
6592 GlobalRoutingID(process1->GetDeprecatedID(), routing_id1)));
6593 EXPECT_FALSE(base::Contains(
6594 web_contents()->pending_widgets_,
6595 GlobalRoutingID(process2->GetDeprecatedID(), routing_id2)));
Peter Kastinga903c7f2021-04-30 15:44:376596
6597 // There are posted tasks that must be run before the test shuts down, lest
6598 // they access deleted state.
6599 RunPostedTasks();
alexmosc2a8cec2016-05-23 22:19:536600}
6601#endif
6602
nasko39e3eb72016-06-24 23:15:446603// Test for https://crbug.com/615575. It ensures that file chooser triggered
6604// by a document in an out-of-process subframe works properly.
Fergal Daly2e7e1e12020-06-24 09:18:286605IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, FileChooserInSubframe) {
nasko39e3eb72016-06-24 23:15:446606 EXPECT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
6607 "a.com", "/cross_site_iframe_factory.html?a(b)")));
Carlos Caballero15caeeb2021-10-27 09:57:556608 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
nasko39e3eb72016-06-24 23:15:446609
6610 GURL url(embedded_test_server()->GetURL("b.com", "/file_input.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:206611 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), url));
nasko39e3eb72016-06-24 23:15:446612
6613 // Use FileChooserDelegate to avoid showing the actual dialog and to respond
6614 // back to the renderer process with predefined file.
Kent Tamura8c9e0562018-11-06 07:02:196615 base::RunLoop run_loop;
nasko39e3eb72016-06-24 23:15:446616 base::FilePath file;
Avi Drissman1cb5e9f2018-05-01 15:53:286617 EXPECT_TRUE(base::PathService::Get(base::DIR_TEMP, &file));
nasko39e3eb72016-06-24 23:15:446618 file = file.AppendASCII("bar");
Kent Tamura8c9e0562018-11-06 07:02:196619 std::unique_ptr<FileChooserDelegate> delegate(
6620 new FileChooserDelegate(file, run_loop.QuitClosure()));
nasko39e3eb72016-06-24 23:15:446621 shell()->web_contents()->SetDelegate(delegate.get());
Avi Drissmanc91bd8e2021-04-19 23:58:446622 EXPECT_TRUE(ExecJs(root->child_at(0),
6623 "document.getElementById('fileinput').click();"));
Kent Tamura8c9e0562018-11-06 07:02:196624 run_loop.Run();
nasko39e3eb72016-06-24 23:15:446625
6626 // Also, extract the file from the renderer process to ensure that the
6627 // response made it over successfully and the proper filename is set.
Nick Carterb7e71312018-08-03 23:36:136628 EXPECT_EQ("bar",
6629 EvalJs(root->child_at(0),
6630 "document.getElementById('fileinput').files[0].name;"));
nasko39e3eb72016-06-24 23:15:446631}
6632
alexmos20b99f02016-08-10 02:20:216633// Test that the pending RenderFrameHost is canceled and destroyed when its
6634// process dies. Previously, reusing a top-level pending RFH which
6635// is not live was hitting a CHECK in CreateRenderView due to having neither a
6636// main frame routing ID nor a proxy routing ID. See https://crbug.com/627400
6637// for more details.
Fergal Daly2e7e1e12020-06-24 09:18:286638IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmos20b99f02016-08-10 02:20:216639 PendingRFHIsCanceledWhenItsProcessDies) {
6640 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
6641 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:556642 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos20b99f02016-08-10 02:20:216643
6644 // Open a popup at b.com.
6645 GURL popup_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
6646 Shell* popup_shell = OpenPopup(root, popup_url, "foo");
6647 EXPECT_TRUE(popup_shell);
6648
6649 // The RenderViewHost for b.com in the main tab should not be active.
Sharon Yang57bde122022-03-01 20:01:126650 SiteInstanceGroup* b_group =
6651 static_cast<SiteInstanceImpl*>(
6652 popup_shell->web_contents()->GetSiteInstance())
6653 ->group();
alexmos20b99f02016-08-10 02:20:216654 RenderViewHostImpl* rvh =
Sharon Yang57bde122022-03-01 20:01:126655 web_contents()->GetPrimaryFrameTree().GetRenderViewHost(b_group).get();
alexmos20b99f02016-08-10 02:20:216656 EXPECT_FALSE(rvh->is_active());
6657
6658 // Navigate main tab to a b.com URL that will not commit.
6659 GURL stall_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
6660 TestNavigationManager delayer(shell()->web_contents(), stall_url);
Avi Drissmanc91bd8e2021-04-19 23:58:446661 EXPECT_TRUE(ExecJs(shell(), JsReplace("location = $1", stall_url)));
Jiacheng Guo4bdd0be2024-06-11 23:35:216662 delayer.WaitForSpeculativeRenderFrameHostCreation();
alexmos20b99f02016-08-10 02:20:216663
6664 // The pending RFH should be in the same process as the popup.
6665 RenderFrameHostImpl* pending_rfh =
clamy610c63b32017-12-22 15:05:186666 root->render_manager()->speculative_frame_host();
alexmos20b99f02016-08-10 02:20:216667 RenderProcessHost* pending_process = pending_rfh->GetProcess();
6668 EXPECT_EQ(pending_process,
Dave Tapuska327c06c92022-06-13 20:31:516669 popup_shell->web_contents()->GetPrimaryMainFrame()->GetProcess());
alexmos20b99f02016-08-10 02:20:216670
6671 // Kill the b.com process, currently in use by the pending RenderFrameHost
6672 // and the popup.
6673 RenderProcessHostWatcher crash_observer(
6674 pending_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:456675 EXPECT_TRUE(pending_process->Shutdown(0));
alexmos20b99f02016-08-10 02:20:216676 crash_observer.Wait();
6677
6678 // The pending RFH should have been canceled and destroyed, so that it won't
6679 // be reused while it's not live in the next navigation.
Peter Kastingeb8c3ce2021-08-20 04:39:356680 EXPECT_FALSE(root->render_manager()->speculative_frame_host());
alexmos20b99f02016-08-10 02:20:216681
6682 // Navigate main tab to b.com again. This should not crash.
6683 GURL b_url(embedded_test_server()->GetURL("b.com", "/title3.html"));
Alex Moshchuk7e26eca2018-03-03 01:34:296684 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), b_url));
alexmos20b99f02016-08-10 02:20:216685
6686 // The b.com RVH in the main tab should become active.
6687 EXPECT_TRUE(rvh->is_active());
6688}
6689
6690// Test that killing a pending RenderFrameHost's process doesn't leave its
6691// RenderViewHost confused whether it's active or not for future navigations
6692// that try to reuse it. See https://crbug.com/627893 for more details.
6693// Similar to the test above for https://crbug.com/627400, except the popup is
6694// navigated after pending RFH's process is killed, rather than the main tab.
Fergal Daly2e7e1e12020-06-24 09:18:286695IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmos20b99f02016-08-10 02:20:216696 RenderViewHostKeepsSwappedOutStateIfPendingRFHDies) {
6697 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
6698 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:556699 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos20b99f02016-08-10 02:20:216700
6701 // Open a popup at b.com.
6702 GURL popup_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
6703 Shell* popup_shell = OpenPopup(root, popup_url, "foo");
6704 EXPECT_TRUE(popup_shell);
6705
6706 // The RenderViewHost for b.com in the main tab should not be active.
Sharon Yang57bde122022-03-01 20:01:126707 SiteInstanceGroup* b_group =
6708 static_cast<SiteInstanceImpl*>(
6709 popup_shell->web_contents()->GetSiteInstance())
6710 ->group();
alexmos20b99f02016-08-10 02:20:216711 RenderViewHostImpl* rvh =
Sharon Yang57bde122022-03-01 20:01:126712 web_contents()->GetPrimaryFrameTree().GetRenderViewHost(b_group).get();
alexmos20b99f02016-08-10 02:20:216713 EXPECT_FALSE(rvh->is_active());
6714
6715 // Navigate main tab to a b.com URL that will not commit.
6716 GURL stall_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
jamcb4ae152017-05-19 01:35:516717 NavigationHandleObserver handle_observer(shell()->web_contents(), stall_url);
alexmos20b99f02016-08-10 02:20:216718 TestNavigationManager delayer(shell()->web_contents(), stall_url);
Avi Drissmanc91bd8e2021-04-19 23:58:446719 EXPECT_TRUE(ExecJs(shell(), JsReplace("location = $1", stall_url)));
Jiacheng Guo4bdd0be2024-06-11 23:35:216720 delayer.WaitForSpeculativeRenderFrameHostCreation();
alexmos20b99f02016-08-10 02:20:216721
6722 // Kill the b.com process, currently in use by the pending RenderFrameHost
6723 // and the popup.
6724 RenderProcessHost* pending_process =
Dave Tapuska327c06c92022-06-13 20:31:516725 popup_shell->web_contents()->GetPrimaryMainFrame()->GetProcess();
alexmos20b99f02016-08-10 02:20:216726 RenderProcessHostWatcher crash_observer(
6727 pending_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:456728 EXPECT_TRUE(pending_process->Shutdown(0));
alexmos20b99f02016-08-10 02:20:216729 crash_observer.Wait();
6730
6731 // Since the navigation above didn't commit, the b.com RenderViewHost in the
6732 // main tab should still not be active.
6733 EXPECT_FALSE(rvh->is_active());
jamcb4ae152017-05-19 01:35:516734 EXPECT_EQ(net::ERR_ABORTED, handle_observer.net_error_code());
alexmos20b99f02016-08-10 02:20:216735
6736 // Navigate popup to b.com to recreate the b.com process. When creating
6737 // opener proxies, |rvh| should be reused as a swapped out RVH. In
Dave Tapuska2cf1f532022-08-10 15:30:496738 // https://crbug.com/627893, recreating the opener `blink::WebView` was
6739 // hitting a CHECK(params.swapped_out) in the renderer process, since its
alexmos20b99f02016-08-10 02:20:216740 // RenderViewHost was brought into an active state by the navigation to
6741 // |stall_url| above, even though it never committed.
6742 GURL b_url(embedded_test_server()->GetURL("b.com", "/title3.html"));
Alex Moshchuk7e26eca2018-03-03 01:34:296743 EXPECT_TRUE(NavigateToURLInSameBrowsingInstance(popup_shell, b_url));
alexmos20b99f02016-08-10 02:20:216744 EXPECT_FALSE(rvh->is_active());
6745}
6746
alexmosc15fab92016-08-12 00:30:416747// Test that a crashed subframe can be successfully navigated to the site it
6748// was on before crashing. See https://crbug.com/634368.
Fergal Daly2e7e1e12020-06-24 09:18:286749IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmosc15fab92016-08-12 00:30:416750 NavigateCrashedSubframeToSameSite) {
6751 GURL main_url(embedded_test_server()->GetURL(
6752 "a.com", "/cross_site_iframe_factory.html?a(b)"));
6753 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:556754 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmosc15fab92016-08-12 00:30:416755 FrameTreeNode* child = root->child_at(0);
6756
6757 // Set up a postMessage handler in the main frame for later use.
Avi Drissmanc91bd8e2021-04-19 23:58:446758 EXPECT_TRUE(ExecJs(
alexmosc15fab92016-08-12 00:30:416759 root->current_frame_host(),
6760 "window.addEventListener('message',"
6761 " function(e) { document.title = e.data; });"));
6762
6763 // Crash the subframe process.
6764 RenderProcessHost* child_process = child->current_frame_host()->GetProcess();
6765 RenderProcessHostWatcher crash_observer(
6766 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:456767 child_process->Shutdown(0);
alexmosc15fab92016-08-12 00:30:416768 crash_observer.Wait();
6769 EXPECT_FALSE(child->current_frame_host()->IsRenderFrameLive());
6770
6771 // When the subframe dies, its RenderWidgetHostView should be cleared and
6772 // reset in the CrossProcessFrameConnector.
6773 EXPECT_FALSE(child->current_frame_host()->GetView());
6774 RenderFrameProxyHost* proxy_to_parent =
6775 child->render_manager()->GetProxyToParent();
6776 EXPECT_FALSE(
6777 proxy_to_parent->cross_process_frame_connector()->get_view_for_testing());
6778
6779 // Navigate the subframe to the same site it was on before crashing. This
6780 // should reuse the subframe's current RenderFrameHost and reinitialize the
6781 // RenderFrame in a new process.
6782 NavigateFrameToURL(child,
6783 embedded_test_server()->GetURL("b.com", "/title1.html"));
6784 EXPECT_TRUE(child->current_frame_host()->IsRenderFrameLive());
6785
6786 // The RenderWidgetHostView for the child should be recreated and set to be
6787 // used in the CrossProcessFrameConnector. Without this, the frame won't be
6788 // rendered properly.
6789 EXPECT_TRUE(child->current_frame_host()->GetView());
6790 EXPECT_EQ(
6791 child->current_frame_host()->GetView(),
6792 proxy_to_parent->cross_process_frame_connector()->get_view_for_testing());
6793
jonross068adbc2018-05-30 18:04:476794 // Make sure that the child frame has submitted a compositor frame
6795 RenderFrameSubmissionObserver frame_observer(child);
6796 frame_observer.WaitForMetadataChange();
lfgb592bfa82017-05-08 20:47:006797
alexmosc15fab92016-08-12 00:30:416798 // Send a postMessage from the child to its parent. This verifies that the
6799 // parent's proxy in the child's SiteInstance was also restored.
Jan Wilken Dörrie2c470ea2021-03-22 22:26:246800 std::u16string expected_title(u"I am alive!");
alexmosc15fab92016-08-12 00:30:416801 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
Avi Drissmanc91bd8e2021-04-19 23:58:446802 EXPECT_TRUE(ExecJs(child->current_frame_host(),
6803 "parent.postMessage('I am alive!', '*');"));
alexmosc15fab92016-08-12 00:30:416804 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
6805}
6806
alexmos136fd6e62016-08-15 20:58:416807// Test that session history length and offset are replicated to all renderer
6808// processes in a FrameTree. This allows each renderer to see correct values
6809// for history.length, and to check the offset validity properly for
6810// navigations initiated via history.go(). See https:/crbug.com/501116.
Fergal Daly2e7e1e12020-06-24 09:18:286811IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, SessionHistoryReplication) {
alexmos136fd6e62016-08-15 20:58:416812 GURL main_url(embedded_test_server()->GetURL(
6813 "a.com", "/cross_site_iframe_factory.html?a(a,a)"));
6814 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:556815 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos136fd6e62016-08-15 20:58:416816 FrameTreeNode* child1 = root->child_at(0);
6817 FrameTreeNode* child2 = root->child_at(1);
6818 GURL child_first_url(child1->current_url());
6819 EXPECT_EQ(child1->current_url(), child2->current_url());
6820
6821 // Helper to retrieve the history length from a given frame.
6822 auto history_length = [](FrameTreeNode* ftn) {
Nick Carterb7e71312018-08-03 23:36:136823 return EvalJs(ftn->current_frame_host(), "history.length;");
alexmos136fd6e62016-08-15 20:58:416824 };
6825
6826 // All frames should see a history length of 1 to start with.
6827 EXPECT_EQ(1, history_length(root));
6828 EXPECT_EQ(1, history_length(child1));
6829 EXPECT_EQ(1, history_length(child2));
6830
6831 // Navigate first child cross-site. This increases history length to 2.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:206832 EXPECT_TRUE(NavigateToURLFromRenderer(
6833 child1, embedded_test_server()->GetURL("b.com", "/title1.html")));
alexmos136fd6e62016-08-15 20:58:416834 EXPECT_EQ(2, history_length(root));
6835 EXPECT_EQ(2, history_length(child1));
6836 EXPECT_EQ(2, history_length(child2));
6837
6838 // Navigate second child same-site.
6839 GURL child2_last_url(embedded_test_server()->GetURL("a.com", "/title2.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:206840 EXPECT_TRUE(NavigateToURLFromRenderer(child2, child2_last_url));
alexmos136fd6e62016-08-15 20:58:416841 EXPECT_EQ(3, history_length(root));
6842 EXPECT_EQ(3, history_length(child1));
6843 EXPECT_EQ(3, history_length(child2));
6844
6845 // Navigate first child same-site to another b.com URL.
6846 GURL child1_last_url(embedded_test_server()->GetURL("b.com", "/title3.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:206847 EXPECT_TRUE(NavigateToURLFromRenderer(child1, child1_last_url));
alexmos136fd6e62016-08-15 20:58:416848 EXPECT_EQ(4, history_length(root));
6849 EXPECT_EQ(4, history_length(child1));
6850 EXPECT_EQ(4, history_length(child2));
6851
6852 // Go back three entries using the history API from the main frame. This
6853 // checks that both history length and offset are not stale in a.com, as
6854 // otherwise this navigation might be dropped by Blink.
Avi Drissmanc91bd8e2021-04-19 23:58:446855 EXPECT_TRUE(ExecJs(root, "history.go(-3);"));
alexmos136fd6e62016-08-15 20:58:416856 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
6857 EXPECT_EQ(main_url, root->current_url());
6858 EXPECT_EQ(child_first_url, child1->current_url());
6859 EXPECT_EQ(child_first_url, child2->current_url());
6860
6861 // Now go forward three entries from the child1 frame and check that the
6862 // history length and offset are not stale in b.com.
Avi Drissmanc91bd8e2021-04-19 23:58:446863 EXPECT_TRUE(ExecJs(child1, "history.go(3);"));
alexmos136fd6e62016-08-15 20:58:416864 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
6865 EXPECT_EQ(main_url, root->current_url());
6866 EXPECT_EQ(child1_last_url, child1->current_url());
6867 EXPECT_EQ(child2_last_url, child2->current_url());
6868}
6869
Mario Sanchez Prada5c4e7e3b2020-01-22 10:30:066870// Intercepts calls to LocalFrameHost::DispatchLoad method(), and discards them.
6871class DispatchLoadInterceptor
6872 : public blink::mojom::LocalFrameHostInterceptorForTesting {
alexmos7e03e5a92016-08-30 19:18:136873 public:
Mario Sanchez Prada5c4e7e3b2020-01-22 10:30:066874 explicit DispatchLoadInterceptor(RenderFrameHostImpl* render_frame_host)
Daniel Chengf693d882024-05-07 16:48:376875 : swapped_impl_(
6876 render_frame_host->local_frame_host_receiver_for_testing(),
Will Harriseb6cca012022-02-11 01:23:176877 this) {}
alexmos7e03e5a92016-08-30 19:18:136878
Mario Sanchez Prada5c4e7e3b2020-01-22 10:30:066879 ~DispatchLoadInterceptor() override = default;
6880
6881 LocalFrameHost* GetForwardingInterface() override {
Daniel Chengf693d882024-05-07 16:48:376882 return swapped_impl_.old_impl();
Mario Sanchez Prada5c4e7e3b2020-01-22 10:30:066883 }
6884
6885 // Discard incoming calls to LocalFrameHost::DispatchLoad().
6886 void DispatchLoad() override {}
6887
6888 private:
Daniel Cheng304b8f52024-05-07 16:48:116889 mojo::test::ScopedSwapImplForTesting<blink::mojom::LocalFrameHost>
Will Harriseb6cca012022-02-11 01:23:176890 swapped_impl_;
alexmos7e03e5a92016-08-30 19:18:136891};
6892
6893// Test that the renderer isn't killed when a frame generates a load event just
6894// after becoming pending deletion. See https://crbug.com/636513.
Fergal Daly2e7e1e12020-06-24 09:18:286895IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmos7e03e5a92016-08-30 19:18:136896 LoadEventForwardingWhilePendingDeletion) {
6897 GURL main_url(embedded_test_server()->GetURL(
6898 "a.com", "/cross_site_iframe_factory.html?a(a)"));
6899 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:556900 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos7e03e5a92016-08-30 19:18:136901 FrameTreeNode* child = root->child_at(0);
6902
6903 // Open a popup in the b.com process for later use.
6904 GURL popup_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
6905 Shell* popup_shell = OpenPopup(root, popup_url, "foo");
6906 EXPECT_TRUE(popup_shell);
6907
alexmos7e03e5a92016-08-30 19:18:136908 // Navigate subframe to b.com. Wait for commit but not full load.
6909 GURL b_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
6910 {
6911 TestFrameNavigationObserver commit_observer(child);
Avi Drissmanc91bd8e2021-04-19 23:58:446912 EXPECT_TRUE(ExecJs(child, JsReplace("location.href = $1", b_url)));
alexmos7e03e5a92016-08-30 19:18:136913 commit_observer.WaitForCommit();
6914 }
6915 RenderFrameHostImpl* child_rfh = child->current_frame_host();
Nasko Oskov0f3cbb12020-01-07 17:52:146916 child_rfh->DisableUnloadTimerForTesting();
alexmos7e03e5a92016-08-30 19:18:136917
6918 // At this point, the subframe should have a proxy in its parent's
6919 // SiteInstance, a.com.
6920 EXPECT_TRUE(child->render_manager()->GetProxyToParent());
6921
alexmos7e03e5a92016-08-30 19:18:136922 {
Will Harriseb6cca012022-02-11 01:23:176923 // Intercept calls to the LocalFrameHost::DispatchLoad() method.
6924 DispatchLoadInterceptor interceptor(child_rfh);
6925
6926 // Now, go back to a.com in the subframe and wait for commit.
6927 {
6928 TestFrameNavigationObserver commit_observer(child);
6929 web_contents()->GetController().GoBack();
6930 commit_observer.WaitForCommit();
6931 }
6932
6933 // At this point, the subframe's old RFH for b.com should be pending
6934 // deletion, and the subframe's proxy in a.com should've been cleared.
6935 EXPECT_TRUE(child_rfh->IsPendingDeletion());
6936 EXPECT_FALSE(child->render_manager()->GetProxyToParent());
6937
6938 // Simulate that the load event is dispatched from |child_rfh| just after
6939 // it's become pending deletion.
6940 child_rfh->DispatchLoad();
alexmos7e03e5a92016-08-30 19:18:136941 }
6942
Mario Sanchez Prada5c4e7e3b2020-01-22 10:30:066943 // In the bug, DispatchLoad killed the b.com renderer. Ensure that this is
alexmos7e03e5a92016-08-30 19:18:136944 // not the case. Note that the process kill doesn't happen immediately, so
6945 // IsRenderFrameLive() can't be checked here (yet). Instead, check that
6946 // JavaScript can still execute in b.com using the popup.
Avi Drissmanc91bd8e2021-04-19 23:58:446947 EXPECT_TRUE(ExecJs(popup_shell->web_contents(), "true"));
alexmos7e03e5a92016-08-30 19:18:136948}
6949
Fergal Daly2e7e1e12020-06-24 09:18:286950IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
clamyd69748c2016-10-07 22:09:446951 RFHTransfersWhilePendingDeletion) {
6952 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
davidsac6e6c35e42016-11-21 19:45:576953 EXPECT_TRUE(NavigateToURL(shell(), main_url));
clamyd69748c2016-10-07 22:09:446954
6955 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:556956 ->GetPrimaryFrameTree()
6957 .root();
clamyd69748c2016-10-07 22:09:446958
6959 // Start a cross-process navigation and wait until the response is received.
6960 GURL cross_site_url_1 =
6961 embedded_test_server()->GetURL("b.com", "/title1.html");
6962 TestNavigationManager cross_site_manager(shell()->web_contents(),
6963 cross_site_url_1);
6964 shell()->web_contents()->GetController().LoadURL(
6965 cross_site_url_1, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
clamy19f0d492016-10-13 16:53:286966 EXPECT_TRUE(cross_site_manager.WaitForResponse());
clamyd69748c2016-10-07 22:09:446967
6968 // Start a renderer-initiated navigation to a cross-process url and make sure
6969 // the navigation will be blocked before being transferred.
6970 GURL cross_site_url_2 =
6971 embedded_test_server()->GetURL("c.com", "/title1.html");
6972 TestNavigationManager transfer_manager(shell()->web_contents(),
6973 cross_site_url_2);
Avi Drissmanc91bd8e2021-04-19 23:58:446974 EXPECT_TRUE(ExecJs(root, JsReplace("location.href = $1", cross_site_url_2)));
clamy19f0d492016-10-13 16:53:286975 EXPECT_TRUE(transfer_manager.WaitForResponse());
clamyd69748c2016-10-07 22:09:446976
6977 // Now have the cross-process navigation commit and mark the current RFH as
6978 // pending deletion.
Fergal Daly83bc3cd2023-01-18 00:22:546979 ASSERT_TRUE(cross_site_manager.WaitForNavigationFinished());
clamyd69748c2016-10-07 22:09:446980
6981 // Resume the navigation in the previous RFH that has just been marked as
6982 // pending deletion. We should not crash.
Fergal Daly83bc3cd2023-01-18 00:22:546983 ASSERT_TRUE(transfer_manager.WaitForNavigationFinished());
clamyd69748c2016-10-07 22:09:446984}
6985
jam419c0f12016-10-13 02:09:166986class NavigationHandleWatcher : public WebContentsObserver {
6987 public:
Lukasz Anforowicz9e0ce4e2017-09-28 19:09:156988 explicit NavigationHandleWatcher(WebContents* web_contents)
jam419c0f12016-10-13 02:09:166989 : WebContentsObserver(web_contents) {}
6990 void DidStartNavigation(NavigationHandle* navigation_handle) override {
6991 DCHECK_EQ(GURL("http://b.com/"),
6992 navigation_handle->GetStartingSiteInstance()->GetSiteURL());
6993 }
6994};
6995
6996// Verifies that the SiteInstance of a NavigationHandle correctly identifies the
6997// RenderFrameHost that started the navigation (and not the destination RFH).
Fergal Daly2e7e1e12020-06-24 09:18:286998IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
jam419c0f12016-10-13 02:09:166999 NavigationHandleSiteInstance) {
7000 // Navigate to a page with a cross-site iframe.
7001 GURL main_url(embedded_test_server()->GetURL(
7002 "a.com", "/cross_site_iframe_factory.html?a(b)"));
7003 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7004
7005 // Navigate the iframe cross-site.
7006 NavigationHandleWatcher watcher(shell()->web_contents());
7007 TestNavigationObserver load_observer(shell()->web_contents());
7008 GURL frame_url = embedded_test_server()->GetURL("c.com", "/title1.html");
Avi Drissmanc91bd8e2021-04-19 23:58:447009 EXPECT_TRUE(ExecJs(shell()->web_contents(),
7010 JsReplace("window.frames[0].location = $1", frame_url)));
jam419c0f12016-10-13 02:09:167011 load_observer.Wait();
7012}
7013
alexmos78c9c0d2016-10-14 18:57:037014// Test that when canceling a pending RenderFrameHost in the middle of a
Dave Tapuska2cf1f532022-08-10 15:30:497015// redirect, and then killing the corresponding `blink::WebView`'s renderer
7016// process, the RenderViewHost isn't reused in an improper state later.
7017// Previously this led to a crash in CreateRenderView when recreating the
7018// `blink::WebView` due to a stale main frame routing ID. See
7019// https://crbug.com/627400.
Fergal Daly2e7e1e12020-06-24 09:18:287020IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmos78c9c0d2016-10-14 18:57:037021 ReuseNonLiveRenderViewHostAfterCancelPending) {
7022 GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
7023 GURL b_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
7024 GURL c_url(embedded_test_server()->GetURL("c.com", "/title3.html"));
7025
7026 EXPECT_TRUE(NavigateToURL(shell(), a_url));
7027
7028 // Open a popup and navigate it to b.com.
7029 Shell* popup = OpenPopup(shell(), a_url, "popup");
Alex Moshchuk7e26eca2018-03-03 01:34:297030 EXPECT_TRUE(NavigateToURLFromRenderer(popup, b_url));
alexmos78c9c0d2016-10-14 18:57:037031
7032 // Open a second popup and navigate it to b.com, which redirects to c.com.
7033 // The navigation to b.com will create a pending RenderFrameHost, which will
Alex Moshchuk7e26eca2018-03-03 01:34:297034 // be canceled during the redirect to c.com. Note that
7035 // NavigateToURLFromRenderer will return false because the committed URL
7036 // won't match the requested URL due to the redirect.
alexmos78c9c0d2016-10-14 18:57:037037 Shell* popup2 = OpenPopup(shell(), a_url, "popup2");
7038 TestNavigationObserver observer(popup2->web_contents());
7039 GURL redirect_url(embedded_test_server()->GetURL(
7040 "b.com", "/server-redirect?" + c_url.spec()));
Alex Moshchuk7e26eca2018-03-03 01:34:297041 EXPECT_FALSE(NavigateToURLFromRenderer(popup2, redirect_url));
alexmos78c9c0d2016-10-14 18:57:037042 EXPECT_EQ(c_url, observer.last_navigation_url());
7043 EXPECT_TRUE(observer.last_navigation_succeeded());
7044
Dave Tapuska2402595f2022-08-03 16:24:217045 // Kill the b.com process (which currently hosts a `blink::RemoteFrame` that
alexmos78c9c0d2016-10-14 18:57:037046 // replaced the pending RenderFrame in |popup2|, as well as the RenderFrame
7047 // for |popup|).
7048 RenderProcessHost* b_process =
Dave Tapuska327c06c92022-06-13 20:31:517049 popup->web_contents()->GetPrimaryMainFrame()->GetProcess();
alexmos78c9c0d2016-10-14 18:57:037050 RenderProcessHostWatcher crash_observer(
7051 b_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:457052 b_process->Shutdown(0);
alexmos78c9c0d2016-10-14 18:57:037053 crash_observer.Wait();
7054
7055 // Navigate the second popup to b.com. This used to crash when creating the
Dave Tapuska2cf1f532022-08-10 15:30:497056 // `blink::WebView`, because it reused the RenderViewHost created by the
7057 // canceled navigation to b.com, and that RenderViewHost had a stale main
7058 // frame routing ID and active state.
Alex Moshchuk7e26eca2018-03-03 01:34:297059 EXPECT_TRUE(NavigateToURLInSameBrowsingInstance(popup2, b_url));
alexmos78c9c0d2016-10-14 18:57:037060}
7061
7062// Check that after a pending RFH is canceled and replaced with a proxy (which
7063// reuses the canceled RFH's RenderViewHost), navigating to a main frame in the
7064// same site as the canceled RFH doesn't lead to a renderer crash. The steps
7065// here are similar to ReuseNonLiveRenderViewHostAfterCancelPending, but don't
7066// involve crashing the renderer. See https://crbug.com/651980.
Fergal Daly2e7e1e12020-06-24 09:18:287067IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
danakj4421aa82020-09-25 03:43:477068 RecreateMainFrameAfterCancelPending) {
alexmos78c9c0d2016-10-14 18:57:037069 GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
7070 GURL b_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
7071 GURL c_url(embedded_test_server()->GetURL("c.com", "/title3.html"));
7072
7073 EXPECT_TRUE(NavigateToURL(shell(), a_url));
7074
7075 // Open a popup and navigate it to b.com.
7076 Shell* popup = OpenPopup(shell(), a_url, "popup");
Alex Moshchuk7e26eca2018-03-03 01:34:297077 EXPECT_TRUE(NavigateToURLFromRenderer(popup, b_url));
alexmos78c9c0d2016-10-14 18:57:037078
7079 // Open a second popup and navigate it to b.com, which redirects to c.com.
7080 // The navigation to b.com will create a pending RenderFrameHost, which will
7081 // be canceled during the redirect to c.com. Note that NavigateToURL will
7082 // return false because the committed URL won't match the requested URL due
7083 // to the redirect.
7084 Shell* popup2 = OpenPopup(shell(), a_url, "popup2");
7085 TestNavigationObserver observer(popup2->web_contents());
7086 GURL redirect_url(embedded_test_server()->GetURL(
7087 "b.com", "/server-redirect?" + c_url.spec()));
Alex Moshchuk7e26eca2018-03-03 01:34:297088 EXPECT_FALSE(NavigateToURLFromRenderer(popup2, redirect_url));
alexmos78c9c0d2016-10-14 18:57:037089 EXPECT_EQ(c_url, observer.last_navigation_url());
7090 EXPECT_TRUE(observer.last_navigation_succeeded());
7091
7092 // Navigate the second popup to b.com. This used to crash the b.com renderer
7093 // because it failed to delete the canceled RFH's RenderFrame, so this caused
7094 // it to try to create a frame widget which already existed.
Alex Moshchuk7e26eca2018-03-03 01:34:297095 EXPECT_TRUE(NavigateToURLFromRenderer(popup2, b_url));
alexmos78c9c0d2016-10-14 18:57:037096}
7097
7098// Check that when a pending RFH is canceled and a proxy needs to be created in
7099// its place, the proxy is properly initialized on the renderer side. See
7100// https://crbug.com/653746.
Jiacheng Guo782c9ac2024-08-01 08:14:247101// The test disables the delay of creating the speculative RFH since it requires
7102// the created RFH to be cancelld because of the cross-origin redirect.
7103IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTestWithoutSpeculativeRFHDelay,
alexmos78c9c0d2016-10-14 18:57:037104 CommunicateWithProxyAfterCancelPending) {
7105 GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
7106 GURL b_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
7107 GURL c_url(embedded_test_server()->GetURL("c.com", "/title3.html"));
7108
7109 EXPECT_TRUE(NavigateToURL(shell(), a_url));
7110
7111 // Open a popup and navigate it to b.com.
7112 Shell* popup = OpenPopup(shell(), a_url, "popup");
Alex Moshchuk7e26eca2018-03-03 01:34:297113 EXPECT_TRUE(NavigateToURLFromRenderer(popup, b_url));
alexmos78c9c0d2016-10-14 18:57:037114
7115 // Open a second popup and navigate it to b.com, which redirects to c.com.
7116 // The navigation to b.com will create a pending RenderFrameHost, which will
7117 // be canceled during the redirect to c.com. Note that NavigateToURL will
7118 // return false because the committed URL won't match the requested URL due
7119 // to the redirect.
7120 Shell* popup2 = OpenPopup(shell(), a_url, "popup2");
7121 TestNavigationObserver observer(popup2->web_contents());
7122 GURL redirect_url(embedded_test_server()->GetURL(
7123 "b.com", "/server-redirect?" + c_url.spec()));
Alex Moshchuk7e26eca2018-03-03 01:34:297124 EXPECT_FALSE(NavigateToURLFromRenderer(popup2, redirect_url));
alexmos78c9c0d2016-10-14 18:57:037125 EXPECT_EQ(c_url, observer.last_navigation_url());
7126 EXPECT_TRUE(observer.last_navigation_succeeded());
7127
7128 // Because b.com has other active frames (namely, the frame in |popup|),
7129 // there should be a proxy created for the canceled RFH, and it should be
7130 // live.
7131 SiteInstance* b_instance = popup->web_contents()->GetSiteInstance();
7132 FrameTreeNode* popup2_root =
7133 static_cast<WebContentsImpl*>(popup2->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:557134 ->GetPrimaryFrameTree()
7135 .root();
alexmos78c9c0d2016-10-14 18:57:037136 RenderFrameProxyHost* proxy =
Harkiran Bolariad22a1dca2022-02-22 17:01:127137 popup2_root->current_frame_host()
7138 ->browsing_context_state()
7139 ->GetRenderFrameProxyHost(
7140 static_cast<SiteInstanceImpl*>(b_instance)->group());
alexmos78c9c0d2016-10-14 18:57:037141 EXPECT_TRUE(proxy);
7142 EXPECT_TRUE(proxy->is_render_frame_proxy_live());
7143
7144 // Add a postMessage listener in |popup2| (currently at a c.com URL).
Avi Drissmanc91bd8e2021-04-19 23:58:447145 EXPECT_TRUE(ExecJs(popup2,
7146 "window.addEventListener('message', function(event) {\n"
7147 " document.title=event.data;\n"
7148 "});"));
alexmos78c9c0d2016-10-14 18:57:037149
7150 // Check that a postMessage can be sent via |proxy| above. This needs to be
7151 // done from the b.com process. |popup| is currently in b.com, but it can't
7152 // reach the window reference for |popup2| due to a security restriction in
7153 // Blink. So, navigate the main tab to b.com and then send a postMessage to
7154 // |popup2|. This is allowed since the main tab is |popup2|'s opener.
Alex Moshchuk7e26eca2018-03-03 01:34:297155 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), b_url));
alexmos78c9c0d2016-10-14 18:57:037156
Jan Wilken Dörrie2c470ea2021-03-22 22:26:247157 std::u16string expected_title(u"foo");
alexmos78c9c0d2016-10-14 18:57:037158 TitleWatcher title_watcher(popup2->web_contents(), expected_title);
Avi Drissmanc91bd8e2021-04-19 23:58:447159 EXPECT_TRUE(
7160 ExecJs(shell(), "window.open('','popup2').postMessage('foo', '*');"));
alexmos78c9c0d2016-10-14 18:57:037161 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
7162}
7163
Ian Clellanda5278c822020-12-22 17:59:347164IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Charlie Hu31b94f42020-11-26 17:51:217165 HeaderPolicyOnXSLTNavigation) {
7166 GURL url(embedded_test_server()->GetURL("a.com", "/permissions-policy.xml"));
7167
7168 EXPECT_TRUE(NavigateToURL(shell(), url));
7169
Carlos Caballero15caeeb2021-10-27 09:57:557170 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Ari Chivukula04f6ff7e2023-03-22 18:02:007171 EXPECT_EQ(CreateParsedPermissionsPolicyMatchesSelf(
Sandor «Alex» Majore9545a72025-01-31 20:40:467172 {network::mojom::PermissionsPolicyFeature::kGeolocation},
Ari Chivukula04f6ff7e2023-03-22 18:02:007173 url.DeprecatedGetOriginAsURL()),
Charlie Hue20fe2f2021-03-07 03:39:597174 root->current_replication_state().permissions_policy_header);
Charlie Hu31b94f42020-11-26 17:51:217175}
7176
Ian Clellanda5278c822020-12-22 17:59:347177IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Luna Lu58c77b02019-03-05 20:13:467178 TestPolicyReplicationOnSameOriginNavigation) {
iclellandab749ec92016-11-23 02:00:437179 GURL start_url(
Charlie Hu563114f2021-03-11 18:56:367180 embedded_test_server()->GetURL("a.com", "/permissions-policy1.html"));
iclellandab749ec92016-11-23 02:00:437181 GURL first_nav_url(
Charlie Hu563114f2021-03-11 18:56:367182 embedded_test_server()->GetURL("a.com", "/permissions-policy2.html"));
iclellandab749ec92016-11-23 02:00:437183 GURL second_nav_url(embedded_test_server()->GetURL("a.com", "/title2.html"));
7184
7185 EXPECT_TRUE(NavigateToURL(shell(), start_url));
7186
Carlos Caballero15caeeb2021-10-27 09:57:557187 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Ari Chivukula04f6ff7e2023-03-22 18:02:007188 EXPECT_EQ(CreateParsedPermissionsPolicyMatchesSelf(
Sandor «Alex» Majore9545a72025-01-31 20:40:467189 {network::mojom::PermissionsPolicyFeature::kGeolocation,
7190 network::mojom::PermissionsPolicyFeature::kPayment},
Ari Chivukula04f6ff7e2023-03-22 18:02:007191 start_url.DeprecatedGetOriginAsURL()),
Charlie Hue20fe2f2021-03-07 03:39:597192 root->current_replication_state().permissions_policy_header);
iclellandab749ec92016-11-23 02:00:437193
7194 // When the main frame navigates to a page with a new policy, it should
7195 // overwrite the old one.
7196 EXPECT_TRUE(NavigateToURL(shell(), first_nav_url));
Charlie Hue24f04832021-03-04 21:07:067197 EXPECT_EQ(CreateParsedPermissionsPolicyMatchesAll(
Sandor «Alex» Majore9545a72025-01-31 20:40:467198 {network::mojom::PermissionsPolicyFeature::kGeolocation,
7199 network::mojom::PermissionsPolicyFeature::kPayment}),
Charlie Hue20fe2f2021-03-07 03:39:597200 root->current_replication_state().permissions_policy_header);
iclellandab749ec92016-11-23 02:00:437201
7202 // When the main frame navigates to a page without a policy, the replicated
7203 // policy header should be cleared.
7204 EXPECT_TRUE(NavigateToURL(shell(), second_nav_url));
Charlie Hue20fe2f2021-03-07 03:39:597205 EXPECT_TRUE(
7206 root->current_replication_state().permissions_policy_header.empty());
iclellandab749ec92016-11-23 02:00:437207}
7208
Ian Clellanda5278c822020-12-22 17:59:347209IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Luna Lu58c77b02019-03-05 20:13:467210 TestPolicyReplicationOnCrossOriginNavigation) {
iclellandab749ec92016-11-23 02:00:437211 GURL start_url(
Charlie Hu563114f2021-03-11 18:56:367212 embedded_test_server()->GetURL("a.com", "/permissions-policy1.html"));
iclellandab749ec92016-11-23 02:00:437213 GURL first_nav_url(
Charlie Hu563114f2021-03-11 18:56:367214 embedded_test_server()->GetURL("b.com", "/permissions-policy2.html"));
iclellandab749ec92016-11-23 02:00:437215 GURL second_nav_url(embedded_test_server()->GetURL("c.com", "/title2.html"));
7216
7217 EXPECT_TRUE(NavigateToURL(shell(), start_url));
7218
Carlos Caballero15caeeb2021-10-27 09:57:557219 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Ari Chivukula04f6ff7e2023-03-22 18:02:007220 EXPECT_EQ(CreateParsedPermissionsPolicyMatchesSelf(
Sandor «Alex» Majore9545a72025-01-31 20:40:467221 {network::mojom::PermissionsPolicyFeature::kGeolocation,
7222 network::mojom::PermissionsPolicyFeature::kPayment},
Ari Chivukula04f6ff7e2023-03-22 18:02:007223 start_url.DeprecatedGetOriginAsURL()),
Charlie Hue20fe2f2021-03-07 03:39:597224 root->current_replication_state().permissions_policy_header);
iclellandab749ec92016-11-23 02:00:437225
7226 // When the main frame navigates to a page with a new policy, it should
7227 // overwrite the old one.
7228 EXPECT_TRUE(NavigateToURL(shell(), first_nav_url));
Charlie Hue24f04832021-03-04 21:07:067229 EXPECT_EQ(CreateParsedPermissionsPolicyMatchesAll(
Sandor «Alex» Majore9545a72025-01-31 20:40:467230 {network::mojom::PermissionsPolicyFeature::kGeolocation,
7231 network::mojom::PermissionsPolicyFeature::kPayment}),
Charlie Hue20fe2f2021-03-07 03:39:597232 root->current_replication_state().permissions_policy_header);
iclellandab749ec92016-11-23 02:00:437233
7234 // When the main frame navigates to a page without a policy, the replicated
7235 // policy header should be cleared.
7236 EXPECT_TRUE(NavigateToURL(shell(), second_nav_url));
Charlie Hue20fe2f2021-03-07 03:39:597237 EXPECT_TRUE(
7238 root->current_replication_state().permissions_policy_header.empty());
iclellandab749ec92016-11-23 02:00:437239}
7240
Charlie Hu5130d25e2021-03-05 21:53:397241// Test that the replicated permissions policy header is correct in subframes as
iclellandab749ec92016-11-23 02:00:437242// they navigate.
Ian Clellanda5278c822020-12-22 17:59:347243IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Luna Lu58c77b02019-03-05 20:13:467244 TestPolicyReplicationFromRemoteFrames) {
iclellandab749ec92016-11-23 02:00:437245 GURL main_url(
Charlie Hu563114f2021-03-11 18:56:367246 embedded_test_server()->GetURL("a.com", "/permissions-policy-main.html"));
iclellandab749ec92016-11-23 02:00:437247 GURL first_nav_url(
Charlie Hu563114f2021-03-11 18:56:367248 embedded_test_server()->GetURL("b.com", "/permissions-policy2.html"));
iclellandab749ec92016-11-23 02:00:437249 GURL second_nav_url(embedded_test_server()->GetURL("c.com", "/title2.html"));
7250
7251 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7252
Carlos Caballero15caeeb2021-10-27 09:57:557253 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Ari Chivukula04f6ff7e2023-03-22 18:02:007254 EXPECT_EQ(CreateParsedPermissionsPolicy(
Sandor «Alex» Majore9545a72025-01-31 20:40:467255 {network::mojom::PermissionsPolicyFeature::kGeolocation,
7256 network::mojom::PermissionsPolicyFeature::kPayment},
Ari Chivukula04f6ff7e2023-03-22 18:02:007257 {GURL("http://example.com/")}, /*match_all_origins=*/false,
7258 main_url.DeprecatedGetOriginAsURL()),
7259 root->current_replication_state().permissions_policy_header);
iclellandab749ec92016-11-23 02:00:437260 EXPECT_EQ(1UL, root->child_count());
7261 EXPECT_EQ(
Ari Chivukula04f6ff7e2023-03-22 18:02:007262 CreateParsedPermissionsPolicyMatchesSelf(
Sandor «Alex» Majore9545a72025-01-31 20:40:467263 {network::mojom::PermissionsPolicyFeature::kGeolocation,
7264 network::mojom::PermissionsPolicyFeature::kPayment},
Ari Chivukula04f6ff7e2023-03-22 18:02:007265 main_url.DeprecatedGetOriginAsURL()),
Charlie Hue20fe2f2021-03-07 03:39:597266 root->child_at(0)->current_replication_state().permissions_policy_header);
iclellandab749ec92016-11-23 02:00:437267
7268 // Navigate the iframe cross-site.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:207269 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), first_nav_url));
iclellandab749ec92016-11-23 02:00:437270 EXPECT_EQ(
Charlie Hue24f04832021-03-04 21:07:067271 CreateParsedPermissionsPolicyMatchesAll(
Sandor «Alex» Majore9545a72025-01-31 20:40:467272 {network::mojom::PermissionsPolicyFeature::kGeolocation,
7273 network::mojom::PermissionsPolicyFeature::kPayment}),
Charlie Hue20fe2f2021-03-07 03:39:597274 root->child_at(0)->current_replication_state().permissions_policy_header);
iclellandab749ec92016-11-23 02:00:437275
7276 // Navigate the iframe to another location, this one with no policy header
Lukasz Anforowicz69c25dfd2020-11-12 21:50:207277 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), second_nav_url));
raymesd405a052016-12-05 23:41:347278 EXPECT_TRUE(root->child_at(0)
7279 ->current_replication_state()
Charlie Hue20fe2f2021-03-07 03:39:597280 .permissions_policy_header.empty());
iclellandab749ec92016-11-23 02:00:437281
7282 // Navigate the iframe back to a page with a policy
Lukasz Anforowicz69c25dfd2020-11-12 21:50:207283 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), first_nav_url));
iclellandab749ec92016-11-23 02:00:437284 EXPECT_EQ(
Charlie Hue24f04832021-03-04 21:07:067285 CreateParsedPermissionsPolicyMatchesAll(
Sandor «Alex» Majore9545a72025-01-31 20:40:467286 {network::mojom::PermissionsPolicyFeature::kGeolocation,
7287 network::mojom::PermissionsPolicyFeature::kPayment}),
Charlie Hue20fe2f2021-03-07 03:39:597288 root->child_at(0)->current_replication_state().permissions_policy_header);
iclellandab749ec92016-11-23 02:00:437289}
arthursonzogni85ef77182016-11-23 10:05:227290
Charlie Hu5130d25e2021-03-05 21:53:397291// Test that the replicated permissions policy header is correct in remote
7292// proxies after the local frame has navigated.
Ian Clellanda5278c822020-12-22 17:59:347293IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Charlie Hubb5943d2021-03-09 19:46:127294 TestPermissionsPolicyReplicationToProxyOnNavigation) {
Ian Clellandedb8c5dd2018-03-01 17:01:377295 GURL main_url(embedded_test_server()->GetURL(
7296 "a.com", "/frame_tree/page_with_two_frames.html"));
7297 GURL first_nav_url(
Charlie Hu563114f2021-03-11 18:56:367298 embedded_test_server()->GetURL("a.com", "/permissions-policy3.html"));
Ian Clellandedb8c5dd2018-03-01 17:01:377299 GURL second_nav_url(
Charlie Hu563114f2021-03-11 18:56:367300 embedded_test_server()->GetURL("a.com", "/permissions-policy4.html"));
Ian Clellandedb8c5dd2018-03-01 17:01:377301
7302 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7303
Carlos Caballero15caeeb2021-10-27 09:57:557304 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Charlie Hue20fe2f2021-03-07 03:39:597305 EXPECT_TRUE(
7306 root->current_replication_state().permissions_policy_header.empty());
Ian Clellandedb8c5dd2018-03-01 17:01:377307 EXPECT_EQ(2UL, root->child_count());
7308 EXPECT_TRUE(root->child_at(1)
7309 ->current_replication_state()
Charlie Hue20fe2f2021-03-07 03:39:597310 .permissions_policy_header.empty());
Ian Clellandedb8c5dd2018-03-01 17:01:377311
7312 // Navigate the iframe to a page with a policy, and a nested cross-site iframe
7313 // (to the same site as a root->child_at(1) so that the render process already
7314 // exists.)
Lukasz Anforowicz69c25dfd2020-11-12 21:50:207315 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(1), first_nav_url));
Ian Clellandedb8c5dd2018-03-01 17:01:377316 EXPECT_EQ(
Charlie Hue24f04832021-03-04 21:07:067317 CreateParsedPermissionsPolicyMatchesNone(
Sandor «Alex» Majore9545a72025-01-31 20:40:467318 {network::mojom::PermissionsPolicyFeature::kGeolocation,
7319 network::mojom::PermissionsPolicyFeature::kPayment}),
Charlie Hue20fe2f2021-03-07 03:39:597320 root->child_at(1)->current_replication_state().permissions_policy_header);
Ian Clellandedb8c5dd2018-03-01 17:01:377321
7322 EXPECT_EQ(1UL, root->child_at(1)->child_count());
7323
7324 // Ask the deepest iframe to report the enabled state of the geolocation
7325 // feature. If its parent frame's policy was replicated correctly to the
Ian Clellandee2fce32020-09-24 23:12:597326 // proxy, then this will be disabled. Otherwise, it will be enabled by the
7327 // "allow" attribute on the parent frame.
7328 EXPECT_EQ(false,
Ian Clelland905cde302019-01-04 20:33:297329 EvalJs(root->child_at(1)->child_at(0),
7330 "document.featurePolicy.allowsFeature('geolocation')"));
Ian Clellandedb8c5dd2018-03-01 17:01:377331
Ian Clellandee2fce32020-09-24 23:12:597332 // Now navigate the iframe to a page with no header policy, and the same
7333 // nested cross-site iframe. The header policy should be cleared in the proxy.
7334 // In this case, the frame policy from the parent will allow geolocation to be
7335 // delegated.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:207336 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(1), second_nav_url));
Ian Clellandedb8c5dd2018-03-01 17:01:377337 EXPECT_TRUE(root->child_at(1)
7338 ->current_replication_state()
Charlie Hue20fe2f2021-03-07 03:39:597339 .permissions_policy_header.empty());
Ian Clellandedb8c5dd2018-03-01 17:01:377340 EXPECT_EQ(1UL, root->child_at(1)->child_count());
7341
7342 // Ask the deepest iframe to report the enabled state of the geolocation
7343 // feature. If its parent frame's policy was replicated correctly to the
Ian Clellandee2fce32020-09-24 23:12:597344 // proxy, then this will now be allowed.
7345 EXPECT_EQ(true,
Ian Clelland905cde302019-01-04 20:33:297346 EvalJs(root->child_at(1)->child_at(0),
7347 "document.featurePolicy.allowsFeature('geolocation')"));
Ian Clellandedb8c5dd2018-03-01 17:01:377348}
7349
Charlie Hu5130d25e2021-03-05 21:53:397350// Test that the constructed permissions policy is correct in sandboxed
Ian Clelland2c0351ed2018-04-13 01:44:387351// frames. Sandboxed frames have an opaque origin, and if the frame policy,
7352// which is constructed in the parent frame, cannot send that origin through
7353// the browser process to the sandboxed frame, then the sandboxed frame's
7354// policy will be incorrect.
7355//
7356// This is a regression test for https://crbug.com/690520
Ian Clellanda5278c822020-12-22 17:59:347357IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Ian Clelland2c0351ed2018-04-13 01:44:387358 TestAllowAttributeInSandboxedFrame) {
7359 GURL main_url(embedded_test_server()->GetURL(
7360 "a.com",
7361 "/cross_site_iframe_factory.html?"
7362 "a(b{allow-geolocation,sandbox-allow-scripts})"));
7363 GURL nav_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
7364
7365 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7366
Carlos Caballero15caeeb2021-10-27 09:57:557367 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Charlie Hue20fe2f2021-03-07 03:39:597368 EXPECT_TRUE(
7369 root->current_replication_state().permissions_policy_header.empty());
Ian Clelland2c0351ed2018-04-13 01:44:387370 EXPECT_EQ(1UL, root->child_count());
7371 // Verify that the child frame is sandboxed with an opaque origin.
7372 EXPECT_TRUE(root->child_at(0)
7373 ->current_frame_host()
7374 ->GetLastCommittedOrigin()
Chris Palmerab5e5b52018-09-28 19:19:307375 .opaque());
Ian Clelland2c0351ed2018-04-13 01:44:387376 // And verify that the origin in the replication state is also opaque.
Chris Palmerab5e5b52018-09-28 19:19:307377 EXPECT_TRUE(root->child_at(0)->current_origin().opaque());
Ian Clelland2c0351ed2018-04-13 01:44:387378
7379 // Ask the sandboxed iframe to report the enabled state of the geolocation
7380 // feature. If the declared policy was correctly flagged as referring to the
7381 // opaque origin, then the policy in the sandboxed renderer will be
7382 // constructed correctly, and geolocation will be enabled in the sandbox.
7383 // Otherwise, it will be disabled, as geolocation is disabled by default in
7384 // cross-origin frames.
Avi Drissmanc91bd8e2021-04-19 23:58:447385 EXPECT_EQ(true,
7386 EvalJs(root->child_at(0),
7387 "document.featurePolicy.allowsFeature('geolocation');"));
Ian Clelland2c0351ed2018-04-13 01:44:387388
7389 TestNavigationObserver load_observer(shell()->web_contents());
Avi Drissmanc91bd8e2021-04-19 23:58:447390 EXPECT_TRUE(ExecJs(root->child_at(0),
7391 JsReplace("document.location.href=$1", nav_url)));
Ian Clelland2c0351ed2018-04-13 01:44:387392 load_observer.Wait();
7393
7394 // Verify that the child frame is sandboxed with an opaque origin.
7395 EXPECT_TRUE(root->child_at(0)
7396 ->current_frame_host()
7397 ->GetLastCommittedOrigin()
Chris Palmerab5e5b52018-09-28 19:19:307398 .opaque());
Ian Clelland2c0351ed2018-04-13 01:44:387399 // And verify that the origin in the replication state is also opaque.
Chris Palmerab5e5b52018-09-28 19:19:307400 EXPECT_TRUE(root->child_at(0)->current_origin().opaque());
Ian Clelland2c0351ed2018-04-13 01:44:387401
Avi Drissmanc91bd8e2021-04-19 23:58:447402 EXPECT_EQ(true,
7403 EvalJs(root->child_at(0),
7404 "document.featurePolicy.allowsFeature('geolocation');"));
Ian Clelland2c0351ed2018-04-13 01:44:387405}
7406
Charlie Hu5130d25e2021-03-05 21:53:397407// Test that the constructed permissions policy is correct in sandboxed
Ian Clelland2c0351ed2018-04-13 01:44:387408// frames. Sandboxed frames have an opaque origin, and if the frame policy,
7409// which is constructed in the parent frame, cannot send that origin through
7410// the browser process to the sandboxed frame, then the sandboxed frame's
7411// policy will be incorrect.
7412//
7413// This is a regression test for https://crbug.com/690520
Ian Clellanda5278c822020-12-22 17:59:347414IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Ian Clelland2c0351ed2018-04-13 01:44:387415 TestAllowAttributeInOpaqueOriginAfterNavigation) {
7416 GURL main_url(embedded_test_server()->GetURL(
7417 "a.com", "/page_with_data_iframe_and_allow.html"));
7418 GURL nav_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
7419
7420 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7421
Carlos Caballero15caeeb2021-10-27 09:57:557422 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Charlie Hue20fe2f2021-03-07 03:39:597423 EXPECT_TRUE(
7424 root->current_replication_state().permissions_policy_header.empty());
Ian Clelland2c0351ed2018-04-13 01:44:387425 EXPECT_EQ(1UL, root->child_count());
7426 // Verify that the child frame has an opaque origin.
7427 EXPECT_TRUE(root->child_at(0)
7428 ->current_frame_host()
7429 ->GetLastCommittedOrigin()
Chris Palmerab5e5b52018-09-28 19:19:307430 .opaque());
Ian Clelland2c0351ed2018-04-13 01:44:387431 // And verify that the origin in the replication state is also opaque.
Chris Palmerab5e5b52018-09-28 19:19:307432 EXPECT_TRUE(root->child_at(0)->current_origin().opaque());
Ian Clelland2c0351ed2018-04-13 01:44:387433
7434 // Verify that geolocation is enabled in the document.
Avi Drissmanc91bd8e2021-04-19 23:58:447435 EXPECT_EQ(true,
7436 EvalJs(root->child_at(0),
7437 "document.featurePolicy.allowsFeature('geolocation');"));
Ian Clelland2c0351ed2018-04-13 01:44:387438
7439 TestNavigationObserver load_observer(shell()->web_contents());
Avi Drissmanc91bd8e2021-04-19 23:58:447440 EXPECT_TRUE(ExecJs(root->child_at(0),
7441 JsReplace("document.location.href=$1", nav_url)));
Ian Clelland2c0351ed2018-04-13 01:44:387442 load_observer.Wait();
7443
7444 // Verify that the child frame no longer has an opaque origin.
7445 EXPECT_FALSE(root->child_at(0)
7446 ->current_frame_host()
7447 ->GetLastCommittedOrigin()
Chris Palmerab5e5b52018-09-28 19:19:307448 .opaque());
Ian Clelland2c0351ed2018-04-13 01:44:387449 // Verify that the origin in the replication state is also no longer opaque.
Chris Palmerab5e5b52018-09-28 19:19:307450 EXPECT_FALSE(root->child_at(0)->current_origin().opaque());
Ian Clelland2c0351ed2018-04-13 01:44:387451
7452 // Verify that the new document does not have geolocation enabled.
Avi Drissmanc91bd8e2021-04-19 23:58:447453 EXPECT_EQ(false,
7454 EvalJs(root->child_at(0),
7455 "document.featurePolicy.allowsFeature('geolocation');"));
Ian Clelland2c0351ed2018-04-13 01:44:387456}
7457
arthursonzogni85ef77182016-11-23 10:05:227458// Ensure that an iframe that navigates cross-site doesn't use the same process
7459// as its parent. Then when its parent navigates it via the "srcdoc" attribute,
7460// it must reuse its parent's process.
Fergal Daly2e7e1e12020-06-24 09:18:287461IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
arthursonzogni85ef77182016-11-23 10:05:227462 IframeSrcdocAfterCrossSiteNavigation) {
7463 GURL parent_url(embedded_test_server()->GetURL(
7464 "a.com", "/cross_site_iframe_factory.html?a(b)"));
7465 GURL child_url(embedded_test_server()->GetURL(
7466 "b.com", "/cross_site_iframe_factory.html?b()"));
arthursonzogni85ef77182016-11-23 10:05:227467
7468 // #1 Navigate to a page with a cross-site iframe.
7469 EXPECT_TRUE(NavigateToURL(shell(), parent_url));
7470
7471 // Ensure that the iframe uses its own process.
Carlos Caballero15caeeb2021-10-27 09:57:557472 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
arthursonzogni85ef77182016-11-23 10:05:227473 ASSERT_EQ(1u, root->child_count());
7474 FrameTreeNode* child = root->child_at(0);
7475 EXPECT_EQ(parent_url, root->current_url());
7476 EXPECT_EQ(child_url, child->current_url());
7477 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
7478 child->current_frame_host()->GetSiteInstance());
7479 EXPECT_NE(root->current_frame_host()->GetProcess(),
7480 child->current_frame_host()->GetProcess());
7481
7482 // #2 Navigate the iframe to its srcdoc attribute.
7483 TestNavigationObserver load_observer(shell()->web_contents());
Avi Drissmanc91bd8e2021-04-19 23:58:447484 EXPECT_TRUE(ExecJs(
arthursonzogni85ef77182016-11-23 10:05:227485 root, "document.getElementById('child-0').srcdoc = 'srcdoc content';"));
7486 load_observer.Wait();
7487
7488 // Ensure that the iframe reuses its parent's process.
Lukasz Anforowicz42d3d07f2019-06-19 01:06:427489 EXPECT_TRUE(child->current_url().IsAboutSrcdoc());
arthursonzogni85ef77182016-11-23 10:05:227490 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
7491 child->current_frame_host()->GetSiteInstance());
7492 EXPECT_EQ(root->current_frame_host()->GetProcess(),
7493 child->current_frame_host()->GetProcess());
7494}
7495
alexmosf65a795a2017-01-12 22:04:007496// Verify that a remote-to-local navigation in a crashed subframe works. See
7497// https://crbug.com/487872.
Fergal Daly2e7e1e12020-06-24 09:18:287498IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmosf65a795a2017-01-12 22:04:007499 RemoteToLocalNavigationInCrashedSubframe) {
7500 GURL main_url(embedded_test_server()->GetURL(
7501 "a.com", "/cross_site_iframe_factory.html?a(b)"));
7502 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7503
Carlos Caballero15caeeb2021-10-27 09:57:557504 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmosf65a795a2017-01-12 22:04:007505 FrameTreeNode* child = root->child_at(0);
7506
7507 // Crash the subframe process.
7508 RenderProcessHost* child_process = child->current_frame_host()->GetProcess();
7509 {
7510 RenderProcessHostWatcher crash_observer(
7511 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:457512 child_process->Shutdown(0);
alexmosf65a795a2017-01-12 22:04:007513 crash_observer.Wait();
7514 }
7515 EXPECT_FALSE(child->current_frame_host()->IsRenderFrameLive());
7516
7517 // Do a remote-to-local navigation of the child frame from the parent frame.
7518 TestFrameNavigationObserver frame_observer(child);
7519 GURL frame_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
Avi Drissmanc91bd8e2021-04-19 23:58:447520 EXPECT_TRUE(ExecJs(
danakj824a7ff2019-02-07 20:34:027521 root, JsReplace("document.querySelector('iframe').src = $1", frame_url)));
alexmosf65a795a2017-01-12 22:04:007522 frame_observer.Wait();
7523
7524 EXPECT_TRUE(child->current_frame_host()->IsRenderFrameLive());
7525 EXPECT_FALSE(child->IsLoading());
7526 EXPECT_EQ(child->current_frame_host()->GetSiteInstance(),
7527 root->current_frame_host()->GetSiteInstance());
7528
7529 // Ensure the subframe is correctly attached in the frame tree, and that it
7530 // has correct content.
Nick Carterb7e71312018-08-03 23:36:137531 EXPECT_EQ(1, EvalJs(root, "frames.length;"));
alexmosf65a795a2017-01-12 22:04:007532
Nick Carterb7e71312018-08-03 23:36:137533 EXPECT_EQ("This page has no title.",
7534 EvalJs(root, "frames[0].document.body.innerText;"));
alexmosf65a795a2017-01-12 22:04:007535}
7536
lfg6ab84d522017-01-16 18:04:007537// Tests that trying to open a context menu in the old RFH after commiting a
7538// navigation doesn't crash the browser. https://crbug.com/677266.
Fergal Daly2e7e1e12020-06-24 09:18:287539IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
lfg6ab84d522017-01-16 18:04:007540 ContextMenuAfterCrossProcessNavigation) {
7541 // Navigate to a.com.
7542 EXPECT_TRUE(NavigateToURL(
7543 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
7544
Nasko Oskov0f3cbb12020-01-07 17:52:147545 // Disable the unload ACK and the unload timer.
lfg6ab84d522017-01-16 18:04:007546 RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(
Dave Tapuska327c06c92022-06-13 20:31:517547 shell()->web_contents()->GetPrimaryMainFrame());
Dave Tapuskaca250702021-01-07 17:40:557548 auto unload_ack_filter = base::BindRepeating([] { return true; });
7549 rfh->SetUnloadACKCallbackForTesting(unload_ack_filter);
Nasko Oskov0f3cbb12020-01-07 17:52:147550 rfh->DisableUnloadTimerForTesting();
lfg6ab84d522017-01-16 18:04:007551
7552 // Open a popup on a.com to keep the process alive.
7553 OpenPopup(shell(), embedded_test_server()->GetURL("a.com", "/title2.html"),
7554 "foo");
7555
7556 // Cross-process navigation to b.com.
7557 EXPECT_TRUE(NavigateToURL(
7558 shell(), embedded_test_server()->GetURL("b.com", "/title3.html")));
7559
7560 // Pretend that a.com just requested a context menu. This used to cause a
Nasko Oskov0f3cbb12020-01-07 17:52:147561 // because the RenderWidgetHostView is destroyed when the frame is unloaded
7562 // and added to pending delete list.
Gyuyoung Kim1bc1ba82021-02-08 23:32:447563 rfh->ShowContextMenu(mojo::NullAssociatedRemote(), ContextMenuParams());
lfg6ab84d522017-01-16 18:04:007564}
7565
iclelland92f8c0b2017-04-19 12:43:057566// Test iframe container policy is replicated properly to the browser.
Fergal Daly2e7e1e12020-06-24 09:18:287567IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, ContainerPolicy) {
iclelland92f8c0b2017-04-19 12:43:057568 GURL url(embedded_test_server()->GetURL("/allowed_frames.html"));
7569 EXPECT_TRUE(NavigateToURL(shell(), url));
7570
Carlos Caballero15caeeb2021-10-27 09:57:557571 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
iclelland92f8c0b2017-04-19 12:43:057572
Ian Clellandcdc4f312017-10-13 22:24:127573 EXPECT_EQ(0UL, root->effective_frame_policy().container_policy.size());
7574 EXPECT_EQ(
7575 0UL, root->child_at(0)->effective_frame_policy().container_policy.size());
7576 EXPECT_EQ(
7577 0UL, root->child_at(1)->effective_frame_policy().container_policy.size());
7578 EXPECT_EQ(
7579 2UL, root->child_at(2)->effective_frame_policy().container_policy.size());
7580 EXPECT_EQ(
7581 2UL, root->child_at(3)->effective_frame_policy().container_policy.size());
iclelland92f8c0b2017-04-19 12:43:057582}
7583
7584// Test dynamic updates to iframe "allow" attribute are propagated correctly.
Fergal Daly2e7e1e12020-06-24 09:18:287585IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, ContainerPolicyDynamic) {
iclelland92f8c0b2017-04-19 12:43:057586 GURL main_url(embedded_test_server()->GetURL("/allowed_frames.html"));
7587 GURL nav_url(
Charlie Hu563114f2021-03-11 18:56:367588 embedded_test_server()->GetURL("b.com", "/permissions-policy2.html"));
iclelland92f8c0b2017-04-19 12:43:057589 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7590
Carlos Caballero15caeeb2021-10-27 09:57:557591 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
iclelland92f8c0b2017-04-19 12:43:057592
Ian Clellandcdc4f312017-10-13 22:24:127593 EXPECT_EQ(
7594 2UL, root->child_at(2)->effective_frame_policy().container_policy.size());
iclelland92f8c0b2017-04-19 12:43:057595
7596 // Removing the "allow" attribute; pending policy should update, but effective
7597 // policy remains unchanged.
Avi Drissmanc91bd8e2021-04-19 23:58:447598 EXPECT_TRUE(ExecJs(
iclelland92f8c0b2017-04-19 12:43:057599 root, "document.getElementById('child-2').setAttribute('allow','')"));
Ian Clellandcdc4f312017-10-13 22:24:127600 EXPECT_EQ(
7601 2UL, root->child_at(2)->effective_frame_policy().container_policy.size());
7602 EXPECT_EQ(0UL,
7603 root->child_at(2)->pending_frame_policy().container_policy.size());
iclelland92f8c0b2017-04-19 12:43:057604
7605 // Navigate the frame; pending policy should be committed.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:207606 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(2), nav_url));
Ian Clellandcdc4f312017-10-13 22:24:127607 EXPECT_EQ(
7608 0UL, root->child_at(2)->effective_frame_policy().container_policy.size());
iclelland92f8c0b2017-04-19 12:43:057609}
7610
Ian Clelland0e8654a2017-04-28 15:06:297611// Check that out-of-process frames correctly calculate the container policy in
7612// the renderer when navigating cross-origin. The policy should be unchanged
7613// when modified dynamically in the parent frame. When the frame is navigated,
7614// the new renderer should have the correct container policy.
7615//
7616// TODO(iclelland): Once there is a proper JS inspection API from the renderer,
7617// use that to check the policy. Until then, we test webkitFullscreenEnabled,
7618// which conveniently just returns the result of calling isFeatureEnabled on
7619// the fullscreen feature. Since there are no HTTP header policies involved,
7620// this verifies the presence of the container policy in the iframe.
7621// https://crbug.com/703703
Fergal Daly2e7e1e12020-06-24 09:18:287622IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Ian Clelland91cff7ae2018-02-15 16:51:377623 ContainerPolicyCrossOriginNavigation) {
Ian Clelland0e8654a2017-04-28 15:06:297624 WebContentsImpl* contents = web_contents();
Carlos Caballero15caeeb2021-10-27 09:57:557625 FrameTreeNode* root = contents->GetPrimaryFrameTree().root();
Ian Clelland0e8654a2017-04-28 15:06:297626
7627 // Helper to check if a frame is allowed to go fullscreen on the renderer
7628 // side.
7629 auto is_fullscreen_allowed = [](FrameTreeNode* ftn) {
Nick Carterb7e71312018-08-03 23:36:137630 return EvalJs(ftn, "document.webkitFullscreenEnabled;");
Ian Clelland0e8654a2017-04-28 15:06:297631 };
7632
7633 // Load a page with an <iframe> without allowFullscreen.
7634 EXPECT_TRUE(NavigateToURL(
7635 shell(), embedded_test_server()->GetURL(
7636 "a.com", "/cross_site_iframe_factory.html?a(b)")));
7637
7638 // Dynamically enable fullscreen for the subframe and check that the
7639 // fullscreen property was updated on the FrameTreeNode.
Avi Drissmanc91bd8e2021-04-19 23:58:447640 EXPECT_TRUE(ExecJs(
Ian Clelland0e8654a2017-04-28 15:06:297641 root, "document.getElementById('child-0').allowFullscreen='true'"));
7642
7643 // No change is expected to the container policy for dynamic modification of
7644 // a loaded frame.
Nick Carterb7e71312018-08-03 23:36:137645 EXPECT_EQ(false, is_fullscreen_allowed(root->child_at(0)));
Ian Clelland0e8654a2017-04-28 15:06:297646
7647 // Cross-site navigation should update the container policy in the new render
7648 // frame.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:207649 EXPECT_TRUE(NavigateToURLFromRenderer(
7650 root->child_at(0),
7651 embedded_test_server()->GetURL("c.com", "/title1.html")));
Nick Carterb7e71312018-08-03 23:36:137652 EXPECT_EQ(true, is_fullscreen_allowed(root->child_at(0)));
Ian Clelland0e8654a2017-04-28 15:06:297653}
7654
iclelland92f8c0b2017-04-19 12:43:057655// Test that dynamic updates to iframe sandbox attribute correctly set the
7656// replicated container policy.
Fergal Daly2e7e1e12020-06-24 09:18:287657IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
iclelland92f8c0b2017-04-19 12:43:057658 ContainerPolicySandboxDynamic) {
7659 GURL main_url(embedded_test_server()->GetURL("/allowed_frames.html"));
7660 GURL nav_url(
Charlie Hu563114f2021-03-11 18:56:367661 embedded_test_server()->GetURL("b.com", "/permissions-policy2.html"));
iclelland92f8c0b2017-04-19 12:43:057662 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7663
Carlos Caballero15caeeb2021-10-27 09:57:557664 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
iclelland92f8c0b2017-04-19 12:43:057665
7666 // Validate that the effective container policy contains a single non-unique
7667 // origin.
Sandor Major878f8352025-02-18 20:16:027668 const network::ParsedPermissionsPolicy initial_effective_policy =
Ian Clellandcdc4f312017-10-13 22:24:127669 root->child_at(2)->effective_frame_policy().container_policy;
Charlie Hu8daefb12020-04-24 19:57:127670 EXPECT_EQ(1UL, initial_effective_policy[0].allowed_origins.size());
iclelland92f8c0b2017-04-19 12:43:057671
7672 // Set the "sandbox" attribute; pending policy should update, and should now
Ian Clelland2c0351ed2018-04-13 01:44:387673 // be flagged as matching the opaque origin of the frame (without containing
7674 // an actual opaque origin, since the parent frame doesn't actually have that
7675 // origin yet) but the effective policy should remain unchanged.
Avi Drissmanc91bd8e2021-04-19 23:58:447676 EXPECT_TRUE(ExecJs(
iclelland92f8c0b2017-04-19 12:43:057677 root, "document.getElementById('child-2').setAttribute('sandbox','')"));
Sandor Major878f8352025-02-18 20:16:027678 const network::ParsedPermissionsPolicy updated_effective_policy =
Ian Clellandcdc4f312017-10-13 22:24:127679 root->child_at(2)->effective_frame_policy().container_policy;
Sandor Major878f8352025-02-18 20:16:027680 const network::ParsedPermissionsPolicy updated_pending_policy =
Ian Clellandcdc4f312017-10-13 22:24:127681 root->child_at(2)->pending_frame_policy().container_policy;
Charlie Hu8daefb12020-04-24 19:57:127682 EXPECT_EQ(1UL, updated_effective_policy[0].allowed_origins.size());
Ian Clelland3c5c24522020-06-29 18:51:287683 EXPECT_TRUE(updated_pending_policy[0].matches_opaque_src);
Charlie Hu8daefb12020-04-24 19:57:127684 EXPECT_EQ(0UL, updated_pending_policy[0].allowed_origins.size());
iclelland92f8c0b2017-04-19 12:43:057685
7686 // Navigate the frame; pending policy should now be committed.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:207687 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(2), nav_url));
Sandor Major878f8352025-02-18 20:16:027688 const network::ParsedPermissionsPolicy final_effective_policy =
Ian Clellandcdc4f312017-10-13 22:24:127689 root->child_at(2)->effective_frame_policy().container_policy;
Ian Clelland3c5c24522020-06-29 18:51:287690 EXPECT_TRUE(final_effective_policy[0].matches_opaque_src);
Charlie Hu8daefb12020-04-24 19:57:127691 EXPECT_EQ(0UL, final_effective_policy[0].allowed_origins.size());
iclelland92f8c0b2017-04-19 12:43:057692}
7693
Ian Clellandbd2a91a2018-07-07 05:13:577694// Test that creating a new remote frame at the same origin as its parent
Charlie Hu5130d25e2021-03-05 21:53:397695// results in the correct permissions policy in the RemoteSecurityContext.
Ian Clellandbd2a91a2018-07-07 05:13:577696// https://crbug.com/852102
Ian Clellanda5278c822020-12-22 17:59:347697IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Charlie Hubb5943d2021-03-09 19:46:127698 PermissionsPolicyConstructionInExistingProxy) {
Ian Clellandbd2a91a2018-07-07 05:13:577699 WebContentsImpl* contents = web_contents();
Carlos Caballero15caeeb2021-10-27 09:57:557700 FrameTreeNode* root = contents->GetPrimaryFrameTree().root();
Ian Clellandbd2a91a2018-07-07 05:13:577701
7702 // Navigate to a page (1) with a cross-origin iframe (2). After load, the
7703 // frame tree should look like:
7704 //
7705 // a.com(1)
7706 // /
7707 // b.com(2)
7708 EXPECT_TRUE(NavigateToURL(
7709 shell(), embedded_test_server()->GetURL(
7710 "a.com", "/cross_site_iframe_factory.html?a(b)")));
7711
7712 // Programmatically create a new same-origin frame (3) under the root, with a
7713 // cross-origin child (4). Since two SiteInstances already exist at this
7714 // point, a proxy for frame 3 will be created in the renderer for frames 2 and
7715 // 4. The frame tree should look like:
7716 //
7717 // a.com(1)
7718 // / \
7719 // b.com(2) a.com(3)
7720 // \
7721 // b.com(4)
danakj824a7ff2019-02-07 20:34:027722 auto create_subframe_script = JsReplace(
7723 "var f = document.createElement('iframe'); f.src=$1; "
7724 "document.body.appendChild(f);",
7725 embedded_test_server()->GetURL(
7726 "a.com", "/cross_site_iframe_factory.html?a(b{allow-autoplay})"));
Avi Drissmanc91bd8e2021-04-19 23:58:447727 EXPECT_TRUE(ExecJs(root, create_subframe_script));
Ian Clellandbd2a91a2018-07-07 05:13:577728 EXPECT_TRUE(WaitForLoadStop(contents));
7729
7730 // Verify the shape of the frame tree
7731 EXPECT_EQ(2UL, root->child_count());
7732 EXPECT_EQ(1UL, root->child_at(1)->child_count());
7733
7734 // Ask frame 4 to report the enabled state of the autoplay feature. Frame 3's
7735 // policy should allow autoplay if created correctly, as it is same-origin
7736 // with the root, where the feature is enabled by default, and therefore
7737 // should be able to delegate it to frame 4.
7738 // This indirectly tests the replicated policy in frame 3: Because frame 4 is
7739 // cross-origin to frame 3, it will use the proxy's replicated policy as the
7740 // parent policy; otherwise we would just ask frame 3 to report its own state.
Avi Drissmanc91bd8e2021-04-19 23:58:447741 EXPECT_EQ(true, EvalJs(root->child_at(1)->child_at(0),
7742 "document.featurePolicy.allowsFeature('autoplay');"));
Ian Clellandbd2a91a2018-07-07 05:13:577743}
7744
csharrisond86c35bc2017-02-02 17:41:267745// Test harness that allows for "barrier" style delaying of requests matching
7746// certain paths. Call SetDelayedRequestsForPath to delay requests, then
7747// SetUpEmbeddedTestServer to register handlers and start the server.
7748class RequestDelayingSitePerProcessBrowserTest
7749 : public SitePerProcessBrowserTest {
7750 public:
7751 RequestDelayingSitePerProcessBrowserTest()
Jeremy Roman04f27c372017-10-27 15:20:557752 : test_server_(std::make_unique<net::EmbeddedTestServer>()) {}
csharrisond86c35bc2017-02-02 17:41:267753
7754 // Must be called after any calls to SetDelayedRequestsForPath.
7755 void SetUpEmbeddedTestServer() {
csharrisond86c35bc2017-02-02 17:41:267756 SetupCrossSiteRedirector(test_server_.get());
danakjf416ce9d2019-12-11 20:45:457757 test_server_->RegisterRequestHandler(base::BindRepeating(
csharrisond86c35bc2017-02-02 17:41:267758 &RequestDelayingSitePerProcessBrowserTest::HandleMockResource,
7759 base::Unretained(this)));
7760 ASSERT_TRUE(test_server_->Start());
7761 }
7762
7763 // Delays |num_delayed| requests with URLs whose path parts match |path|. When
7764 // the |num_delayed| + 1 request matching the path comes in, the rest are
7765 // unblocked.
7766 // Note: must be called on the UI thread before |test_server_| is started.
7767 void SetDelayedRequestsForPath(const std::string& path, int num_delayed) {
7768 DCHECK_CURRENTLY_ON(BrowserThread::UI);
7769 DCHECK(!test_server_->Started());
7770 num_remaining_requests_to_delay_for_path_[path] = num_delayed;
7771 }
7772
7773 private:
7774 // Called on the test server's thread.
Aaron Tagliaboschid4ad7a302021-09-24 19:51:517775 void AddDelayedResponse(
7776 base::WeakPtr<net::test_server::HttpResponseDelegate> delegate) {
7777 response_closures_.push_back(base::BindOnce(
7778 &net::test_server::HttpResponseDelegate::SendHeadersContentAndFinish,
7779 delegate, net::HTTP_OK, "OK", base::StringPairs(), ""));
csharrisond86c35bc2017-02-02 17:41:267780 }
7781
7782 // Custom embedded test server handler. Looks for requests matching
7783 // num_remaining_requests_to_delay_for_path_, and delays them if necessary. As
7784 // soon as a single request comes in and:
7785 // 1) It matches a delayed path
7786 // 2) No path has any more requests to delay
7787 // Then we release the barrier and finish all delayed requests.
7788 std::unique_ptr<net::test_server::HttpResponse> HandleMockResource(
7789 const net::test_server::HttpRequest& request) {
7790 auto it =
7791 num_remaining_requests_to_delay_for_path_.find(request.GetURL().path());
7792 if (it == num_remaining_requests_to_delay_for_path_.end())
7793 return nullptr;
7794
7795 // If there are requests to delay for this path, make a delayed request
7796 // which will be finished later. Otherwise fall through to the bottom and
7797 // send an empty response.
7798 if (it->second > 0) {
7799 --it->second;
Jeremy Roman04f27c372017-10-27 15:20:557800 return std::make_unique<DelayedResponse>(this);
csharrisond86c35bc2017-02-02 17:41:267801 }
7802 MaybeStartRequests();
Lei Zhangdf291f62021-04-14 17:23:447803 return nullptr;
csharrisond86c35bc2017-02-02 17:41:267804 }
7805
7806 // If there are no more requests to delay, post a series of tasks finishing
7807 // all the delayed tasks. This will be called on the test server's thread.
7808 void MaybeStartRequests() {
7809 for (auto it : num_remaining_requests_to_delay_for_path_) {
7810 if (it.second > 0)
7811 return;
7812 }
Aaron Tagliaboschid4ad7a302021-09-24 19:51:517813 for (auto& it : response_closures_)
David Benjamin34627872019-11-27 20:11:327814 std::move(it).Run();
csharrisond86c35bc2017-02-02 17:41:267815 }
7816
Aaron Tagliaboschid4ad7a302021-09-24 19:51:517817 // This class passes the delegates needed to respond to a request to the
csharrisond86c35bc2017-02-02 17:41:267818 // underlying test fixture.
7819 class DelayedResponse : public net::test_server::BasicHttpResponse {
7820 public:
7821 explicit DelayedResponse(
7822 RequestDelayingSitePerProcessBrowserTest* test_harness)
7823 : test_harness_(test_harness) {}
Peter Boström9b036532021-10-28 23:37:287824
7825 DelayedResponse(const DelayedResponse&) = delete;
7826 DelayedResponse& operator=(const DelayedResponse&) = delete;
7827
Aaron Tagliaboschid4ad7a302021-09-24 19:51:517828 void SendResponse(base::WeakPtr<net::test_server::HttpResponseDelegate>
7829 delegate) override {
7830 test_harness_->AddDelayedResponse(delegate);
csharrisond86c35bc2017-02-02 17:41:267831 }
7832
7833 private:
Keishi Hattori0e45c022021-11-27 09:25:527834 raw_ptr<RequestDelayingSitePerProcessBrowserTest> test_harness_;
csharrisond86c35bc2017-02-02 17:41:267835 };
7836
Aaron Tagliaboschid4ad7a302021-09-24 19:51:517837 // Set of delegates to call which will complete delayed requests. May only be
csharrisond86c35bc2017-02-02 17:41:267838 // modified on the test_server_'s thread.
Aaron Tagliaboschid4ad7a302021-09-24 19:51:517839 std::vector<base::OnceClosure> response_closures_;
csharrisond86c35bc2017-02-02 17:41:267840
7841 // Map from URL paths to the number of requests to delay for that particular
7842 // path. Initialized on the UI thread but modified and read on the test
7843 // server's thread after the |test_server_| is started.
7844 std::map<std::string, int> num_remaining_requests_to_delay_for_path_;
7845
7846 // Don't use embedded_test_server() because this one requires custom
7847 // initialization.
7848 std::unique_ptr<net::EmbeddedTestServer> test_server_;
7849};
7850
7851// Regression tests for https://crbug.com/678206, where the request throttling
7852// in ResourceScheduler was not updated for OOPIFs. This resulted in a single
7853// hung delayable request (e.g. video) starving all other delayable requests.
7854// The tests work by delaying n requests in a cross-domain iframe. Once the n +
7855// 1st request goes through to the network stack (ensuring it was not starved),
7856// the delayed request completed.
7857//
7858// If the logic is not correct, these tests will time out, as the n + 1st
7859// request will never start.
Fergal Daly2e7e1e12020-06-24 09:18:287860IN_PROC_BROWSER_TEST_P(RequestDelayingSitePerProcessBrowserTest,
csharrisond86c35bc2017-02-02 17:41:267861 DelayableSubframeRequestsOneFrame) {
7862 std::string path = "/mock-video.mp4";
7863 SetDelayedRequestsForPath(path, 2);
7864 SetUpEmbeddedTestServer();
7865 GURL url(embedded_test_server()->GetURL(
7866 "a.com", base::StringPrintf("/site_isolation/"
7867 "subframes_with_resources.html?urls=%s&"
7868 "numSubresources=3",
7869 path.c_str())));
7870 EXPECT_TRUE(NavigateToURL(shell(), url));
Chris Fredricksonb854bbf2023-03-27 17:27:447871 EXPECT_EQ(true, EvalJs(shell(), "createFrames()"));
csharrisond86c35bc2017-02-02 17:41:267872}
7873
Fergal Daly2e7e1e12020-06-24 09:18:287874IN_PROC_BROWSER_TEST_P(RequestDelayingSitePerProcessBrowserTest,
csharrisond86c35bc2017-02-02 17:41:267875 DelayableSubframeRequestsTwoFrames) {
7876 std::string path0 = "/mock-video0.mp4";
7877 std::string path1 = "/mock-video1.mp4";
7878 SetDelayedRequestsForPath(path0, 2);
7879 SetDelayedRequestsForPath(path1, 2);
7880 SetUpEmbeddedTestServer();
7881 GURL url(embedded_test_server()->GetURL(
7882 "a.com", base::StringPrintf("/site_isolation/"
7883 "subframes_with_resources.html?urls=%s,%s&"
7884 "numSubresources=3",
7885 path0.c_str(), path1.c_str())));
7886 EXPECT_TRUE(NavigateToURL(shell(), url));
Chris Fredricksonb854bbf2023-03-27 17:27:447887 EXPECT_EQ(true, EvalJs(shell(), "createFrames()"));
csharrisond86c35bc2017-02-02 17:41:267888}
7889
Xiaohan Wang1ecfd002022-01-19 22:33:107890#if BUILDFLAG(IS_ANDROID)
ekaramada06dc6c2017-02-22 20:07:437891class TextSelectionObserver : public TextInputManager::Observer {
7892 public:
7893 explicit TextSelectionObserver(TextInputManager* text_input_manager)
7894 : text_input_manager_(text_input_manager) {
7895 text_input_manager->AddObserver(this);
7896 }
7897
Peter Boström828b9022021-09-21 02:28:437898 TextSelectionObserver(const TextSelectionObserver&) = delete;
7899 TextSelectionObserver& operator=(const TextSelectionObserver&) = delete;
7900
ekaramada06dc6c2017-02-22 20:07:437901 ~TextSelectionObserver() { text_input_manager_->RemoveObserver(this); }
7902
7903 void WaitForSelectedText(const std::string& expected_text) {
7904 if (last_selected_text_ == expected_text)
7905 return;
7906 expected_text_ = expected_text;
7907 loop_runner_ = new MessageLoopRunner();
7908 loop_runner_->Run();
7909 }
7910
7911 private:
7912 void OnTextSelectionChanged(TextInputManager* text_input_manager,
7913 RenderWidgetHostViewBase* updated_view) override {
ekaramad374b2662017-03-02 20:44:527914 last_selected_text_ = base::UTF16ToUTF8(
7915 text_input_manager->GetTextSelection(updated_view)->selected_text());
7916 if (last_selected_text_ == expected_text_ && loop_runner_)
7917 loop_runner_->Quit();
ekaramada06dc6c2017-02-22 20:07:437918 }
ekaramad374b2662017-03-02 20:44:527919
Keishi Hattori0e45c022021-11-27 09:25:527920 const raw_ptr<TextInputManager> text_input_manager_;
ekaramada06dc6c2017-02-22 20:07:437921 std::string last_selected_text_;
7922 std::string expected_text_;
7923 scoped_refptr<MessageLoopRunner> loop_runner_;
ekaramada06dc6c2017-02-22 20:07:437924};
7925
7926class SitePerProcessAndroidImeTest : public SitePerProcessBrowserTest {
7927 public:
7928 SitePerProcessAndroidImeTest() : SitePerProcessBrowserTest() {}
Peter Boström828b9022021-09-21 02:28:437929
7930 SitePerProcessAndroidImeTest(const SitePerProcessAndroidImeTest&) = delete;
7931 SitePerProcessAndroidImeTest& operator=(const SitePerProcessAndroidImeTest&) =
7932 delete;
7933
ekaramada06dc6c2017-02-22 20:07:437934 ~SitePerProcessAndroidImeTest() override {}
7935
7936 protected:
7937 ImeAdapterAndroid* ime_adapter() {
7938 return static_cast<RenderWidgetHostViewAndroid*>(
7939 web_contents()->GetRenderWidgetHostView())
7940 ->ime_adapter_for_testing();
7941 }
7942
ekaramada06dc6c2017-02-22 20:07:437943 void FocusInputInFrame(RenderFrameHostImpl* frame) {
Avi Drissmanc91bd8e2021-04-19 23:58:447944 ASSERT_TRUE(ExecJs(frame, "window.focus(); input.focus();"));
ekaramada06dc6c2017-02-22 20:07:437945 }
7946
7947 // Creates a page with multiple (nested) OOPIFs and populates all of them
7948 // with an <input> element along with the required handlers for the test.
7949 void LoadPage() {
7950 ASSERT_TRUE(NavigateToURL(
7951 shell(),
7952 GURL(embedded_test_server()->GetURL(
7953 "a.com", "/cross_site_iframe_factory.html?a(b,c(a(b)))"))));
Carlos Caballero15caeeb2021-10-27 09:57:557954 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
ekaramada06dc6c2017-02-22 20:07:437955 frames_.push_back(root->current_frame_host());
7956 frames_.push_back(root->child_at(0)->current_frame_host());
7957 frames_.push_back(root->child_at(1)->current_frame_host());
7958 frames_.push_back(root->child_at(1)->child_at(0)->current_frame_host());
7959 frames_.push_back(
7960 root->child_at(1)->child_at(0)->child_at(0)->current_frame_host());
7961
7962 // Adds an <input> to frame and sets up a handler for |window.oninput|. When
7963 // the input event is fired (by changing the value of <input> element), the
7964 // handler will select all the text so that the corresponding text selection
7965 // update on the browser side notifies the test about input insertion.
7966 std::string add_input_script =
7967 "var input = document.createElement('input');"
7968 "document.body.appendChild(input);"
7969 "window.oninput = function() {"
7970 " input.select();"
7971 "};";
7972
Ali Hijazie63cbaf62023-12-20 19:29:357973 for (content::RenderFrameHostImpl* frame : frames_) {
Avi Drissmanc91bd8e2021-04-19 23:58:447974 ASSERT_TRUE(ExecJs(frame, add_input_script));
Ali Hijazie63cbaf62023-12-20 19:29:357975 }
ekaramada06dc6c2017-02-22 20:07:437976 }
7977
7978 // This methods tries to commit |text| by simulating a native call from Java.
7979 void CommitText(const char* text) {
7980 JNIEnv* env = base::android::AttachCurrentThread();
7981
7982 // A valid caller is needed for ImeAdapterAndroid::GetUnderlinesFromSpans.
7983 base::android::ScopedJavaLocalRef<jobject> caller =
7984 ime_adapter()->java_ime_adapter_for_testing(env);
7985
7986 // Input string from Java side.
7987 base::android::ScopedJavaLocalRef<jstring> jtext =
7988 base::android::ConvertUTF8ToJavaString(env, text);
7989
7990 // Simulating a native call from Java side.
Andrew Grievecb2a3f02025-07-22 20:24:437991 ime_adapter()->CommitText(env, caller, jtext, jtext, 0);
ekaramada06dc6c2017-02-22 20:07:437992 }
7993
Ali Hijazie63cbaf62023-12-20 19:29:357994 std::vector<raw_ptr<RenderFrameHostImpl, VectorExperimental>> frames_;
ekaramada06dc6c2017-02-22 20:07:437995};
7996
7997// This test verifies that committing text will be applied on the focused
7998// RenderWidgetHost.
Fergal Daly2e7e1e12020-06-24 09:18:287999IN_PROC_BROWSER_TEST_P(SitePerProcessAndroidImeTest,
ekaramada06dc6c2017-02-22 20:07:438000 CommitTextForFocusedWidget) {
8001 LoadPage();
8002 TextSelectionObserver selection_observer(
8003 web_contents()->GetTextInputManager());
8004 for (size_t index = 0; index < frames_.size(); ++index) {
8005 std::string text = base::StringPrintf("text%zu", index);
8006 FocusInputInFrame(frames_[index]);
8007 CommitText(text.c_str());
8008 selection_observer.WaitForSelectedText(text);
8009 }
8010}
Xiaohan Wang1ecfd002022-01-19 22:33:108011#endif // BUILDFLAG(IS_ANDROID)
ekaramada06dc6c2017-02-22 20:07:438012
alexmos0431b6b2017-03-01 02:52:438013// Test that an OOPIF at b.com can navigate to a cross-site a.com URL that
8014// transfers back to b.com. See https://crbug.com/681077#c10 and
8015// https://crbug.com/660407.
Fergal Daly2e7e1e12020-06-24 09:18:288016IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmos0431b6b2017-03-01 02:52:438017 SubframeTransfersToCurrentRFH) {
8018 GURL main_url(embedded_test_server()->GetURL(
8019 "a.com", "/cross_site_iframe_factory.html?a(b)"));
8020 ASSERT_TRUE(NavigateToURL(shell(), main_url));
8021
8022 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:558023 ->GetPrimaryFrameTree()
8024 .root();
alexmos0431b6b2017-03-01 02:52:438025 scoped_refptr<SiteInstanceImpl> b_site_instance =
8026 root->child_at(0)->current_frame_host()->GetSiteInstance();
8027
8028 // Navigate subframe to a URL that will redirect from a.com back to b.com.
8029 // This navigation shouldn't time out. Also ensure that the pending RFH
8030 // that was created for a.com is destroyed.
8031 GURL frame_url(
8032 embedded_test_server()->GetURL("a.com", "/cross-site/b.com/title2.html"));
8033 NavigateIframeToURL(shell()->web_contents(), "child-0", frame_url);
clamy610c63b32017-12-22 15:05:188034 EXPECT_FALSE(root->child_at(0)->render_manager()->speculative_frame_host());
alexmos0431b6b2017-03-01 02:52:438035 GURL redirected_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
8036 EXPECT_EQ(root->child_at(0)->current_url(), redirected_url);
8037 EXPECT_EQ(b_site_instance,
8038 root->child_at(0)->current_frame_host()->GetSiteInstance());
8039
8040 // Try the same navigation, but use the browser-initiated path.
8041 NavigateFrameToURL(root->child_at(0), frame_url);
clamy610c63b32017-12-22 15:05:188042 EXPECT_FALSE(root->child_at(0)->render_manager()->speculative_frame_host());
alexmos0431b6b2017-03-01 02:52:438043 EXPECT_EQ(root->child_at(0)->current_url(), redirected_url);
8044 EXPECT_EQ(b_site_instance,
8045 root->child_at(0)->current_frame_host()->GetSiteInstance());
8046}
8047
Fergal Daly2e7e1e12020-06-24 09:18:288048IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Daniel Cheng999698bd2017-03-22 04:56:378049 FrameSwapPreservesUniqueName) {
8050 GURL main_url(embedded_test_server()->GetURL(
8051 "a.com", "/cross_site_iframe_factory.html?a(a)"));
8052 ASSERT_TRUE(NavigateToURL(shell(), main_url));
8053
8054 // Navigate the subframe cross-site…
8055 {
8056 GURL url(embedded_test_server()->GetURL("b.com", "/title1.html"));
8057 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "child-0", url));
8058 }
8059 // and then same-site…
8060 {
8061 GURL url(embedded_test_server()->GetURL("a.com", "/title1.html"));
8062 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "child-0", url));
8063 }
8064 // and cross-site once more.
8065 {
8066 GURL url(embedded_test_server()->GetURL("b.com", "/title1.html"));
8067 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "child-0", url));
8068 }
8069
8070 // Inspect the navigation entries and make sure that the navigation target
8071 // remained constant across frame swaps.
Lucas Furukawa Gadani5553a1582019-01-08 18:55:578072 auto& controller = static_cast<NavigationControllerImpl&>(
Daniel Cheng999698bd2017-03-22 04:56:378073 shell()->web_contents()->GetController());
8074 EXPECT_EQ(4, controller.GetEntryCount());
8075
8076 std::set<std::string> names;
8077 for (int i = 0; i < controller.GetEntryCount(); ++i) {
8078 NavigationEntryImpl::TreeNode* root =
8079 controller.GetEntryAtIndex(i)->root_node();
8080 ASSERT_EQ(1U, root->children.size());
8081 names.insert(root->children[0]->frame_entry->frame_unique_name());
8082 }
8083
8084 // More than one entry in the set means that the subframe frame navigation
8085 // entries didn't have a consistent unique name. This will break history
8086 // navigations =(
8087 EXPECT_THAT(names, SizeIs(1)) << "Mismatched names for subframe!";
8088}
8089
arthursonzognibc5732b52017-05-17 03:37:288090// Tests that POST body is not lost when it targets a OOPIF.
8091// See https://crbug.com/710937.
Fergal Daly2e7e1e12020-06-24 09:18:288092IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, PostTargetSubFrame) {
arthursonzognibc5732b52017-05-17 03:37:288093 // Navigate to a page with an OOPIF.
8094 GURL main_url(
8095 embedded_test_server()->GetURL("/frame_tree/page_with_one_frame.html"));
8096 EXPECT_TRUE(NavigateToURL(shell(), main_url));
8097 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:558098 ->GetPrimaryFrameTree()
8099 .root();
arthursonzognibc5732b52017-05-17 03:37:288100
8101 // The main frame and the subframe live on different processes.
8102 EXPECT_EQ(1u, root->child_count());
8103 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
8104 root->child_at(0)->current_frame_host()->GetSiteInstance());
8105
8106 // Make a form submission from the main frame and target the OOPIF.
8107 GURL form_url(embedded_test_server()->GetURL("/echoall"));
8108 TestNavigationObserver form_post_observer(shell()->web_contents(), 1);
Avi Drissmanc91bd8e2021-04-19 23:58:448109 EXPECT_TRUE(ExecJs(shell()->web_contents(), JsReplace(R"(
arthursonzognibc5732b52017-05-17 03:37:288110 var form = document.createElement('form');
8111
8112 // POST form submission to /echoall.
8113 form.setAttribute("method", "POST");
danakj824a7ff2019-02-07 20:34:028114 form.setAttribute("action", $1);
arthursonzognibc5732b52017-05-17 03:37:288115
8116 // Target the OOPIF.
8117 form.setAttribute("target", "child-name-0");
8118
8119 // Add some POST data: "my_token=my_value";
8120 var input = document.createElement("input");
8121 input.setAttribute("type", "hidden");
8122 input.setAttribute("name", "my_token");
8123 input.setAttribute("value", "my_value");
8124 form.appendChild(input);
8125
8126 // Submit the form.
8127 document.body.appendChild(form);
8128 form.submit();
danakj824a7ff2019-02-07 20:34:028129 )",
Avi Drissmanc91bd8e2021-04-19 23:58:448130 form_url)));
arthursonzognibc5732b52017-05-17 03:37:288131 form_post_observer.Wait();
8132
8133 NavigationEntryImpl* entry = static_cast<NavigationEntryImpl*>(
8134 shell()->web_contents()->GetController().GetLastCommittedEntry());
8135 // TODO(arthursonzogni): This is wrong. The last committed entry was
8136 // renderer-initiated. See https://crbug.com/722251.
8137 EXPECT_FALSE(entry->is_renderer_initiated());
8138
8139 // Verify that POST body was correctly passed to the server and ended up in
8140 // the body of the page.
Nick Carterb7e71312018-08-03 23:36:138141 EXPECT_EQ("my_token=my_value\n",
8142 EvalJs(root->child_at(0),
8143 "document.getElementsByTagName('pre')[0].innerText;"));
arthursonzognibc5732b52017-05-17 03:37:288144}
8145
Alex Moshchuk5bb1f9442018-01-27 07:41:328146// Tests that POST method and body is not lost when an OOPIF submits a form
8147// that targets the main frame. See https://crbug.com/806215.
Fergal Daly2e7e1e12020-06-24 09:18:288148IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Alex Moshchuk5bb1f9442018-01-27 07:41:328149 PostTargetsMainFrameFromOOPIF) {
8150 // Navigate to a page with an OOPIF.
8151 GURL main_url(
8152 embedded_test_server()->GetURL("/frame_tree/page_with_one_frame.html"));
8153 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:558154 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchuk5bb1f9442018-01-27 07:41:328155
8156 // The main frame and the subframe live on different processes.
8157 EXPECT_EQ(1u, root->child_count());
8158 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
8159 root->child_at(0)->current_frame_host()->GetSiteInstance());
8160
8161 // Make a form submission from the subframe and target its parent frame.
8162 GURL form_url(embedded_test_server()->GetURL("/echoall"));
8163 TestNavigationObserver form_post_observer(web_contents());
Avi Drissmanc91bd8e2021-04-19 23:58:448164 EXPECT_TRUE(
8165 ExecJs(root->child_at(0)->current_frame_host(), JsReplace(R"(
Alex Moshchuk5bb1f9442018-01-27 07:41:328166 var form = document.createElement('form');
8167
8168 // POST form submission to /echoall.
8169 form.setAttribute("method", "POST");
danakj824a7ff2019-02-07 20:34:028170 form.setAttribute("action", $1);
Alex Moshchuk5bb1f9442018-01-27 07:41:328171
8172 // Target the parent.
8173 form.setAttribute("target", "_parent");
8174
8175 // Add some POST data: "my_token=my_value";
8176 var input = document.createElement("input");
8177 input.setAttribute("type", "hidden");
8178 input.setAttribute("name", "my_token");
8179 input.setAttribute("value", "my_value");
8180 form.appendChild(input);
8181
8182 // Submit the form.
8183 document.body.appendChild(form);
8184 form.submit();
danakj824a7ff2019-02-07 20:34:028185 )",
Avi Drissmanc91bd8e2021-04-19 23:58:448186 form_url)));
Alex Moshchuk5bb1f9442018-01-27 07:41:328187 form_post_observer.Wait();
8188
8189 // Verify that the FrameNavigationEntry's method is POST.
8190 NavigationEntryImpl* entry = static_cast<NavigationEntryImpl*>(
8191 web_contents()->GetController().GetLastCommittedEntry());
8192 EXPECT_EQ("POST", entry->root_node()->frame_entry->method());
8193
8194 // Verify that POST body was correctly passed to the server and ended up in
8195 // the body of the page.
Avi Drissmanc91bd8e2021-04-19 23:58:448196 EXPECT_EQ("my_token=my_value\n",
8197 EvalJs(root, "document.getElementsByTagName('pre')[0].innerText"));
Alex Moshchuk5bb1f9442018-01-27 07:41:328198
8199 // Reload the main frame and ensure the POST body is preserved. This checks
8200 // that the POST body was saved in the FrameNavigationEntry.
8201 web_contents()->GetController().Reload(ReloadType::NORMAL,
8202 false /* check_for_repost */);
8203 EXPECT_TRUE(WaitForLoadStop(web_contents()));
Avi Drissmanc91bd8e2021-04-19 23:58:448204 EXPECT_EQ("my_token=my_value\n",
8205 EvalJs(root, "document.getElementsByTagName('pre')[0].innerText"));
Alex Moshchuk5bb1f9442018-01-27 07:41:328206}
8207
alexmosc5e91faf2017-05-25 02:55:018208// Verify that a remote-to-local main frame navigation doesn't overwrite
8209// the previous history entry. See https://crbug.com/725716.
Fergal Daly2e7e1e12020-06-24 09:18:288210IN_PROC_BROWSER_TEST_P(
Luciano Pachecoc2072682018-07-02 06:36:298211 SitePerProcessBrowserTest,
8212 DISABLED_CrossProcessMainFrameNavigationDoesNotOverwriteHistory) {
alexmosc5e91faf2017-05-25 02:55:018213 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
8214 GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title2.html"));
8215
8216 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
8217
8218 // Open a same-site popup to keep the www.foo.com process alive.
8219 OpenPopup(shell(), GURL(url::kAboutBlankURL), "foo");
8220
8221 // Navigate foo -> bar -> foo.
Alex Moshchuk7e26eca2018-03-03 01:34:298222 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), bar_url));
8223 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), foo_url));
alexmosc5e91faf2017-05-25 02:55:018224
8225 // There should be three history entries.
8226 EXPECT_EQ(3, web_contents()->GetController().GetEntryCount());
8227
8228 // Go back: this should go to bar.com.
8229 {
8230 TestNavigationObserver back_observer(web_contents());
8231 web_contents()->GetController().GoBack();
8232 back_observer.Wait();
8233 }
Dave Tapuska327c06c92022-06-13 20:31:518234 EXPECT_EQ(bar_url,
8235 web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL());
alexmosc5e91faf2017-05-25 02:55:018236
8237 // Go back again. This should go to foo.com.
8238 {
8239 TestNavigationObserver back_observer(web_contents());
8240 web_contents()->GetController().GoBack();
8241 back_observer.Wait();
8242 }
Dave Tapuska327c06c92022-06-13 20:31:518243 EXPECT_EQ(foo_url,
8244 web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL());
alexmosc5e91faf2017-05-25 02:55:018245}
8246
Greg Thompsonea796b72021-03-16 11:53:528247// The test is flaky on Linux, Chrome OS, etc; cf https://crbug.com/1170583.
Xiaohan Wang1ecfd002022-01-19 22:33:108248#if BUILDFLAG(IS_POSIX)
Antonio Sartoribb80e23b2021-02-05 13:59:128249#define MAYBE_CrossProcessInertSubframe DISABLED_CrossProcessInertSubframe
8250#else
8251#define MAYBE_CrossProcessInertSubframe CrossProcessInertSubframe
8252#endif
Oriol Brufaua486ffc2022-02-18 18:05:488253// Tests that when an out-of-process iframe becomes inert due to a modal
8254// <dialog> element, the contents of the iframe can still take focus.
Antonio Sartoribb80e23b2021-02-05 13:59:128255IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
8256 MAYBE_CrossProcessInertSubframe) {
kenrb04323782017-06-23 01:23:328257 // This uses a(b,b) instead of a(b) to preserve the b.com process even when
8258 // the first subframe is navigated away from it.
8259 GURL main_url(embedded_test_server()->GetURL(
8260 "a.com", "/cross_site_iframe_factory.html?a(b,b)"));
8261 EXPECT_TRUE(NavigateToURL(shell(), main_url));
8262
8263 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:558264 ->GetPrimaryFrameTree()
8265 .root();
kenrb04323782017-06-23 01:23:328266 ASSERT_EQ(2U, root->child_count());
8267
8268 FrameTreeNode* iframe_node = root->child_at(0);
8269
Avi Drissmanc91bd8e2021-04-19 23:58:448270 EXPECT_TRUE(ExecJs(
kenrb04323782017-06-23 01:23:328271 iframe_node,
8272 "document.head.innerHTML = '';"
8273 "document.body.innerHTML = '<input id=\"text1\"> <input id=\"text2\">';"
8274 "text1.focus();"));
8275
kenrb04323782017-06-23 01:23:328276 // Add a <dialog> to the root frame and call showModal on it.
Avi Drissmanc91bd8e2021-04-19 23:58:448277 EXPECT_TRUE(ExecJs(root,
8278 "let dialog = "
8279 "document.body.appendChild(document.createElement('"
8280 "dialog'));"
8281 "dialog.innerHTML = 'Modal dialog <input>';"
8282 "dialog.showModal();"));
kenrb04323782017-06-23 01:23:328283
Ken Buchanan5005b6132018-11-21 23:39:228284 // Yield the UI thread to ensure that the real SetIsInert message
kenrb04323782017-06-23 01:23:328285 // handler runs, in order to guarantee that the update arrives at the
8286 // renderer process before the script below.
Ken Buchanan5005b6132018-11-21 23:39:228287 base::RunLoop().RunUntilIdle();
kenrb04323782017-06-23 01:23:328288
Antonio Gomes8942d9232020-02-12 14:12:018289 RenderFrameProxyHost* root_proxy =
8290 iframe_node->render_manager()->GetProxyToParent();
8291 EXPECT_TRUE(root_proxy->IsInertForTesting());
8292
kenrb04323782017-06-23 01:23:328293 std::string focused_element;
8294
Oriol Brufaua486ffc2022-02-18 18:05:488295 // Attempt to change focus in the inert subframe. This should work.
kenrb04323782017-06-23 01:23:328296 // The setTimeout ensures that the inert bit can propagate before the
8297 // test JS code runs.
Chris Fredricksonb854bbf2023-03-27 17:27:448298 EXPECT_EQ("text2", EvalJs(iframe_node,
8299 "new Promise(resolve => {"
8300 " window.setTimeout(() => {"
8301 " text2.focus();"
8302 " resolve(document.activeElement.id);"
8303 " }, 0);"
8304 "});"));
kenrb04323782017-06-23 01:23:328305
8306 // Navigate the child frame to another site, so that it moves into a new
8307 // process.
8308 GURL site_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:208309 EXPECT_TRUE(NavigateToURLFromRenderer(iframe_node, site_url));
kenrb04323782017-06-23 01:23:328310
Lukasz Anforowicz69c25dfd2020-11-12 21:50:208311 // NavigateToURLFromRenderer returns when the navigation commits, at which
8312 // point frame state has to be re-sent to the new frame. Yield the thread to
8313 // prevent races with the inertness update.
Ken Buchanan5005b6132018-11-21 23:39:228314 base::RunLoop().RunUntilIdle();
8315
Avi Drissmanc91bd8e2021-04-19 23:58:448316 EXPECT_TRUE(ExecJs(
kenrb04323782017-06-23 01:23:328317 iframe_node,
8318 "document.head.innerHTML = '';"
8319 "document.body.innerHTML = '<input id=\"text1\"> <input id=\"text2\">';"
8320 "text1.focus();"));
8321
Oriol Brufaua486ffc2022-02-18 18:05:488322 // Verify we can still set focus after the navigation.
Chris Fredricksonb854bbf2023-03-27 17:27:448323 EXPECT_EQ("text2", EvalJs(iframe_node,
8324 "text2.focus();"
8325 "document.activeElement.id;"));
kenrb04323782017-06-23 01:23:328326
8327 // Navigate the subframe back into its parent process to verify that the
Oriol Brufaua486ffc2022-02-18 18:05:488328 // new local frame remains non-inert.
kenrb04323782017-06-23 01:23:328329 GURL same_site_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:208330 EXPECT_TRUE(NavigateToURLFromRenderer(iframe_node, same_site_url));
kenrb04323782017-06-23 01:23:328331
Avi Drissmanc91bd8e2021-04-19 23:58:448332 EXPECT_TRUE(ExecJs(
kenrb04323782017-06-23 01:23:328333 iframe_node,
8334 "document.head.innerHTML = '';"
8335 "document.body.innerHTML = '<input id=\"text1\"> <input id=\"text2\">';"
8336 "text1.focus();"));
8337
Oriol Brufaua486ffc2022-02-18 18:05:488338 // Verify we can still set focus after the navigation.
Chris Fredricksonb854bbf2023-03-27 17:27:448339 EXPECT_EQ("text2", EvalJs(iframe_node,
8340 "text2.focus();"
8341 "document.activeElement.id;"));
kenrb04323782017-06-23 01:23:328342}
8343
Oriol Brufaua486ffc2022-02-18 18:05:488344// Tests that IsInert frame flag is correctly updated and propagated.
8345IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
8346 CrossProcessIsInertPropagation) {
Oriol Brufaua486ffc2022-02-18 18:05:488347 GURL main_url(embedded_test_server()->GetURL(
8348 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
8349 EXPECT_TRUE(NavigateToURL(shell(), main_url));
8350
8351 FrameTreeNode* frame_a =
8352 static_cast<WebContentsImpl*>(shell()->web_contents())
8353 ->GetPrimaryFrameTree()
8354 .root();
8355 ASSERT_EQ(1U, frame_a->child_count());
8356 FrameTreeNode* frame_b = frame_a->child_at(0);
8357 ASSERT_EQ(1U, frame_b->child_count());
8358 FrameTreeNode* frame_c = frame_b->child_at(0);
8359 RenderFrameProxyHost* proxy_b = frame_b->render_manager()->GetProxyToParent();
8360 RenderFrameProxyHost* proxy_c = frame_c->render_manager()->GetProxyToParent();
8361
8362 auto waitForInertPropagated = [&]() {
8363 // Force layout. This recomputes the element styles so that the <iframe>
8364 // gets the updated ComputedStyle::IsInert() flag. This triggers an update
8365 // of the associated RenderFrameProxyHost::IsInertForTesting().
8366 for (FrameTreeNode* frame : {frame_a, frame_b, frame_c})
8367 ExecuteScriptAsync(frame, "document.body.offsetLeft");
8368
8369 // Propagating the inert flag requires sending messages in between the
8370 // browser and the renderers. Since they are using the same mojo interfaces
8371 // as ExecJs, waiting for an browser<->renderer roundtrip using ExecJs
8372 // should be enough to guarantee it has been propagate.
8373 for (FrameTreeNode* frame : {frame_a, frame_b, frame_c})
8374 EXPECT_TRUE(ExecJs(frame, "'Done'"));
8375 };
8376
8377 waitForInertPropagated();
8378 EXPECT_FALSE(proxy_b->IsInertForTesting());
8379 EXPECT_FALSE(proxy_c->IsInertForTesting());
8380
8381 // Make b inert, this should also make c inert.
8382 EXPECT_TRUE(ExecJs(frame_a, "document.body.inert = true;"));
8383 waitForInertPropagated();
8384 EXPECT_TRUE(proxy_b->IsInertForTesting());
8385 EXPECT_TRUE(proxy_c->IsInertForTesting());
8386
8387 // Make b non-inert, this should also make c non-inert.
8388 EXPECT_TRUE(ExecJs(frame_a, "document.body.inert = false;"));
8389 waitForInertPropagated();
8390 EXPECT_FALSE(proxy_b->IsInertForTesting());
8391 EXPECT_FALSE(proxy_c->IsInertForTesting());
8392
8393 // Make c inert.
8394 EXPECT_TRUE(ExecJs(frame_b, "document.body.inert = true;"));
8395 waitForInertPropagated();
8396 EXPECT_FALSE(proxy_b->IsInertForTesting());
8397 EXPECT_TRUE(proxy_c->IsInertForTesting());
8398
8399 // Make b inert, c should continue being inert.
8400 EXPECT_TRUE(ExecJs(frame_a, "document.body.inert = true;"));
8401 waitForInertPropagated();
8402 EXPECT_TRUE(proxy_b->IsInertForTesting());
8403 EXPECT_TRUE(proxy_c->IsInertForTesting());
8404
8405 // Try to make c non-inert, it should still be inert due to b.
8406 EXPECT_TRUE(ExecJs(frame_b, "document.body.inert = false;"));
8407 waitForInertPropagated();
8408 EXPECT_TRUE(proxy_b->IsInertForTesting());
8409 EXPECT_TRUE(proxy_c->IsInertForTesting());
8410
8411 // Make b non-inert, this should also make c non-inert.
8412 EXPECT_TRUE(ExecJs(frame_a, "document.body.inert = false;"));
8413 waitForInertPropagated();
8414 EXPECT_FALSE(proxy_b->IsInertForTesting());
8415 EXPECT_FALSE(proxy_c->IsInertForTesting());
8416
8417 // Make b anc inert.
8418 EXPECT_TRUE(ExecJs(frame_a, "document.body.inert = true;"));
8419 EXPECT_TRUE(ExecJs(frame_b, "document.body.inert = true;"));
8420 waitForInertPropagated();
8421 EXPECT_TRUE(proxy_b->IsInertForTesting());
8422 EXPECT_TRUE(proxy_c->IsInertForTesting());
8423
8424 // Make b non-inert, c should continue being inert.
8425 EXPECT_TRUE(ExecJs(frame_a, "document.body.inert = false;"));
8426 waitForInertPropagated();
8427 EXPECT_FALSE(proxy_b->IsInertForTesting());
8428 EXPECT_TRUE(proxy_c->IsInertForTesting());
8429}
8430
alexmos13fe1962017-06-28 04:25:128431// Check that main frames for the same site rendering in unrelated tabs start
8432// sharing processes that are already dedicated to that site when over process
8433// limit. See https://crbug.com/513036.
Fergal Daly2e7e1e12020-06-24 09:18:288434IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
alexmos13fe1962017-06-28 04:25:128435 MainFrameProcessReuseWhenOverLimit) {
8436 // Set the process limit to 1.
8437 RenderProcessHost::SetMaxRendererProcessCount(1);
8438
8439 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
8440 ASSERT_TRUE(NavigateToURL(shell(), url_a));
8441
Carlos Caballero15caeeb2021-10-27 09:57:558442 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos13fe1962017-06-28 04:25:128443
8444 // Create an unrelated shell window.
8445 GURL url_b(embedded_test_server()->GetURL("b.com", "/title2.html"));
8446 Shell* new_shell = CreateBrowser();
8447 EXPECT_TRUE(NavigateToURL(new_shell, url_b));
8448
8449 FrameTreeNode* new_shell_root =
8450 static_cast<WebContentsImpl*>(new_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:558451 ->GetPrimaryFrameTree()
8452 .root();
alexmos13fe1962017-06-28 04:25:128453
8454 // The new window's b.com root should not reuse the a.com process.
8455 EXPECT_NE(root->current_frame_host()->GetProcess(),
8456 new_shell_root->current_frame_host()->GetProcess());
8457
8458 // Navigating the new window to a.com should reuse the first window's
8459 // process.
8460 EXPECT_TRUE(NavigateToURL(new_shell, url_a));
8461 EXPECT_EQ(root->current_frame_host()->GetProcess(),
8462 new_shell_root->current_frame_host()->GetProcess());
8463}
8464
8465// Check that subframes for the same site rendering in unrelated tabs start
8466// sharing processes that are already dedicated to that site when over process
8467// limit. See https://crbug.com/513036.
Fergal Daly2e7e1e12020-06-24 09:18:288468IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Alex Moshchuk777a6372017-07-25 21:59:438469 SubframeProcessReuseWhenOverLimit) {
alexmos13fe1962017-06-28 04:25:128470 // Set the process limit to 1.
8471 RenderProcessHost::SetMaxRendererProcessCount(1);
8472
8473 GURL first_url(embedded_test_server()->GetURL(
8474 "a.com", "/cross_site_iframe_factory.html?a(b,b(c))"));
8475 ASSERT_TRUE(NavigateToURL(shell(), first_url));
8476
Carlos Caballero15caeeb2021-10-27 09:57:558477 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
alexmos13fe1962017-06-28 04:25:128478
8479 // Processes for dedicated sites should never be reused.
8480 EXPECT_NE(root->current_frame_host()->GetProcess(),
8481 root->child_at(0)->current_frame_host()->GetProcess());
8482 EXPECT_NE(root->current_frame_host()->GetProcess(),
8483 root->child_at(1)->current_frame_host()->GetProcess());
8484 EXPECT_NE(root->current_frame_host()->GetProcess(),
8485 root->child_at(1)->child_at(0)->current_frame_host()->GetProcess());
8486 EXPECT_NE(root->child_at(1)->current_frame_host()->GetProcess(),
8487 root->child_at(1)->child_at(0)->current_frame_host()->GetProcess());
8488 EXPECT_EQ(root->child_at(0)->current_frame_host()->GetProcess(),
8489 root->child_at(1)->current_frame_host()->GetProcess());
8490
8491 // Create an unrelated shell window.
8492 Shell* new_shell = CreateBrowser();
8493
8494 GURL new_shell_url(embedded_test_server()->GetURL(
8495 "d.com", "/cross_site_iframe_factory.html?d(a(b))"));
8496 ASSERT_TRUE(NavigateToURL(new_shell, new_shell_url));
8497
8498 FrameTreeNode* new_shell_root =
8499 static_cast<WebContentsImpl*>(new_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:558500 ->GetPrimaryFrameTree()
8501 .root();
alexmos13fe1962017-06-28 04:25:128502
8503 // New tab's root (d.com) should go into a separate process.
8504 EXPECT_NE(root->current_frame_host()->GetProcess(),
8505 new_shell_root->current_frame_host()->GetProcess());
8506 EXPECT_NE(root->child_at(0)->current_frame_host()->GetProcess(),
8507 new_shell_root->current_frame_host()->GetProcess());
8508 EXPECT_NE(root->child_at(1)->child_at(0)->current_frame_host()->GetProcess(),
8509 new_shell_root->current_frame_host()->GetProcess());
8510
8511 // The new tab's subframe should reuse the a.com process.
8512 EXPECT_EQ(root->current_frame_host()->GetProcess(),
8513 new_shell_root->child_at(0)->current_frame_host()->GetProcess());
8514
8515 // The new tab's grandchild frame should reuse the b.com process.
8516 EXPECT_EQ(root->child_at(0)->current_frame_host()->GetProcess(),
8517 new_shell_root->child_at(0)
8518 ->child_at(0)
8519 ->current_frame_host()
8520 ->GetProcess());
8521}
8522
Alex Moshchuk27caae82017-09-11 23:11:188523// Check that when a main frame and a subframe start navigating to the same
8524// cross-site URL at the same time, the new RenderFrame for the subframe is
8525// created successfully without crashing, and the navigations complete
8526// successfully. This test checks the scenario where the main frame ends up
8527// committing before the subframe, and the test below checks the case where the
8528// subframe commits first.
8529//
8530// This used to be problematic in that the main frame navigation created an
8531// active RenderViewHost with a RenderFrame already swapped into the tree, and
8532// then while that navigation was still pending, the subframe navigation
8533// created its RenderFrame, which crashed when referencing its parent by a
8534// proxy which didn't exist.
8535//
Dave Tapuska2402595f2022-08-03 16:24:218536// All cross-process navigations now require creating a `blink::RemoteFrame`
8537// before creating a RenderFrame, which makes such navigations follow the
8538// provisional frame (remote-to-local navigation) paths, where such a scenario
8539// is no longer possible. See https://crbug.com/756790.
Fergal Daly2e7e1e12020-06-24 09:18:288540IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Alex Moshchuk27caae82017-09-11 23:11:188541 TwoCrossSitePendingNavigationsAndMainFrameWins) {
Alex Moshchuk27caae82017-09-11 23:11:188542 GURL main_url(embedded_test_server()->GetURL(
8543 "a.com", "/cross_site_iframe_factory.html?a(a)"));
8544 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:558545 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchuk27caae82017-09-11 23:11:188546 FrameTreeNode* child = root->child_at(0);
8547
8548 // Navigate both frames cross-site to b.com simultaneously.
8549 GURL new_url_1(embedded_test_server()->GetURL("b.com", "/title1.html"));
8550 GURL new_url_2(embedded_test_server()->GetURL("b.com", "/title2.html"));
8551 TestNavigationManager manager1(web_contents(), new_url_1);
8552 TestNavigationManager manager2(web_contents(), new_url_2);
danakj824a7ff2019-02-07 20:34:028553 auto script = JsReplace("location = $1; frames[0].location = $2;", new_url_1,
8554 new_url_2);
Avi Drissmanc91bd8e2021-04-19 23:58:448555 EXPECT_TRUE(ExecJs(web_contents(), script));
Alex Moshchuk27caae82017-09-11 23:11:188556
8557 // Wait for main frame request, but don't commit it yet. This should create
8558 // a speculative RenderFrameHost.
Jiacheng Guo4bdd0be2024-06-11 23:35:218559 manager1.WaitForSpeculativeRenderFrameHostCreation();
Alex Moshchuk27caae82017-09-11 23:11:188560 RenderFrameHostImpl* root_speculative_rfh =
8561 root->render_manager()->speculative_frame_host();
8562 EXPECT_TRUE(root_speculative_rfh);
arthursonzognia2754522019-07-03 18:25:368563 scoped_refptr<SiteInstanceImpl> b_root_site_instance(
Alex Moshchuk27caae82017-09-11 23:11:188564 root_speculative_rfh->GetSiteInstance());
8565
8566 // There should now be a live b.com proxy for the root, since it is doing a
8567 // cross-process navigation.
8568 RenderFrameProxyHost* root_proxy =
Harkiran Bolariad22a1dca2022-02-22 17:01:128569 root->current_frame_host()
8570 ->browsing_context_state()
8571 ->GetRenderFrameProxyHost(b_root_site_instance->group());
Alex Moshchuk27caae82017-09-11 23:11:188572 EXPECT_TRUE(root_proxy);
8573 EXPECT_TRUE(root_proxy->is_render_frame_proxy_live());
8574
8575 // Wait for subframe request, but don't commit it yet.
Jiacheng Guo4bdd0be2024-06-11 23:35:218576 manager2.WaitForSpeculativeRenderFrameHostCreation();
arthursonzognia2754522019-07-03 18:25:368577 RenderFrameHostImpl* subframe_speculative_rfh =
8578 child->render_manager()->speculative_frame_host();
Alex Moshchuk27caae82017-09-11 23:11:188579 EXPECT_TRUE(child->render_manager()->speculative_frame_host());
arthursonzognia2754522019-07-03 18:25:368580 scoped_refptr<SiteInstanceImpl> b_subframe_site_instance(
8581 subframe_speculative_rfh->GetSiteInstance());
Alex Moshchuk27caae82017-09-11 23:11:188582
8583 // Similarly, the subframe should also have a b.com proxy (unused in this
8584 // test), since it is also doing a cross-process navigation.
8585 RenderFrameProxyHost* child_proxy =
Harkiran Bolariad22a1dca2022-02-22 17:01:128586 child->current_frame_host()
8587 ->browsing_context_state()
Jiacheng Guo782c9ac2024-08-01 08:14:248588 ->GetRenderFrameProxyHost(b_subframe_site_instance->group());
Alex Moshchuk27caae82017-09-11 23:11:188589 EXPECT_TRUE(child_proxy);
8590 EXPECT_TRUE(child_proxy->is_render_frame_proxy_live());
8591
8592 // Now let the main frame commit.
Fergal Daly83bc3cd2023-01-18 00:22:548593 ASSERT_TRUE(manager1.WaitForNavigationFinished());
Alex Moshchuk27caae82017-09-11 23:11:188594
8595 // Make sure the process is live and at the new URL.
arthursonzognia2754522019-07-03 18:25:368596 EXPECT_TRUE(b_root_site_instance->GetProcess()->IsInitializedAndNotDead());
Alex Moshchuk27caae82017-09-11 23:11:188597 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
8598 EXPECT_EQ(root_speculative_rfh, root->current_frame_host());
8599 EXPECT_EQ(new_url_1, root->current_frame_host()->GetLastCommittedURL());
8600
8601 // The subframe should be gone, so the second navigation should have no
8602 // effect.
Fergal Daly83bc3cd2023-01-18 00:22:548603 ASSERT_TRUE(manager2.WaitForNavigationFinished());
Alex Moshchuk27caae82017-09-11 23:11:188604
8605 // The new commit should have detached the old child frame.
8606 EXPECT_EQ(0U, root->child_count());
Avi Drissmanc91bd8e2021-04-19 23:58:448607 EXPECT_EQ(0, EvalJs(web_contents(), "frames.length;"));
Alex Moshchuk27caae82017-09-11 23:11:188608
8609 // The root proxy should be gone.
Jiacheng Guo782c9ac2024-08-01 08:14:248610 if (b_subframe_site_instance->group()) {
8611 EXPECT_FALSE(
8612 root->current_frame_host()
8613 ->browsing_context_state()
8614 ->GetRenderFrameProxyHost(b_subframe_site_instance->group()));
8615 }
Alex Moshchuk27caae82017-09-11 23:11:188616}
8617
8618// Similar to TwoCrossSitePendingNavigationsAndMainFrameWins, but checks the
8619// case where the subframe navigation commits before the main frame. See
8620// https://crbug.com/756790.
Fergal Daly2e7e1e12020-06-24 09:18:288621IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Alex Moshchuk27caae82017-09-11 23:11:188622 TwoCrossSitePendingNavigationsAndSubframeWins) {
Alex Moshchuk27caae82017-09-11 23:11:188623 GURL main_url(embedded_test_server()->GetURL(
8624 "a.com", "/cross_site_iframe_factory.html?a(a,a)"));
8625 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:558626 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchuk27caae82017-09-11 23:11:188627 FrameTreeNode* child = root->child_at(0);
8628 FrameTreeNode* child2 = root->child_at(1);
8629
8630 // Install postMessage handlers in main frame and second subframe for later
8631 // use.
Avi Drissmanc91bd8e2021-04-19 23:58:448632 EXPECT_TRUE(ExecJs(root->current_frame_host(),
8633 "window.addEventListener('message', function(event) {\n"
8634 " event.source.postMessage(event.data + '-reply', '*');\n"
8635 "});"));
8636 EXPECT_TRUE(ExecJs(
Alex Moshchuk27caae82017-09-11 23:11:188637 child2->current_frame_host(),
8638 "window.addEventListener('message', function(event) {\n"
8639 " event.source.postMessage(event.data + '-subframe-reply', '*');\n"
8640 "});"));
8641
8642 // Start a main frame navigation to b.com.
8643 GURL new_url_1(embedded_test_server()->GetURL("b.com", "/title1.html"));
8644 TestNavigationManager manager1(web_contents(), new_url_1);
Avi Drissmanc91bd8e2021-04-19 23:58:448645 EXPECT_TRUE(ExecJs(web_contents(), JsReplace("location = $1", new_url_1)));
Alex Moshchuk27caae82017-09-11 23:11:188646
8647 // Wait for main frame request and check the frame tree. There should be a
8648 // proxy for b.com at the root, but nowhere else at this point.
Jiacheng Guo4bdd0be2024-06-11 23:35:218649 manager1.WaitForSpeculativeRenderFrameHostCreation();
Alex Moshchuk27caae82017-09-11 23:11:188650 EXPECT_EQ(
8651 " Site A (B speculative) -- proxies for B\n"
8652 " |--Site A\n"
8653 " +--Site A\n"
8654 "Where A = http://a.com/\n"
8655 " B = http://b.com/",
8656 DepictFrameTree(root));
8657
8658 // Now start navigating the first subframe to b.com.
8659 GURL new_url_2(embedded_test_server()->GetURL("b.com", "/title2.html"));
8660 TestNavigationManager manager2(web_contents(), new_url_2);
Avi Drissmanc91bd8e2021-04-19 23:58:448661 EXPECT_TRUE(
8662 ExecJs(web_contents(), JsReplace("frames[0].location = $1", new_url_2)));
Alex Moshchuk27caae82017-09-11 23:11:188663
8664 // Wait for subframe request.
Jiacheng Guo4bdd0be2024-06-11 23:35:218665 manager2.WaitForSpeculativeRenderFrameHostCreation();
Alex Moshchuk27caae82017-09-11 23:11:188666 RenderFrameHostImpl* child_speculative_rfh =
8667 child->render_manager()->speculative_frame_host();
8668 EXPECT_TRUE(child_speculative_rfh);
8669 scoped_refptr<SiteInstanceImpl> b_site_instance(
8670 child_speculative_rfh->GetSiteInstance());
8671
arthursonzognia2754522019-07-03 18:25:368672 // Check that all frames have proxies for b.com at this point. The proxy for
Alex Moshchuk27caae82017-09-11 23:11:188673 // |child2| is important to create since |child| has to use it to communicate
8674 // with |child2| if |child| commits first.
Rakina Zata Amni6d66b932020-07-21 05:44:178675 if (CanCrossSiteNavigationsProactivelySwapBrowsingInstances()) {
arthursonzognia2754522019-07-03 18:25:368676 // With ProactivelySwapBrowsingInstance, the new main document and the new
8677 // iframe don't have the same SiteInstance, because they belong to two
8678 // unrelated pages. The two page use different BrowsingInstances.
8679 EXPECT_EQ(
8680 " Site A (B speculative) -- proxies for B C\n"
8681 " |--Site A (C speculative) -- proxies for C\n"
8682 " +--Site A ------- proxies for C\n"
8683 "Where A = http://a.com/\n"
8684 " B = http://b.com/\n"
8685 " C = http://b.com/",
8686 DepictFrameTree(root));
8687 } else {
8688 EXPECT_EQ(
8689 " Site A (B speculative) -- proxies for B\n"
8690 " |--Site A (B speculative) -- proxies for B\n"
8691 " +--Site A ------- proxies for B\n"
8692 "Where A = http://a.com/\n"
8693 " B = http://b.com/",
8694 DepictFrameTree(root));
8695 }
Alex Moshchuk27caae82017-09-11 23:11:188696
8697 // Now let the subframe commit.
Fergal Daly83bc3cd2023-01-18 00:22:548698 ASSERT_TRUE(manager2.WaitForNavigationFinished());
Alex Moshchuk27caae82017-09-11 23:11:188699
8700 // Make sure the process is live and at the new URL.
Lukasz Anforowicz5510ed652018-06-06 16:16:198701 EXPECT_TRUE(b_site_instance->GetProcess()->IsInitializedAndNotDead());
Alex Moshchuk27caae82017-09-11 23:11:188702 ASSERT_EQ(2U, root->child_count());
8703 EXPECT_TRUE(child->current_frame_host()->IsRenderFrameLive());
8704 EXPECT_EQ(child_speculative_rfh, child->current_frame_host());
8705 EXPECT_EQ(new_url_2, child->current_frame_host()->GetLastCommittedURL());
8706
8707 // Recheck the proxies. Main frame should still be pending.
Rakina Zata Amni6d66b932020-07-21 05:44:178708 if (CanCrossSiteNavigationsProactivelySwapBrowsingInstances()) {
arthursonzognia2754522019-07-03 18:25:368709 EXPECT_EQ(
8710 " Site A (B speculative) -- proxies for B C\n"
8711 " |--Site C ------- proxies for A\n"
8712 " +--Site A ------- proxies for C\n"
8713 "Where A = http://a.com/\n"
8714 " B = http://b.com/\n"
8715 " C = http://b.com/",
8716 DepictFrameTree(root));
8717 } else {
8718 EXPECT_EQ(
8719 " Site A (B speculative) -- proxies for B\n"
8720 " |--Site B ------- proxies for A\n"
8721 " +--Site A ------- proxies for B\n"
8722 "Where A = http://a.com/\n"
8723 " B = http://b.com/",
8724 DepictFrameTree(root));
8725 }
Alex Moshchuk27caae82017-09-11 23:11:188726
8727 // Make sure the subframe can communicate to both the root remote frame
8728 // (where the postMessage should go to the current RenderFrameHost rather
8729 // than the pending one) and its sibling remote frame in the a.com process.
Chris Fredrickson5b9810c2023-03-27 21:08:308730 EXPECT_TRUE(
8731 ExecJs(child->current_frame_host(), WaitForMessageScript("event.data")));
8732 EXPECT_TRUE(ExecJs(child, "parent.postMessage('root-ping', '*')"));
Avi Drissmanc91bd8e2021-04-19 23:58:448733 EXPECT_EQ("root-ping-reply",
Chris Fredrickson5b9810c2023-03-27 21:08:308734 EvalJs(child->current_frame_host(), "onMessagePromise"));
Alex Moshchuk27caae82017-09-11 23:11:188735
Chris Fredrickson5b9810c2023-03-27 21:08:308736 EXPECT_TRUE(
8737 ExecJs(child->current_frame_host(), WaitForMessageScript("event.data")));
8738 EXPECT_TRUE(
8739 ExecJs(child, "parent.frames[1].postMessage('sibling-ping', '*')"));
Avi Drissmanc91bd8e2021-04-19 23:58:448740 EXPECT_EQ("sibling-ping-subframe-reply",
Chris Fredrickson5b9810c2023-03-27 21:08:308741 EvalJs(child->current_frame_host(), "onMessagePromise"));
Alex Moshchuk27caae82017-09-11 23:11:188742
8743 // Cancel the pending main frame navigation, and verify that the subframe can
8744 // still communicate with the (old) main frame.
Rakina Zata Amni58681c62024-06-25 06:32:138745 root->navigator().CancelNavigation(
8746 root, NavigationDiscardReason::kExplicitCancellation);
Alex Moshchuk27caae82017-09-11 23:11:188747 EXPECT_FALSE(root->render_manager()->speculative_frame_host());
Avi Drissmanc91bd8e2021-04-19 23:58:448748
Chris Fredrickson5b9810c2023-03-27 21:08:308749 EXPECT_TRUE(
8750 ExecJs(child->current_frame_host(), WaitForMessageScript("event.data")));
8751 EXPECT_TRUE(ExecJs(child, "parent.postMessage('root-ping', '*')"));
Avi Drissmanc91bd8e2021-04-19 23:58:448752 EXPECT_EQ("root-ping-reply",
Chris Fredrickson5b9810c2023-03-27 21:08:308753 EvalJs(child->current_frame_host(), "onMessagePromise"));
Alex Moshchuk27caae82017-09-11 23:11:188754}
8755
8756// Similar to TwoCrossSitePendingNavigations* tests above, but checks the case
8757// where the current window and its opener navigate simultaneously.
8758// See https://crbug.com/756790.
Fergal Daly2e7e1e12020-06-24 09:18:288759IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Alex Moshchuk27caae82017-09-11 23:11:188760 TwoCrossSitePendingNavigationsWithOpener) {
Alex Moshchuk27caae82017-09-11 23:11:188761 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
8762 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:558763 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchuk27caae82017-09-11 23:11:188764
8765 // Install a postMessage handler in main frame for later use.
Avi Drissmanc91bd8e2021-04-19 23:58:448766 EXPECT_TRUE(ExecJs(web_contents(),
8767 "window.addEventListener('message', function(event) {\n"
8768 " event.source.postMessage(event.data + '-reply', '*');\n"
8769 "});"));
Alex Moshchuk27caae82017-09-11 23:11:188770
8771 Shell* popup_shell =
8772 OpenPopup(shell()->web_contents(), GURL(url::kAboutBlankURL), "popup");
8773
8774 // Start a navigation to b.com in the first (opener) tab.
8775 GURL new_url_1(embedded_test_server()->GetURL("b.com", "/title1.html"));
8776 TestNavigationManager manager(web_contents(), new_url_1);
Avi Drissmanc91bd8e2021-04-19 23:58:448777 EXPECT_TRUE(ExecJs(web_contents(), JsReplace("location = $1", new_url_1)));
Jiacheng Guo4bdd0be2024-06-11 23:35:218778 manager.WaitForSpeculativeRenderFrameHostCreation();
Alex Moshchuk27caae82017-09-11 23:11:188779
8780 // Before it commits, start and commit a navigation to b.com in the second
8781 // tab.
8782 GURL new_url_2(embedded_test_server()->GetURL("b.com", "/title2.html"));
Alex Moshchuk7e26eca2018-03-03 01:34:298783 EXPECT_TRUE(NavigateToURLFromRenderer(popup_shell, new_url_2));
Alex Moshchuk27caae82017-09-11 23:11:188784
8785 // Check that the opener still has a speculative RenderFrameHost and a
8786 // corresponding proxy for b.com.
8787 RenderFrameHostImpl* speculative_rfh =
8788 root->render_manager()->speculative_frame_host();
8789 EXPECT_TRUE(speculative_rfh);
8790 scoped_refptr<SiteInstanceImpl> b_site_instance(
8791 speculative_rfh->GetSiteInstance());
8792 RenderFrameProxyHost* proxy =
Harkiran Bolariad22a1dca2022-02-22 17:01:128793 root->current_frame_host()
8794 ->browsing_context_state()
8795 ->GetRenderFrameProxyHost(b_site_instance->group());
Alex Moshchuk27caae82017-09-11 23:11:188796 EXPECT_TRUE(proxy);
8797 EXPECT_TRUE(proxy->is_render_frame_proxy_live());
8798
8799 // Make sure the second tab can communicate to its (old) opener remote frame.
8800 // The postMessage should go to the current RenderFrameHost rather than the
8801 // pending one in the first tab's main frame.
Chris Fredrickson5b9810c2023-03-27 21:08:308802 EXPECT_TRUE(
8803 ExecJs(popup_shell->web_contents(), WaitForMessageScript("event.data")));
Alex Moshchuk27caae82017-09-11 23:11:188804
Chris Fredrickson5b9810c2023-03-27 21:08:308805 EXPECT_TRUE(ExecJs(popup_shell->web_contents(),
8806 "opener.postMessage('opener-ping', '*');"));
Avi Drissmanc91bd8e2021-04-19 23:58:448807 EXPECT_EQ("opener-ping-reply",
Chris Fredrickson5b9810c2023-03-27 21:08:308808 EvalJs(popup_shell->web_contents(), "onMessagePromise"));
Alex Moshchuk27caae82017-09-11 23:11:188809
8810 // Cancel the pending main frame navigation, and verify that the subframe can
8811 // still communicate with the (old) main frame.
Rakina Zata Amni58681c62024-06-25 06:32:138812 root->navigator().CancelNavigation(
8813 root, NavigationDiscardReason::kExplicitCancellation);
Alex Moshchuk27caae82017-09-11 23:11:188814 EXPECT_FALSE(root->render_manager()->speculative_frame_host());
Avi Drissmanc91bd8e2021-04-19 23:58:448815
Chris Fredrickson5b9810c2023-03-27 21:08:308816 EXPECT_TRUE(
8817 ExecJs(popup_shell->web_contents(), WaitForMessageScript("event.data")));
8818 EXPECT_TRUE(ExecJs(popup_shell->web_contents(),
8819 "opener.postMessage('opener-ping', '*')"));
Avi Drissmanc91bd8e2021-04-19 23:58:448820 EXPECT_EQ("opener-ping-reply",
Chris Fredrickson5b9810c2023-03-27 21:08:308821 EvalJs(popup_shell->web_contents(), "onMessagePromise"));
Alex Moshchuk27caae82017-09-11 23:11:188822}
8823
Daniel Chengcf054222020-11-10 19:39:318824IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
8825 DetachSpeculativeRenderFrameHost) {
8826 // Commit a page with one iframe.
8827 GURL main_url(embedded_test_server()->GetURL(
8828 "a.com", "/cross_site_iframe_factory.html?a(a)"));
8829 EXPECT_TRUE(NavigateToURL(shell(), main_url));
8830
8831 // Start a cross-site navigation.
8832 GURL cross_site_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
8833 TestNavigationManager nav_manager(shell()->web_contents(), cross_site_url);
8834 BeginNavigateIframeToURL(web_contents(), "child-0", cross_site_url);
8835
8836 // Wait for the request, but don't commit it yet. This should create a
8837 // speculative RenderFrameHost.
Jiacheng Guo4bdd0be2024-06-11 23:35:218838 nav_manager.WaitForSpeculativeRenderFrameHostCreation();
Carlos Caballero15caeeb2021-10-27 09:57:558839 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Daniel Chengcf054222020-11-10 19:39:318840 RenderFrameHostImpl* speculative_rfh = root->current_frame_host()
8841 ->child_at(0)
8842 ->render_manager()
8843 ->speculative_frame_host();
8844 EXPECT_TRUE(speculative_rfh);
8845
8846 // Currently, the browser process never handles an explicit Detach() for a
8847 // speculative RFH, since the speculative RFH or the entire FTN is always
8848 // destroyed before the renderer sends this IPC.
8849 speculative_rfh->Detach();
8850
8851 // Passes if there is no crash.
8852}
8853
Daniel Cheng039670a42020-11-21 00:00:588854// Tests what happens if the renderer attempts to cancel a navigation after the
8855// NavigationRequest has already reached READY_TO_COMMIT.
8856IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
8857 CancelNavigationAfterReadyToCommit) {
8858 class NavigationCanceller : public WebContentsObserver {
8859 public:
8860 NavigationCanceller(WebContents* web_contents,
8861 RenderFrameHost& requesting_rfh)
8862 : WebContentsObserver(web_contents), requesting_rfh_(requesting_rfh) {}
8863
8864 // WebContentsObserver overrides:
8865 void ReadyToCommitNavigation(NavigationHandle* navigation_handle) override {
8866 // Cancel the navigation in the renderer, but don't wait for the
8867 // reply. This is to ensure the browser process does not process any
8868 // incoming messages and learn about the renderer's cancellation
8869 // before the browser process dispatches a CommitNavigation() to the
8870 // renderer.
Ali Hijazid87307d2022-11-07 20:15:038871 ExecuteScriptAsync(&*requesting_rfh_, "window.stop()");
Daniel Cheng039670a42020-11-21 00:00:588872 }
8873
8874 private:
Pârise6361d02023-07-19 09:00:438875 const raw_ref<RenderFrameHost, AcrossTasksDanglingUntriaged>
8876 requesting_rfh_;
Daniel Cheng039670a42020-11-21 00:00:588877 };
8878
8879 // Set up a test page with a same-site child frame.
8880 // TODO(dcheng): In the future, it might be useful to also have a test where
8881 // the child frame is same-site but cross-origin, and have the parent
8882 // initiate the navigation in the child frame.
8883 GURL url1(embedded_test_server()->GetURL(
8884 "a.com", "/cross_site_iframe_factory.html?a(a)"));
8885 EXPECT_TRUE(NavigateToURL(web_contents(), url1));
8886
8887 // Now navigate the first child to another same-site page. Note that with
8888 // subframe RenderDocument, this will create a speculative RFH.
Carlos Caballero15caeeb2021-10-27 09:57:558889 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Daniel Cheng039670a42020-11-21 00:00:588890 GURL url2(embedded_test_server()->GetURL("a.com", "/title1.html"));
8891 TestNavigationManager nav_manager(web_contents(), url2);
8892 FrameTreeNode* first_child = root->child_at(0);
8893 EXPECT_TRUE(BeginNavigateToURLFromRenderer(
8894 first_child->render_manager()->current_frame_host(), url2));
8895
8896 EXPECT_TRUE(nav_manager.WaitForResponse());
8897
8898 bool using_speculative_rfh =
8899 !!first_child->render_manager()->speculative_frame_host();
8900
8901 NavigationCanceller canceller(
8902 web_contents(), *first_child->render_manager()->current_frame_host());
8903
Fergal Daly83bc3cd2023-01-18 00:22:548904 ASSERT_TRUE(nav_manager.WaitForNavigationFinished());
Daniel Cheng039670a42020-11-21 00:00:588905 // The navigation should be committed if and only if it committed in a new
8906 // RFH (i.e. if the navigation used a speculative RFH).
8907 EXPECT_EQ(using_speculative_rfh, nav_manager.was_committed());
8908}
8909
Daniel Chenge8b69402021-05-25 20:44:448910namespace {
8911
8912// Helper for various <object> navigation test cases that trigger fallback
8913// handling. Fallback handling should never reach ready-to-commit navigation, so
8914// this helper forces test failure if a ReadyToCommitNavigation() is received.
8915class AssertNoReadyToCommitNavigationCalls : public WebContentsObserver {
8916 public:
8917 explicit AssertNoReadyToCommitNavigationCalls(WebContents* contents)
8918 : WebContentsObserver(contents) {}
8919
8920 private:
8921 // WebContentsObserver overrides:
8922 void ReadyToCommitNavigation(NavigationHandle* handle) override {
8923 ASSERT_TRUE(false);
8924 }
8925};
8926
8927} // namespace
8928
8929// Test that a same-site navigation in <object> that fails with an HTTP error
8930// directly triggers fallback handling, rather than triggering fallback handling
8931// in the renderer after it receives a `CommitNavigation()` IPC.
Daniel Cheng039670a42020-11-21 00:00:588932IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Daniel Chenge8b69402021-05-25 20:44:448933 ObjectTagSameSiteNavigationWithHTTPError) {
Daniel Cheng039670a42020-11-21 00:00:588934 // Set up a test page with a same-site child frame hosted in an <object> tag.
8935 // TODO(dcheng): In the future, it might be useful to also have a test where
8936 // the child frame is same-site but cross-origin, and have the parent
8937 // initiate the navigation in the child frame.
8938 GURL url1(embedded_test_server()->GetURL("a.com", "/object-frame.html"));
8939 EXPECT_TRUE(NavigateToURL(web_contents(), url1));
8940
8941 // There should be one nested browsing context.
8942 EXPECT_EQ(1, EvalJs(web_contents(), "window.length"));
8943 // And there should be no fallback content displayed.
8944 EXPECT_EQ("", EvalJs(web_contents(), "document.body.innerText"));
8945
Daniel Chenge8b69402021-05-25 20:44:448946 // <object> fallback handling should never reach ReadyToCommitNavigation.
8947 AssertNoReadyToCommitNavigationCalls asserter(web_contents());
8948
8949 // Now navigate the first child to a same-site page that will result in a 404.
8950 // Note that with subframe RenderDocument, this will create a speculative RFH.
Carlos Caballero15caeeb2021-10-27 09:57:558951 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Daniel Cheng039670a42020-11-21 00:00:588952 GURL url2(embedded_test_server()->GetURL("a.com", "/page404.html"));
8953 TestNavigationManager nav_manager(web_contents(), url2);
8954 FrameTreeNode* first_child = root->child_at(0);
8955 EXPECT_TRUE(BeginNavigateToURLFromRenderer(
8956 first_child->render_manager()->current_frame_host(), url2));
8957
Daniel Chenge8b69402021-05-25 20:44:448958 const bool using_speculative_rfh =
8959 !!first_child->render_manager()->speculative_frame_host();
Jiacheng Guo4bdd0be2024-06-11 23:35:218960 // Speculative RFH will not be created at this point if we enable deferring.
Daniel Chenge8b69402021-05-25 20:44:448961 EXPECT_EQ(using_speculative_rfh,
Jiacheng Guo4bdd0be2024-06-11 23:35:218962 GetRenderDocumentLevel() >= RenderDocumentLevel::kSubframe &&
8963 !base::FeatureList::IsEnabled(
8964 features::kDeferSpeculativeRFHCreation));
Daniel Chenge8b69402021-05-25 20:44:448965
Fergal Daly83bc3cd2023-01-18 00:22:548966 ASSERT_TRUE(nav_manager.WaitForNavigationFinished());
Daniel Cheng9bd90f92021-04-23 20:49:458967 // There should be no commit...
8968 EXPECT_FALSE(nav_manager.was_committed());
8969 // .. and the navigation should have been aborted.
8970 EXPECT_FALSE(nav_manager.was_successful());
8971 // Fallback handling should discard the child browsing context and render the
8972 // fallback contents.
zhoupengd04e13c682024-10-11 02:54:178973 EXPECT_EQ(0, EvalJs(web_contents(), "window.length"));
Daniel Cheng039670a42020-11-21 00:00:588974 EXPECT_EQ("fallback", EvalJs(web_contents(), "document.body.innerText"));
8975}
8976
Daniel Chenge8b69402021-05-25 20:44:448977// Test that a cross-site navigation in <object> that fails with an HTTP error
8978// directly triggers fallback handling, rather than triggering fallback handling
8979// in the renderer after it receives a `CommitNavigation()` IPC.
Jiacheng Guo782c9ac2024-08-01 08:14:248980// The test disables the delay of creating the speculative RFH since it
8981// will check the created speculative RFH for a failing request. The speculative
8982// RFH will not be created after receiving the 404 response.
8983IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTestWithoutSpeculativeRFHDelay,
Daniel Chenge8b69402021-05-25 20:44:448984 ObjectTagCrossSiteNavigationWithHTTPError) {
8985 // Set up a test page with a same-site child frame hosted in an <object> tag.
8986 // TODO(dcheng): In the future, it might be useful to also have a test where
8987 // the child frame is same-site but cross-origin, and have the parent
8988 // initiate the navigation in the child frame.
8989 GURL url1(embedded_test_server()->GetURL("a.com", "/object-frame.html"));
8990 EXPECT_TRUE(NavigateToURL(web_contents(), url1));
8991
8992 // There should be one nested browsing context.
8993 EXPECT_EQ(1, EvalJs(web_contents(), "window.length"));
8994 // And there should be no fallback content displayed.
8995 EXPECT_EQ("", EvalJs(web_contents(), "document.body.innerText"));
8996
8997 // <object> fallback handling should never reach ReadyToCommitNavigation.
8998 AssertNoReadyToCommitNavigationCalls asserter(web_contents());
8999
9000 // Now navigate the first child to a cross-site page that will result in a
9001 // 404.
Carlos Caballero15caeeb2021-10-27 09:57:559002 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Daniel Chenge8b69402021-05-25 20:44:449003 GURL url2(embedded_test_server()->GetURL("b.com", "/page404.html"));
9004 TestNavigationManager nav_manager(web_contents(), url2);
9005 FrameTreeNode* first_child = root->child_at(0);
9006 EXPECT_TRUE(BeginNavigateToURLFromRenderer(
9007 first_child->render_manager()->current_frame_host(), url2));
Jiacheng Guo4bdd0be2024-06-11 23:35:219008 nav_manager.WaitForSpeculativeRenderFrameHostCreation();
Daniel Chenge8b69402021-05-25 20:44:449009 // Cross-site navigations always force a speculative RFH to be created.
9010 EXPECT_TRUE(first_child->render_manager()->speculative_frame_host());
9011
Fergal Daly83bc3cd2023-01-18 00:22:549012 ASSERT_TRUE(nav_manager.WaitForNavigationFinished());
Daniel Chenge8b69402021-05-25 20:44:449013 // There should be no commit...
9014 EXPECT_FALSE(nav_manager.was_committed());
9015 // .. and the navigation should have been aborted.
9016 EXPECT_FALSE(nav_manager.was_successful());
9017 // Fallback handling should discard the child browsing context and render the
9018 // fallback contents.
zhoupengd04e13c682024-10-11 02:54:179019 EXPECT_EQ(0, EvalJs(web_contents(), "window.length"));
Daniel Chenge8b69402021-05-25 20:44:449020 EXPECT_EQ("fallback", EvalJs(web_contents(), "document.body.innerText"));
9021}
9022
9023// Test that a same-site navigation in <object> that fails with an HTTP error
9024// and also subsequently fails to load the body still directly triggers fallback
9025// handling, rather than triggering fallback handling in the renderer after it
9026// receives a `CommitNavigation()` IPC.
9027IN_PROC_BROWSER_TEST_P(
9028 SitePerProcessBrowserTest,
9029 ObjectTagSameSiteNavigationWithHTTPErrorAndFailedBodyLoad) {
9030 // Set up a test page with a same-site child frame hosted in an <object> tag.
9031 // TODO(dcheng): In the future, it might be useful to also have a test where
9032 // the child frame is same-site but cross-origin, and have the parent
9033 // initiate the navigation in the child frame.
9034 GURL url1(embedded_test_server()->GetURL("a.com", "/object-frame.html"));
9035 EXPECT_TRUE(NavigateToURL(web_contents(), url1));
9036
9037 // There should be one nested browsing context.
9038 EXPECT_EQ(1, EvalJs(web_contents(), "window.length"));
9039 // And there should be no fallback content displayed.
9040 EXPECT_EQ("", EvalJs(web_contents(), "document.body.innerText"));
9041
9042 // This test differs from CommitNavigationWithHTTPErrorInObjectTag by
9043 // triggering a body load failure. `ObjectNavigationFallbackBodyLoader`
9044 // detects this by setting a disconnect handler on the `mojo::Receiver` for
9045 // `network:;mojom::URLLoaderClient`. Exercise this code path by:
9046 // 1. inserting a test `NavigationThrottle`
9047 // 2. replacing the `network::mojom::URLLoaderClient` endpoint with one where
9048 // the corresponding `mojo::Remote` is simply closed at
9049 // `WILL_PROCESS_RESPONSE` time.
9050 TestNavigationThrottleInserter navigation_throttle_inserter(
9051 web_contents(),
9052 base::BindRepeating(
Takashi Toyoshimae9c959ce92025-05-20 19:18:389053 [](NavigationThrottleRegistry& registry) -> void {
9054 auto throttle = std::make_unique<TestNavigationThrottle>(registry);
9055 auto* handle = &registry.GetNavigationHandle();
Daniel Chenge8b69402021-05-25 20:44:449056 throttle->SetCallback(
9057 TestNavigationThrottle::WILL_PROCESS_RESPONSE,
9058 base::BindLambdaForTesting([handle]() {
9059 // Swap out the URL loader client endpoint and just drop the
9060 // mojo::Remote. This will trigger the mojo::Receiver to be
9061 // disconnected, which should still trigger fallback handling
9062 // despite body loading failing.
9063 mojo::Remote<network::mojom::URLLoaderClient>
9064 remote_to_be_dropped;
9065 auto* request = static_cast<NavigationRequest*>(handle);
9066 request->mutable_url_loader_client_endpoints_for_testing()
9067 ->url_loader_client =
9068 remote_to_be_dropped.BindNewPipeAndPassReceiver();
9069 }));
Takashi Toyoshimae9c959ce92025-05-20 19:18:389070 registry.AddThrottle(std::move(throttle));
Daniel Chenge8b69402021-05-25 20:44:449071 }));
9072
9073 // <object> fallback handling should never reach ReadyToCommitNavigation.
9074 AssertNoReadyToCommitNavigationCalls asserter(web_contents());
9075
9076 // Now navigate the first child to a same-site page that will result in a 404,
9077 // though the body loading will fail. Note that with subframe RenderDocument,
9078 // this will create a speculative RFH.
Carlos Caballero15caeeb2021-10-27 09:57:559079 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Daniel Chenge8b69402021-05-25 20:44:449080 GURL url2(embedded_test_server()->GetURL("a.com", "/page404.html"));
9081 TestNavigationManager nav_manager(web_contents(), url2);
9082 FrameTreeNode* first_child = root->child_at(0);
9083 EXPECT_TRUE(BeginNavigateToURLFromRenderer(
9084 first_child->render_manager()->current_frame_host(), url2));
9085
9086 const bool using_speculative_rfh =
9087 !!first_child->render_manager()->speculative_frame_host();
Jiacheng Guo4bdd0be2024-06-11 23:35:219088 // Speculative RFH will not be created at this point if we enable deferring.
Daniel Chenge8b69402021-05-25 20:44:449089 EXPECT_EQ(using_speculative_rfh,
Jiacheng Guo4bdd0be2024-06-11 23:35:219090 GetRenderDocumentLevel() >= RenderDocumentLevel::kSubframe &&
9091 !base::FeatureList::IsEnabled(
9092 features::kDeferSpeculativeRFHCreation));
Daniel Chenge8b69402021-05-25 20:44:449093
Fergal Daly83bc3cd2023-01-18 00:22:549094 ASSERT_TRUE(nav_manager.WaitForNavigationFinished());
Daniel Chenge8b69402021-05-25 20:44:449095 // There should be no commit...
9096 EXPECT_FALSE(nav_manager.was_committed());
9097 // .. and the navigation should have been aborted.
9098 EXPECT_FALSE(nav_manager.was_successful());
9099 // Fallback handling should discard the child browsing context and render the
9100 // fallback contents.
zhoupengd04e13c682024-10-11 02:54:179101 EXPECT_EQ(0, EvalJs(web_contents(), "window.length"));
Daniel Chenge8b69402021-05-25 20:44:449102 EXPECT_EQ("fallback", EvalJs(web_contents(), "document.body.innerText"));
Daniel Chenge8b69402021-05-25 20:44:449103}
9104
9105// Test that a cross-site navigation in <object> that fails with an HTTP error
9106// and also subsequently fails to load the body still directly triggers fallback
9107// handling, rather than triggering fallback handling in the renderer after it
9108// receives a `CommitNavigation()` IPC.
Jiacheng Guo782c9ac2024-08-01 08:14:249109// The test disables the delay of creating the speculative RFH since it
9110// will check the created speculative RFH for a failing request. The speculative
9111// RFH will not be created after receiving the 404 response.
Daniel Chenge8b69402021-05-25 20:44:449112IN_PROC_BROWSER_TEST_P(
Jiacheng Guo782c9ac2024-08-01 08:14:249113 SitePerProcessBrowserTestWithoutSpeculativeRFHDelay,
Daniel Chenge8b69402021-05-25 20:44:449114 ObjectTagCrossSiteNavigationWithHTTPErrorAndFailedBodyLoad) {
9115 // Set up a test page with a same-site child frame hosted in an <object> tag.
9116 // TODO(dcheng): In the future, it might be useful to also have a test where
9117 // the child frame is same-site but cross-origin, and have the parent
9118 // initiate the navigation in the child frame.
9119 GURL url1(embedded_test_server()->GetURL("a.com", "/object-frame.html"));
9120 EXPECT_TRUE(NavigateToURL(web_contents(), url1));
9121
9122 // There should be one nested browsing context.
9123 EXPECT_EQ(1, EvalJs(web_contents(), "window.length"));
9124 // And there should be no fallback content displayed.
9125 EXPECT_EQ("", EvalJs(web_contents(), "document.body.innerText"));
9126
9127 // This test differs from CommitNavigationWithHTTPErrorInObjectTag by
9128 // triggering a body load failure. `ObjectNavigationFallbackBodyLoader`
9129 // detects this by setting a disconnect handler on the `mojo::Receiver` for
9130 // `network:;mojom::URLLoaderClient`. Exercise this code path by:
9131 // 1. inserting a test `NavigationThrottle`
9132 // 2. replacing the `network::mojom::URLLoaderClient` endpoint with one where
9133 // the corresponding `mojo::Remote` is simply closed at
9134 // `WILL_PROCESS_RESPONSE` time.
9135 TestNavigationThrottleInserter navigation_throttle_inserter(
9136 web_contents(),
9137 base::BindRepeating(
Takashi Toyoshimae9c959ce92025-05-20 19:18:389138 [](NavigationThrottleRegistry& registry) -> void {
9139 auto throttle = std::make_unique<TestNavigationThrottle>(registry);
9140 auto* handle = &registry.GetNavigationHandle();
Daniel Chenge8b69402021-05-25 20:44:449141 throttle->SetCallback(
9142 TestNavigationThrottle::WILL_PROCESS_RESPONSE,
9143 base::BindLambdaForTesting([handle]() {
9144 // Swap out the URL loader client endpoint and just drop the
9145 // mojo::Remote. This will trigger the mojo::Receiver to be
9146 // disconnected, which should still trigger fallback handling
9147 // despite body loading failing.
9148 mojo::Remote<network::mojom::URLLoaderClient>
9149 remote_to_be_dropped;
9150 auto* request = static_cast<NavigationRequest*>(handle);
9151 request->mutable_url_loader_client_endpoints_for_testing()
9152 ->url_loader_client =
9153 remote_to_be_dropped.BindNewPipeAndPassReceiver();
9154 }));
Takashi Toyoshimae9c959ce92025-05-20 19:18:389155 registry.AddThrottle(std::move(throttle));
Daniel Chenge8b69402021-05-25 20:44:449156 }));
9157
9158 // <object> fallback handling should never reach ReadyToCommitNavigation.
9159 AssertNoReadyToCommitNavigationCalls asserter(web_contents());
9160
9161 // Now navigate the first child to a cross-site page that will result in a
9162 // 404, though the body loading will fail.
Carlos Caballero15caeeb2021-10-27 09:57:559163 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Daniel Chenge8b69402021-05-25 20:44:449164 GURL url2(embedded_test_server()->GetURL("b.com", "/page404.html"));
9165 TestNavigationManager nav_manager(web_contents(), url2);
9166 FrameTreeNode* first_child = root->child_at(0);
9167 EXPECT_TRUE(BeginNavigateToURLFromRenderer(
9168 first_child->render_manager()->current_frame_host(), url2));
Jiacheng Guo4bdd0be2024-06-11 23:35:219169 nav_manager.WaitForSpeculativeRenderFrameHostCreation();
Daniel Chenge8b69402021-05-25 20:44:449170 // Cross-site navigations always force a speculative RFH to be created.
9171 EXPECT_TRUE(first_child->render_manager()->speculative_frame_host());
9172
Fergal Daly83bc3cd2023-01-18 00:22:549173 ASSERT_TRUE(nav_manager.WaitForNavigationFinished());
Daniel Chenge8b69402021-05-25 20:44:449174 // There should be no commit...
9175 EXPECT_FALSE(nav_manager.was_committed());
9176 // .. and the navigation should have been aborted.
9177 EXPECT_FALSE(nav_manager.was_successful());
9178 // Fallback handling should discard the child browsing context and render the
9179 // fallback contents.
zhoupengd04e13c682024-10-11 02:54:179180 EXPECT_EQ(0, EvalJs(web_contents(), "window.length"));
Daniel Chenge8b69402021-05-25 20:44:449181 EXPECT_EQ("fallback", EvalJs(web_contents(), "document.body.innerText"));
Daniel Chenge8b69402021-05-25 20:44:449182}
9183
9184// Test that a same-site navigation in <object> that fails with a network error
9185// directly triggers fallback handling, rather than triggering fallback handling
9186// in the renderer after it receives a `CommitFailedNavigation()` IPC.
Jiacheng Guo782c9ac2024-08-01 08:14:249187// The test disables the delay of creating the speculative RFH since it
9188// will check the created speculative RFH for a failing request. The speculative
9189// RFH will not be created after the network error.
9190IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTestWithoutSpeculativeRFHDelay,
Daniel Chenge8b69402021-05-25 20:44:449191 ObjectTagSameSiteNavigationWithNetworkError) {
Daniel Cheng039670a42020-11-21 00:00:589192 // Set up a test page with a same-site child frame hosted in an <object> tag.
9193 GURL url1(embedded_test_server()->GetURL("a.com", "/object-frame.html"));
9194 EXPECT_TRUE(NavigateToURL(web_contents(), url1));
9195
Daniel Chenge8b69402021-05-25 20:44:449196 // <object> fallback handling should never reach ReadyToCommitNavigation.
9197 AssertNoReadyToCommitNavigationCalls asserter(web_contents());
9198
9199 // Now navigate the first child to a same-site page that will result in a
9200 // network error. Note that with subframe RenderDocument, this will create a
Daniel Cheng039670a42020-11-21 00:00:589201 // speculative RFH.
Carlos Caballero15caeeb2021-10-27 09:57:559202 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Daniel Cheng039670a42020-11-21 00:00:589203 GURL error_url(embedded_test_server()->GetURL("a.com", "/empty.html"));
9204 std::unique_ptr<URLLoaderInterceptor> interceptor =
9205 URLLoaderInterceptor::SetupRequestFailForURL(error_url,
9206 net::ERR_CONNECTION_REFUSED);
9207 TestNavigationManager nav_manager(web_contents(), error_url);
9208 FrameTreeNode* first_child = root->child_at(0);
9209 EXPECT_TRUE(BeginNavigateToURLFromRenderer(
9210 first_child->render_manager()->current_frame_host(), error_url));
Jiacheng Guo4bdd0be2024-06-11 23:35:219211 if (GetRenderDocumentLevel() >= RenderDocumentLevel::kSubframe) {
9212 nav_manager.WaitForSpeculativeRenderFrameHostCreation();
9213 }
Daniel Cheng039670a42020-11-21 00:00:589214
Daniel Chenge8b69402021-05-25 20:44:449215 const bool using_speculative_rfh =
Daniel Cheng039670a42020-11-21 00:00:589216 !!first_child->render_manager()->speculative_frame_host();
9217 EXPECT_EQ(using_speculative_rfh,
Rakina Zata Amni639ac16a2023-03-22 11:48:399218 GetRenderDocumentLevel() >= RenderDocumentLevel::kSubframe);
Daniel Cheng039670a42020-11-21 00:00:589219
Daniel Chenge8b69402021-05-25 20:44:449220 // `WaitForResponse()` should signal failure by returning `false` false since
9221 // the URLLoaderInterceptor forces a network error.
9222 EXPECT_FALSE(nav_manager.WaitForResponse());
9223
Fergal Daly83bc3cd2023-01-18 00:22:549224 ASSERT_TRUE(nav_manager.WaitForNavigationFinished());
Daniel Chenge8b69402021-05-25 20:44:449225 EXPECT_FALSE(nav_manager.was_committed());
9226
9227 // Make sure that the speculative RFH has been cleaned up, if needed.
9228 EXPECT_EQ(nullptr, first_child->render_manager()->speculative_frame_host());
9229}
9230
9231// Test that a cross-site navigation in <object> that fails with a network error
9232// directly triggers fallback handling, rather than triggering fallback handling
9233// in the renderer after it receives a `CommitFailedNavigation()` IPC.
Jiacheng Guo782c9ac2024-08-01 08:14:249234// The test disables the delay of creating the speculative RFH since it
9235// will check the created speculative RFH for a failing request. The speculative
9236// RFH will not be created after the network error.
9237IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTestWithoutSpeculativeRFHDelay,
Daniel Chenge8b69402021-05-25 20:44:449238 ObjectTagCrossSiteNavigationWithNetworkError) {
9239 // Set up a test page with a same-site child frame hosted in an <object> tag.
9240 GURL url1(embedded_test_server()->GetURL("a.com", "/object-frame.html"));
9241 EXPECT_TRUE(NavigateToURL(web_contents(), url1));
9242
9243 // <object> fallback handling should never reach ReadyToCommitNavigation.
9244 AssertNoReadyToCommitNavigationCalls asserter(web_contents());
9245
9246 // Now navigate the first child to a cross-site page that will result in a
9247 // network error.
Carlos Caballero15caeeb2021-10-27 09:57:559248 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Daniel Chenge8b69402021-05-25 20:44:449249 GURL error_url(embedded_test_server()->GetURL("b.com", "/empty.html"));
9250 std::unique_ptr<URLLoaderInterceptor> interceptor =
9251 URLLoaderInterceptor::SetupRequestFailForURL(error_url,
9252 net::ERR_CONNECTION_REFUSED);
9253 TestNavigationManager nav_manager(web_contents(), error_url);
9254 FrameTreeNode* first_child = root->child_at(0);
9255 EXPECT_TRUE(BeginNavigateToURLFromRenderer(
9256 first_child->render_manager()->current_frame_host(), error_url));
Jiacheng Guo4bdd0be2024-06-11 23:35:219257 nav_manager.WaitForSpeculativeRenderFrameHostCreation();
Daniel Chenge8b69402021-05-25 20:44:449258 // Cross-site navigations always force a speculative RFH to be created.
9259 EXPECT_TRUE(first_child->render_manager()->speculative_frame_host());
9260
9261 // `WaitForResponse()` should signal failure by returning `false` false since
9262 // the URLLoaderInterceptor forces a network error.
Daniel Cheng039670a42020-11-21 00:00:589263 EXPECT_FALSE(nav_manager.WaitForResponse());
9264
Fergal Daly83bc3cd2023-01-18 00:22:549265 ASSERT_TRUE(nav_manager.WaitForNavigationFinished());
Daniel Cheng039670a42020-11-21 00:00:589266 EXPECT_FALSE(nav_manager.was_committed());
9267
9268 // Make sure that the speculative RFH has been cleaned up, if needed.
9269 EXPECT_EQ(nullptr, first_child->render_manager()->speculative_frame_host());
9270}
9271
Daniel Cheng463b5fd2020-12-12 02:59:559272class SitePerProcessBrowserTestWithLeakDetector
9273 : public SitePerProcessBrowserTest {
9274 public:
9275 void SetUpCommandLine(base::CommandLine* command_line) override {
9276 SitePerProcessBrowserTest::SetUpCommandLine(command_line);
9277 // Using the LeakDetector requires exposing GC.
Camillo Brunidcd255e32021-10-14 15:50:059278 command_line->AppendSwitchASCII(blink::switches::kJavaScriptFlags,
9279 "--expose-gc");
Daniel Cheng463b5fd2020-12-12 02:59:559280 }
9281};
9282
9283IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTestWithLeakDetector,
9284 CloseWebContentsWithSpeculativeRenderFrameHost) {
9285 const GURL url1(embedded_test_server()->GetURL("a.com", "/title1.html"));
9286 ASSERT_TRUE(NavigateToURL(web_contents(), url1));
9287
9288 // Open a popup in B. This is to prevent any fast shutdown shenanigans that
9289 // might otherwise happen when the speculative RFH is discarded later.
9290 Shell* new_shell =
9291 OpenPopup(web_contents(),
9292 embedded_test_server()->GetURL("b.com", "/title1.html"), "");
9293 ASSERT_TRUE(new_shell);
9294
9295 mojo::Remote<blink::mojom::LeakDetector> leak_detector_remote;
Dave Tapuska327c06c92022-06-13 20:31:519296 new_shell->web_contents()->GetPrimaryMainFrame()->GetProcess()->BindReceiver(
Daniel Cheng463b5fd2020-12-12 02:59:559297 leak_detector_remote.BindNewPipeAndPassReceiver());
Daniel Cheng463b5fd2020-12-12 02:59:559298
9299 // One live document is expected from the newly opened popup.
9300 {
Andrew Rayskiy2646d4142023-08-07 10:32:039301 base::test::TestFuture<blink::mojom::LeakDetectionResultPtr> result_future;
9302 leak_detector_remote->PerformLeakDetection(result_future.GetCallback());
9303 auto result = result_future.Take();
Daniel Cheng463b5fd2020-12-12 02:59:559304 EXPECT_EQ(1u, result->number_of_live_documents);
9305 // Note: the number of live frames includes remote frames.
9306 EXPECT_EQ(2u, result->number_of_live_frames);
9307 }
9308
9309 // Start a navigation to B, but don't let it commit. This should associate a
9310 // speculative RFH with the main frame.
9311 const GURL url2(embedded_test_server()->GetURL("b.com", "/title1.html"));
9312 TestNavigationManager nav_manager(web_contents(), url2);
9313 ASSERT_TRUE(BeginNavigateToURLFromRenderer(web_contents(), url2));
9314 ASSERT_TRUE(nav_manager.WaitForResponse());
9315
9316 // Speculative RFH should be created in B, increasing the number of live
9317 // documents and frames.
9318 {
Andrew Rayskiy2646d4142023-08-07 10:32:039319 base::test::TestFuture<blink::mojom::LeakDetectionResultPtr> result_future;
9320 leak_detector_remote->PerformLeakDetection(result_future.GetCallback());
9321 auto result = result_future.Take();
Daniel Cheng463b5fd2020-12-12 02:59:559322 EXPECT_EQ(2u, result->number_of_live_documents);
9323 // Note: the number of live frames includes remote frames.
9324 EXPECT_EQ(3u, result->number_of_live_frames);
9325 }
9326
9327 // Close the WebContents associated with the speculative RFH.
9328 shell()->Close();
9329 // Synchronize with the renderer.
9330 EXPECT_TRUE(ExecJs(new_shell, ""));
9331
Seth Brenitha2614742022-02-08 18:26:069332 // The resources associated with the speculative RFH should be freed now, as
9333 // well as the original frame from the now closed shell.
Daniel Cheng463b5fd2020-12-12 02:59:559334 {
Andrew Rayskiy2646d4142023-08-07 10:32:039335 base::test::TestFuture<blink::mojom::LeakDetectionResultPtr> result_future;
9336 leak_detector_remote->PerformLeakDetection(result_future.GetCallback());
9337 auto result = result_future.Take();
Daniel Cheng463b5fd2020-12-12 02:59:559338 EXPECT_EQ(1u, result->number_of_live_documents);
9339 // Note: the number of live frames includes remote frames.
Seth Brenitha2614742022-02-08 18:26:069340 EXPECT_EQ(1u, result->number_of_live_frames);
Daniel Cheng463b5fd2020-12-12 02:59:559341 }
9342}
9343
9344IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTestWithLeakDetector,
9345 DetachFrameWithSpeculativeRenderFrameHost) {
9346 const GURL url1(embedded_test_server()->GetURL(
9347 "a.com", "/cross_site_iframe_factory.html?a(a)"));
9348 ASSERT_TRUE(NavigateToURL(web_contents(), url1));
9349
9350 // Open a popup in B. This is to prevent any fast shutdown shenanigans that
9351 // might otherwise happen when the speculative RFH is discarded later.
9352 Shell* new_shell =
9353 OpenPopup(web_contents(),
9354 embedded_test_server()->GetURL("b.com", "/title1.html"), "");
9355 ASSERT_TRUE(new_shell);
9356
9357 mojo::Remote<blink::mojom::LeakDetector> leak_detector_remote;
Dave Tapuska327c06c92022-06-13 20:31:519358 new_shell->web_contents()->GetPrimaryMainFrame()->GetProcess()->BindReceiver(
Daniel Cheng463b5fd2020-12-12 02:59:559359 leak_detector_remote.BindNewPipeAndPassReceiver());
Daniel Cheng463b5fd2020-12-12 02:59:559360
9361 // One live document is expected from the newly opened popup.
9362 {
Andrew Rayskiy2646d4142023-08-07 10:32:039363 base::test::TestFuture<blink::mojom::LeakDetectionResultPtr> result_future;
9364 leak_detector_remote->PerformLeakDetection(result_future.GetCallback());
9365 auto result = result_future.Take();
Daniel Cheng463b5fd2020-12-12 02:59:559366 EXPECT_EQ(1u, result->number_of_live_documents);
9367 // Note: the number of live frames includes remote frames.
9368 EXPECT_EQ(3u, result->number_of_live_frames);
9369 }
9370
9371 // Start a navigation to B in the iframe, but don't let it commit. This should
9372 // associate a speculative RFH with the child frame.
9373 const GURL url2(embedded_test_server()->GetURL("b.com", "/title1.html"));
9374 TestNavigationManager nav_manager(web_contents(), url2);
Carlos Caballero15caeeb2021-10-27 09:57:559375 ASSERT_TRUE(BeginNavigateToURLFromRenderer(web_contents()
9376 ->GetPrimaryFrameTree()
9377 .root()
9378 ->current_frame_host()
9379 ->child_at(0),
9380 url2));
Daniel Cheng463b5fd2020-12-12 02:59:559381 ASSERT_TRUE(nav_manager.WaitForResponse());
9382
9383 // Speculative RFH should be created in B, increasing the number of live
9384 // documents and frames.
9385 {
Andrew Rayskiy2646d4142023-08-07 10:32:039386 base::test::TestFuture<blink::mojom::LeakDetectionResultPtr> result_future;
9387 leak_detector_remote->PerformLeakDetection(result_future.GetCallback());
9388 auto result = result_future.Take();
Daniel Cheng463b5fd2020-12-12 02:59:559389 EXPECT_EQ(2u, result->number_of_live_documents);
9390 // Note: the number of live frames includes remote frames.
9391 EXPECT_EQ(4u, result->number_of_live_frames);
9392 }
9393
9394 // Detach the <iframe> associated with the speculative RFH.
9395 EXPECT_TRUE(
9396 ExecJs(web_contents(), "document.querySelector('iframe').remove()"));
9397 // Synchronize with the renderer.
9398 EXPECT_TRUE(ExecJs(new_shell, ""));
9399
9400 // The resources associated with the speculative RFH should be freed now.
9401 {
Andrew Rayskiy2646d4142023-08-07 10:32:039402 base::test::TestFuture<blink::mojom::LeakDetectionResultPtr> result_future;
9403 leak_detector_remote->PerformLeakDetection(result_future.GetCallback());
9404 auto result = result_future.Take();
Daniel Cheng463b5fd2020-12-12 02:59:559405 EXPECT_EQ(1u, result->number_of_live_documents);
9406 // Note: the number of live frames includes remote frames.
9407 EXPECT_EQ(2u, result->number_of_live_frames);
9408 }
9409}
9410
Xiaohan Wang1ecfd002022-01-19 22:33:109411#if BUILDFLAG(IS_ANDROID)
Kevin McNee18430052018-08-10 16:50:569412
9413namespace {
9414
9415class MockEventHandlerAndroid : public ui::EventHandlerAndroid {
9416 public:
9417 bool OnTouchEvent(const ui::MotionEventAndroid& event) override {
9418 did_receive_event_ = true;
9419 return true;
9420 }
9421
9422 bool did_receive_event() { return did_receive_event_; }
9423
9424 private:
9425 bool did_receive_event_ = false;
9426};
9427
9428} // namespace
9429
Fergal Daly2e7e1e12020-06-24 09:18:289430IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Kevin McNee18430052018-08-10 16:50:569431 SpeculativeRenderFrameHostDoesNotReceiveInput) {
9432 GURL url1(embedded_test_server()->GetURL("a.com", "/title1.html"));
9433 EXPECT_TRUE(NavigateToURL(shell(), url1));
9434
9435 RenderWidgetHostViewAndroid* rwhva =
9436 static_cast<RenderWidgetHostViewAndroid*>(
9437 shell()->web_contents()->GetRenderWidgetHostView());
9438 ui::ViewAndroid* rwhva_native_view = rwhva->GetNativeView();
Carlos Caballero15caeeb2021-10-27 09:57:559439 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Kevin McNee18430052018-08-10 16:50:569440
9441 // Start a cross-site navigation.
9442 GURL url2(embedded_test_server()->GetURL("b.com", "/title2.html"));
9443 TestNavigationManager nav_manager(web_contents(), url2);
9444 shell()->LoadURL(url2);
9445
9446 // Wait for the request, but don't commit it yet. This should create a
9447 // speculative RenderFrameHost.
Jiacheng Guo4bdd0be2024-06-11 23:35:219448 nav_manager.WaitForSpeculativeRenderFrameHostCreation();
Kevin McNee18430052018-08-10 16:50:569449 RenderFrameHostImpl* root_speculative_rfh =
9450 root->render_manager()->speculative_frame_host();
9451 EXPECT_TRUE(root_speculative_rfh);
9452 RenderWidgetHostViewAndroid* rwhv_speculative =
9453 static_cast<RenderWidgetHostViewAndroid*>(
9454 root_speculative_rfh->GetView());
9455 ui::ViewAndroid* rwhv_speculative_native_view =
9456 rwhv_speculative->GetNativeView();
9457
9458 ui::ViewAndroid* root_view = web_contents()->GetView()->GetNativeView();
9459 EXPECT_TRUE(root_view);
9460
9461 MockEventHandlerAndroid mock_handler;
9462 rwhva_native_view->set_event_handler(&mock_handler);
9463 MockEventHandlerAndroid mock_handler_speculative;
9464 rwhv_speculative_native_view->set_event_handler(&mock_handler_speculative);
9465 // Avoid having the root try to handle the following event.
9466 root_view->set_event_handler(nullptr);
9467
Chris Harrelson4f2bc05e2025-01-17 17:13:469468 auto size = root_view->GetSizeDIPs();
Kevin McNee18430052018-08-10 16:50:569469 float x = size.width() / 2;
9470 float y = size.height() / 2;
Kartar Singh2d1710a52025-05-23 15:45:369471 ui::MotionEventAndroid::Pointer pointer0(0, x, y, 0, 0, 0, 0, 0, 0);
Keigo Okadfd57572025-07-05 08:36:099472
9473 JNIEnv* env = base::android::AttachCurrentThread();
9474 base::android::ScopedJavaLocalRef<jobject> obj =
9475 JNI_MotionEvent::Java_MotionEvent_obtain(
9476 env, /*downTime=*/0, /*eventTime=*/0, /*action=*/0, /*x=*/0, /*y=*/0,
9477 /*metaState=*/0);
Keigo Okab9022c52025-07-19 02:34:279478 auto event = ui::MotionEventAndroidFactory::CreateFromJava(
9479 env, obj,
9480 /*pix_to_dip=*/1.f / root_view->GetDipScale(),
9481 /*ticks_x=*/0.f,
9482 /*ticks_y=*/0.f,
9483 /*tick_multiplier=*/0.f,
9484 /*oldest_event_time=*/base::TimeTicks(),
9485 /*android_action=*/0,
9486 /*pointer_count=*/1,
9487 /*history_size=*/0,
9488 /*action_index=*/0,
9489 /*android_action_button=*/0,
9490 /*android_gesture_classification=*/0,
9491 /*android_button_state=*/0,
9492 /*raw_offset_x_pixels=*/0,
9493 /*raw_offset_y_pixels=*/0,
9494 /*for_touch_handle=*/false,
9495 /*pointer0=*/&pointer0,
9496 /*pointer1=*/nullptr);
9497 root_view->OnTouchEventForTesting(*event);
Kevin McNee18430052018-08-10 16:50:569498
9499 EXPECT_TRUE(mock_handler.did_receive_event());
9500 EXPECT_FALSE(mock_handler_speculative.did_receive_event());
9501}
9502
Shin Kawamura7c2f4692025-07-30 00:14:339503class SitePerProcessBrowserTestWithSubframePriority
9504 : public SitePerProcessBrowserTest {
9505 public:
9506 SitePerProcessBrowserTestWithSubframePriority() {
9507 scoped_feature_list_.InitWithFeatures(
9508 /* enabled_features= */ {features::kSubframePriorityContribution,
9509 features::kSubframeImportance},
9510 /* disabled_features= */ {});
9511 }
9512
9513 private:
9514 base::test::ScopedFeatureList scoped_feature_list_;
9515};
9516
9517IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTestWithSubframePriority,
9518 TestChildProcessImportance) {
9519 web_contents()->SetPrimaryPageImportance(ChildProcessImportance::IMPORTANT,
9520 ChildProcessImportance::MODERATE);
9521
9522 // Construct root page with one child in different domain.
9523 EXPECT_TRUE(NavigateToURL(
9524 shell(), embedded_test_server()->GetURL(
9525 "a.com", "/cross_site_iframe_factory.html?a(b)")));
9526 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
9527 ASSERT_EQ(1u, root->child_count());
9528 FrameTreeNode* child = root->child_at(0);
9529
9530 // Importance should survive initial navigation.
9531 EXPECT_EQ(ChildProcessImportance::IMPORTANT,
9532 root->current_frame_host()->GetProcess()->GetEffectiveImportance());
9533 EXPECT_EQ(
9534 ChildProcessImportance::MODERATE,
9535 child->current_frame_host()->GetProcess()->GetEffectiveImportance());
9536
9537 // Setting NORMAL importance for child frame.
9538 web_contents()->SetPrimaryPageImportance(ChildProcessImportance::MODERATE,
9539 ChildProcessImportance::NORMAL);
9540 EXPECT_EQ(ChildProcessImportance::MODERATE,
9541 root->current_frame_host()->GetProcess()->GetEffectiveImportance());
9542 EXPECT_EQ(
9543 ChildProcessImportance::NORMAL,
9544 child->current_frame_host()->GetProcess()->GetEffectiveImportance());
9545
9546 // Check setting the same importance for main frame and subframe.
9547 web_contents()->SetPrimaryPageImportance(ChildProcessImportance::MODERATE,
9548 ChildProcessImportance::MODERATE);
9549 EXPECT_EQ(ChildProcessImportance::MODERATE,
9550 root->current_frame_host()->GetProcess()->GetEffectiveImportance());
9551 EXPECT_EQ(
9552 ChildProcessImportance::MODERATE,
9553 child->current_frame_host()->GetProcess()->GetEffectiveImportance());
9554
9555 // Check setting importance.
9556 web_contents()->SetPrimaryPageImportance(ChildProcessImportance::NORMAL,
9557 ChildProcessImportance::NORMAL);
9558 EXPECT_EQ(ChildProcessImportance::NORMAL,
9559 root->current_frame_host()->GetProcess()->GetEffectiveImportance());
9560 EXPECT_EQ(
9561 ChildProcessImportance::NORMAL,
9562 child->current_frame_host()->GetProcess()->GetEffectiveImportance());
9563 web_contents()->SetPrimaryPageImportance(ChildProcessImportance::IMPORTANT,
9564 ChildProcessImportance::MODERATE);
9565 EXPECT_EQ(ChildProcessImportance::IMPORTANT,
9566 root->current_frame_host()->GetProcess()->GetEffectiveImportance());
9567 EXPECT_EQ(
9568 ChildProcessImportance::MODERATE,
9569 child->current_frame_host()->GetProcess()->GetEffectiveImportance());
9570
9571 // Check importance is maintained if child navigates to new domain.
9572 int old_child_process_id =
9573 child->current_frame_host()->GetProcess()->GetDeprecatedID();
9574 EXPECT_TRUE(NavigateToURLFromRenderer(
9575 root->child_at(0),
9576 embedded_test_server()->GetURL("foo.com", "/title2.html")));
9577 int new_child_process_id =
9578 child->current_frame_host()->GetProcess()->GetDeprecatedID();
9579 EXPECT_NE(old_child_process_id, new_child_process_id);
9580 EXPECT_EQ(
9581 ChildProcessImportance::MODERATE,
9582 child->current_frame_host()->GetProcess()->GetEffectiveImportance());
9583 EXPECT_EQ(ChildProcessImportance::IMPORTANT,
9584 root->current_frame_host()->GetProcess()->GetEffectiveImportance());
9585
9586 // Check importance is maintained if root navigates to new domain.
9587 int old_root_process_id =
9588 root->current_frame_host()->GetProcess()->GetDeprecatedID();
9589 EXPECT_TRUE(NavigateToURLFromRenderer(
9590 root, embedded_test_server()->GetURL(
9591 "b.com", "/cross_site_iframe_factory.html?b(a)")));
9592 int new_root_process_id =
9593 root->current_frame_host()->GetProcess()->GetDeprecatedID();
9594 EXPECT_NE(old_root_process_id, new_root_process_id);
9595 EXPECT_EQ(ChildProcessImportance::IMPORTANT,
9596 root->current_frame_host()->GetProcess()->GetEffectiveImportance());
9597
9598 ASSERT_EQ(1u, root->child_count());
9599 child = root->child_at(0);
9600 int new_child_process_id_2 =
9601 child->current_frame_host()->GetProcess()->GetDeprecatedID();
9602 EXPECT_NE(new_child_process_id, new_child_process_id_2);
9603 EXPECT_EQ(
9604 ChildProcessImportance::MODERATE,
9605 child->current_frame_host()->GetProcess()->GetEffectiveImportance());
9606}
9607
9608IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
9609 TestChildProcessImportanceWithNormalSubframes) {
9610 // In this test case, subframe importance is always NORMAL. With that
9611 // WebContents never updates subframe importance.
9612 web_contents()->SetPrimaryPageImportance(ChildProcessImportance::MODERATE,
9613 ChildProcessImportance::NORMAL);
Bo Liu7c6779e92017-08-16 02:02:289614
9615 // Construct root page with one child in different domain.
9616 GURL main_url(embedded_test_server()->GetURL(
9617 "a.com", "/cross_site_iframe_factory.html?a(b)"));
9618 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:559619 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Bo Liu7c6779e92017-08-16 02:02:289620 ASSERT_EQ(1u, root->child_count());
9621 FrameTreeNode* child = root->child_at(0);
9622
Shin Kawamura7c2f4692025-07-30 00:14:339623 // Importance should survive initial navigation.
Bo Liue0ec68b2018-03-26 18:24:069624 EXPECT_EQ(ChildProcessImportance::MODERATE,
9625 root->current_frame_host()->GetProcess()->GetEffectiveImportance());
Bo Liu18c4a5a2017-08-29 16:00:189626 EXPECT_EQ(
Bo Liu16dd7b8322018-05-02 21:19:509627 ChildProcessImportance::NORMAL,
Bo Liue0ec68b2018-03-26 18:24:069628 child->current_frame_host()->GetProcess()->GetEffectiveImportance());
Bo Liu7c6779e92017-08-16 02:02:289629
9630 // Check setting importance.
Shin Kawamura7c2f4692025-07-30 00:14:339631 web_contents()->SetPrimaryPageImportance(ChildProcessImportance::NORMAL,
9632 ChildProcessImportance::NORMAL);
Bo Liue0ec68b2018-03-26 18:24:069633 EXPECT_EQ(ChildProcessImportance::NORMAL,
9634 root->current_frame_host()->GetProcess()->GetEffectiveImportance());
Bo Liu18c4a5a2017-08-29 16:00:189635 EXPECT_EQ(
9636 ChildProcessImportance::NORMAL,
Bo Liue0ec68b2018-03-26 18:24:069637 child->current_frame_host()->GetProcess()->GetEffectiveImportance());
Shin Kawamura7c2f4692025-07-30 00:14:339638 web_contents()->SetPrimaryPageImportance(ChildProcessImportance::IMPORTANT,
9639 ChildProcessImportance::NORMAL);
Bo Liue0ec68b2018-03-26 18:24:069640 EXPECT_EQ(ChildProcessImportance::IMPORTANT,
9641 root->current_frame_host()->GetProcess()->GetEffectiveImportance());
Bo Liu18c4a5a2017-08-29 16:00:189642 EXPECT_EQ(
Bo Liu16dd7b8322018-05-02 21:19:509643 ChildProcessImportance::NORMAL,
Bo Liue0ec68b2018-03-26 18:24:069644 child->current_frame_host()->GetProcess()->GetEffectiveImportance());
Bo Liu7c6779e92017-08-16 02:02:289645
9646 // Check importance is maintained if child navigates to new domain.
Emily Andrewsd15fd762024-12-10 20:41:549647 int old_child_process_id =
9648 child->current_frame_host()->GetProcess()->GetDeprecatedID();
Bo Liu7c6779e92017-08-16 02:02:289649 GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html");
Lukasz Anforowicz69c25dfd2020-11-12 21:50:209650 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), url));
Emily Andrewsd15fd762024-12-10 20:41:549651 int new_child_process_id =
9652 child->current_frame_host()->GetProcess()->GetDeprecatedID();
Bo Liu7c6779e92017-08-16 02:02:289653 EXPECT_NE(old_child_process_id, new_child_process_id);
Bo Liu18c4a5a2017-08-29 16:00:189654 EXPECT_EQ(
Bo Liu16dd7b8322018-05-02 21:19:509655 ChildProcessImportance::NORMAL,
Bo Liue0ec68b2018-03-26 18:24:069656 child->current_frame_host()->GetProcess()->GetEffectiveImportance());
Bo Liu16dd7b8322018-05-02 21:19:509657 EXPECT_EQ(ChildProcessImportance::IMPORTANT,
9658 root->current_frame_host()->GetProcess()->GetEffectiveImportance());
Bo Liu7c6779e92017-08-16 02:02:289659
9660 // Check importance is maintained if root navigates to new domain.
Emily Andrewsd15fd762024-12-10 20:41:549661 int old_root_process_id =
9662 root->current_frame_host()->GetProcess()->GetDeprecatedID();
Bo Liu7c6779e92017-08-16 02:02:289663 child = nullptr; // Going to navigate root to page without any child.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:209664 EXPECT_TRUE(NavigateToURLFromRenderer(root, url));
Bo Liu7c6779e92017-08-16 02:02:289665 EXPECT_EQ(0u, root->child_count());
Emily Andrewsd15fd762024-12-10 20:41:549666 int new_root_process_id =
9667 root->current_frame_host()->GetProcess()->GetDeprecatedID();
Bo Liu7c6779e92017-08-16 02:02:289668 EXPECT_NE(old_root_process_id, new_root_process_id);
Bo Liue0ec68b2018-03-26 18:24:069669 EXPECT_EQ(ChildProcessImportance::IMPORTANT,
9670 root->current_frame_host()->GetProcess()->GetEffectiveImportance());
Bo Liu7c6779e92017-08-16 02:02:289671}
9672
W. James MacLean12ba7972017-07-08 01:58:269673class TouchSelectionControllerClientTestWrapper
9674 : public ui::TouchSelectionControllerClient {
9675 public:
9676 explicit TouchSelectionControllerClientTestWrapper(
9677 ui::TouchSelectionControllerClient* client)
9678 : expected_event_(ui::SELECTION_HANDLES_SHOWN), client_(client) {}
9679
Peter Boström828b9022021-09-21 02:28:439680 TouchSelectionControllerClientTestWrapper(
9681 const TouchSelectionControllerClientTestWrapper&) = delete;
9682 TouchSelectionControllerClientTestWrapper& operator=(
9683 const TouchSelectionControllerClientTestWrapper&) = delete;
9684
W. James MacLean12ba7972017-07-08 01:58:269685 ~TouchSelectionControllerClientTestWrapper() override {}
9686
9687 void InitWaitForSelectionEvent(ui::SelectionEventType expected_event) {
9688 DCHECK(!run_loop_);
9689 expected_event_ = expected_event;
Peter Boström08e7ed82021-04-19 17:49:599690 run_loop_ = std::make_unique<base::RunLoop>();
W. James MacLean12ba7972017-07-08 01:58:269691 }
9692
9693 void Wait() {
9694 DCHECK(run_loop_);
9695 run_loop_->Run();
9696 run_loop_.reset();
9697 }
9698
9699 private:
9700 // TouchSelectionControllerClient:
9701 void OnSelectionEvent(ui::SelectionEventType event) override {
9702 client_->OnSelectionEvent(event);
9703 if (run_loop_ && event == expected_event_)
9704 run_loop_->Quit();
9705 }
9706
9707 bool SupportsAnimation() const override {
9708 return client_->SupportsAnimation();
9709 }
9710
9711 void SetNeedsAnimate() override { client_->SetNeedsAnimate(); }
9712
9713 void MoveCaret(const gfx::PointF& position) override {
9714 client_->MoveCaret(position);
9715 }
9716
9717 void MoveRangeSelectionExtent(const gfx::PointF& extent) override {
9718 client_->MoveRangeSelectionExtent(extent);
9719 }
9720
9721 void SelectBetweenCoordinates(const gfx::PointF& base,
9722 const gfx::PointF& extent) override {
9723 client_->SelectBetweenCoordinates(base, extent);
9724 }
9725
9726 std::unique_ptr<ui::TouchHandleDrawable> CreateDrawable() override {
9727 return client_->CreateDrawable();
9728 }
9729
W. James MacLean37517c12017-09-27 13:55:269730 void DidScroll() override {}
9731
Shimi Zhang438a9602020-12-17 20:42:209732 void OnDragUpdate(const ui::TouchSelectionDraggable::Type type,
9733 const gfx::PointF& position) override {}
Shimi Zhang0f797b72018-02-03 03:01:139734
W. James MacLean12ba7972017-07-08 01:58:269735 ui::SelectionEventType expected_event_;
9736 std::unique_ptr<base::RunLoop> run_loop_;
9737 // Not owned.
Paul Semel89e1f63c2023-06-19 13:34:109738 raw_ptr<ui::TouchSelectionControllerClient, DanglingUntriaged> client_;
W. James MacLean12ba7972017-07-08 01:58:269739};
9740
W. James MacLean53a6e842018-10-17 16:19:019741class TouchSelectionControllerClientAndroidSiteIsolationTest
9742 : public SitePerProcessBrowserTest {
9743 public:
9744 TouchSelectionControllerClientAndroidSiteIsolationTest()
9745 : root_rwhv_(nullptr),
9746 child_rwhv_(nullptr),
9747 child_frame_tree_node_(nullptr),
9748 selection_controller_client_(nullptr) {}
9749
9750 void SetUpCommandLine(base::CommandLine* command_line) override {
Fergal Daly2e7e1e12020-06-24 09:18:289751 SitePerProcessBrowserTestBase::SetUpCommandLine(command_line);
W. James MacLean53a6e842018-10-17 16:19:019752 IsolateAllSitesForTesting(command_line);
9753 }
9754
9755 RenderWidgetHostViewAndroid* GetRenderWidgetHostViewAndroid() {
9756 return static_cast<RenderWidgetHostViewAndroid*>(
9757 shell()->web_contents()->GetRenderWidgetHostView());
9758 }
9759
9760 void SelectWithLongPress(gfx::Point point) {
9761 // Get main frame view for event insertion.
9762 RenderWidgetHostViewAndroid* main_view = GetRenderWidgetHostViewAndroid();
9763
9764 SendTouch(main_view, ui::MotionEvent::Action::DOWN, point);
9765 // action_timeout() is far longer than needed for a LongPress, so we use
9766 // a custom timeout here.
Peter Kastinge5a38ed2021-10-02 03:06:359767 DelayBy(base::Milliseconds(2000));
W. James MacLean53a6e842018-10-17 16:19:019768 SendTouch(main_view, ui::MotionEvent::Action::UP, point);
9769 }
9770
9771 void SimpleTap(gfx::Point point) {
9772 // Get main frame view for event insertion.
9773 RenderWidgetHostViewAndroid* main_view = GetRenderWidgetHostViewAndroid();
9774
9775 SendTouch(main_view, ui::MotionEvent::Action::DOWN, point);
9776 // tiny_timeout() is way shorter than a reasonable user-created tap gesture,
9777 // so we use a custom timeout here.
Peter Kastinge5a38ed2021-10-02 03:06:359778 DelayBy(base::Milliseconds(300));
W. James MacLean53a6e842018-10-17 16:19:019779 SendTouch(main_view, ui::MotionEvent::Action::UP, point);
9780 }
9781
9782 void SetupTest() {
9783 GURL test_url(embedded_test_server()->GetURL(
9784 "a.com", "/cross_site_iframe_factory.html?a(a)"));
9785 EXPECT_TRUE(NavigateToURL(shell(), test_url));
9786 frame_observer_ = std::make_unique<RenderFrameSubmissionObserver>(
9787 shell()->web_contents());
9788 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:559789 ->GetPrimaryFrameTree()
9790 .root();
W. James MacLean53a6e842018-10-17 16:19:019791 EXPECT_EQ(
9792 " Site A\n"
9793 " +--Site A\n"
9794 "Where A = http://a.com/",
9795 FrameTreeVisualizer().DepictFrameTree(root));
9796 TestNavigationObserver observer(shell()->web_contents());
9797 EXPECT_EQ(1u, root->child_count());
9798 child_frame_tree_node_ = root->child_at(0);
9799
9800 root_rwhv_ = static_cast<RenderWidgetHostViewAndroid*>(
9801 root->current_frame_host()->GetRenderWidgetHost()->GetView());
9802 selection_controller_client_ =
9803 new TouchSelectionControllerClientTestWrapper(
Kartar Singh5f8dea32024-12-05 15:10:559804 static_cast<TouchSelectionControllerClientManagerAndroid*>(
9805 root_rwhv_->GetTouchSelectionControllerClientManager()));
W. James MacLean53a6e842018-10-17 16:19:019806 root_rwhv_->SetSelectionControllerClientForTesting(
Keishi Hattori0e45c022021-11-27 09:25:529807 base::WrapUnique(selection_controller_client_.get()));
W. James MacLean53a6e842018-10-17 16:19:019808
9809 // We need to load the desired subframe and then wait until it's stable,
9810 // i.e. generates no new compositor frames for some reasonable time period:
9811 // a stray frame between touch selection's pre-handling of GestureLongPress
9812 // and the expected frame containing the selected region can confuse the
9813 // TouchSelectionController, causing it to fail to show selection handles.
9814 // Note this is an issue with the TouchSelectionController in general, and
9815 // not a property of this test.
9816 GURL child_url(
9817 embedded_test_server()->GetURL("b.com", "/touch_selection.html"));
Keishi Hattori0e45c022021-11-27 09:25:529818 EXPECT_TRUE(
9819 NavigateToURLFromRenderer(child_frame_tree_node_.get(), child_url));
W. James MacLean53a6e842018-10-17 16:19:019820 EXPECT_EQ(
9821 " Site A ------------ proxies for B\n"
9822 " +--Site B ------- proxies for A\n"
9823 "Where A = http://a.com/\n"
9824 " B = http://b.com/",
9825 FrameTreeVisualizer().DepictFrameTree(root));
9826 // The child will change with the cross-site navigation. It shouldn't change
9827 // after this.
9828 child_frame_tree_node_ = root->child_at(0);
kylechara7c549b2019-07-29 17:47:289829 WaitForHitTestData(child_frame_tree_node_->current_frame_host());
W. James MacLean53a6e842018-10-17 16:19:019830
9831 child_rwhv_ = static_cast<RenderWidgetHostViewChildFrame*>(
9832 child_frame_tree_node_->current_frame_host()
9833 ->GetRenderWidgetHost()
9834 ->GetView());
9835
9836 EXPECT_EQ(child_url, observer.last_navigation_url());
9837 EXPECT_TRUE(observer.last_navigation_succeeded());
W. James MacLean53a6e842018-10-17 16:19:019838 }
9839
9840 // This must be called before the main-frame's RenderWidgetHostView is freed,
9841 // else we'll have a nullptr dereference on shutdown.
9842 void ShutdownTest() {
9843 ASSERT_TRUE(frame_observer_);
9844 frame_observer_.reset();
9845 }
9846
9847 gfx::PointF GetPointInChild() {
9848 gfx::PointF point_f;
Chris Fredricksonb854bbf2023-03-27 17:27:449849 std::string str = EvalJs(child_frame_tree_node_->current_frame_host(),
Michelle49f3efa2023-08-22 03:20:169850 "get_top_left_of_text()")
Chris Fredricksonb854bbf2023-03-27 17:27:449851 .ExtractString();
W. James MacLean53a6e842018-10-17 16:19:019852 ConvertJSONToPoint(str, &point_f);
Michelle49f3efa2023-08-22 03:20:169853 // Offset the point so that it is within the text. Character dimensions are
9854 // based on the font size in `touch_selection.html`.
9855 constexpr int kCharacterWidth = 15;
9856 constexpr int kCharacterHeight = 15;
9857 point_f.Offset(2 * kCharacterWidth, 0.5f * kCharacterHeight);
W. James MacLean53a6e842018-10-17 16:19:019858 point_f = child_rwhv()->TransformPointToRootCoordSpaceF(point_f);
9859 return point_f;
9860 }
9861
9862 void VerifyHandlePosition() {
9863 // Check that selection handles are close to the selection range.
9864 // The test will timeout if this never happens.
9865 ui::TouchSelectionController* touch_selection_controller =
9866 root_rwhv()->touch_selection_controller();
9867
9868 bool handles_in_place = false;
9869 while (!handles_in_place) {
9870 gfx::PointF selection_start =
9871 touch_selection_controller->GetStartPosition();
9872 gfx::PointF selection_end = touch_selection_controller->GetEndPosition();
9873 gfx::RectF handle_start =
9874 touch_selection_controller->GetStartHandleRect();
9875 gfx::RectF handle_end = touch_selection_controller->GetEndHandleRect();
9876
9877 // Not all Android bots seem to actually show the handle, so check first.
9878 if (handle_start.IsEmpty()) {
9879 handles_in_place = true;
9880 } else {
9881 bool has_end_handle =
9882 !touch_selection_controller->GetEndHandleRect().IsEmpty();
9883 // handle_start.y() defined the top of the handle's rect, and x() is
9884 // left.
9885 bool start_near_y =
9886 std::abs(selection_start.y() - handle_start.y()) <= 3.f;
9887 bool start_in_x_range = selection_start.x() >= handle_start.x() &&
9888 selection_start.x() <= handle_start.right();
9889 bool end_near_y = std::abs(selection_end.y() - handle_end.y()) <= 3.f;
9890 bool end_in_x_range = selection_end.x() >= handle_end.x() &&
9891 selection_end.x() <= handle_end.right();
9892 handles_in_place = start_near_y && start_in_x_range && end_near_y &&
9893 end_in_x_range && has_end_handle;
9894 }
9895 if (!handles_in_place)
Peter Kastinge5a38ed2021-10-02 03:06:359896 DelayBy(base::Milliseconds(100));
W. James MacLean53a6e842018-10-17 16:19:019897 }
9898 }
9899
9900 RenderWidgetHostViewAndroid* root_rwhv() { return root_rwhv_; }
9901
9902 RenderWidgetHostViewChildFrame* child_rwhv() { return child_rwhv_; }
9903
9904 float PageScaleFactor() {
9905 return frame_observer_->LastRenderFrameMetadata().page_scale_factor;
9906 }
9907
9908 TouchSelectionControllerClientTestWrapper* selection_controller_client() {
9909 return selection_controller_client_;
9910 }
9911
9912 void OnSyntheticGestureSent() {
9913 gesture_run_loop_ = std::make_unique<base::RunLoop>();
9914 gesture_run_loop_->Run();
9915 }
9916
9917 void OnSyntheticGestureCompleted(SyntheticGesture::Result result) {
9918 EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
9919 gesture_run_loop_->Quit();
9920 }
9921
9922 protected:
9923 void DelayBy(base::TimeDelta delta) {
9924 base::RunLoop run_loop;
Sean Maher5b9af51f2022-11-21 15:32:479925 base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
W. James MacLean53a6e842018-10-17 16:19:019926 FROM_HERE, run_loop.QuitClosure(), delta);
9927 run_loop.Run();
9928 }
9929
9930 private:
9931 void SendTouch(RenderWidgetHostViewAndroid* view,
9932 ui::MotionEvent::Action action,
9933 gfx::Point point) {
9934 DCHECK(action >= ui::MotionEvent::Action::DOWN &&
9935 action < ui::MotionEvent::Action::CANCEL);
9936
Kartar Singh2d1710a52025-05-23 15:45:369937 ui::MotionEventAndroid::Pointer p(0, point.x(), point.y(), 10, 0, 0, 0, 0,
9938 0);
W. James MacLean53a6e842018-10-17 16:19:019939 JNIEnv* env = base::android::AttachCurrentThread();
Violetta Fedotova4fc0d7522023-03-01 12:56:109940 auto time_ns = (ui::EventTimeForNow() - base::TimeTicks()).InNanoseconds();
Keigo Okadfd57572025-07-05 08:36:099941
9942 base::android::ScopedJavaLocalRef<jobject> obj =
9943 JNI_MotionEvent::Java_MotionEvent_obtain(
9944 env, /*downTime=*/0, /*eventTime=*/0, /*action=*/0, /*x=*/0,
9945 /*y=*/0, /*metaState=*/0);
Keigo Okab9022c52025-07-19 02:34:279946 auto touch = ui::MotionEventAndroidFactory::CreateFromJava(
9947 env, obj,
9948 /*pix_to_dip=*/1.f,
9949 /*ticks_x=*/0,
9950 /*ticks_y=*/0,
9951 /*tick_multiplier=*/0,
9952 /*oldest_event_time=*/base::TimeTicks::FromJavaNanoTime(time_ns),
9953 /*android_action=*/ui::MotionEventAndroid::GetAndroidAction(action),
9954 /*pointer_count=*/1,
9955 /*history_size=*/0,
9956 /*action_index=*/0,
9957 /*android_action_button=*/0,
9958 /*android_gesture_classification=*/0,
9959 /*android_button_state=*/0,
9960 /*raw_offset_x_pixels=*/0,
9961 /*raw_offset_y_pixels=*/0,
9962 /*for_touch_handle=*/false,
9963 /*pointer0=*/&p,
9964 /*pointer1=*/nullptr);
9965 view->OnTouchEvent(*touch);
W. James MacLean53a6e842018-10-17 16:19:019966 }
9967
Paul Semel89e1f63c2023-06-19 13:34:109968 raw_ptr<RenderWidgetHostViewAndroid, DanglingUntriaged> root_rwhv_;
9969 raw_ptr<RenderWidgetHostViewChildFrame, DanglingUntriaged> child_rwhv_;
9970 raw_ptr<FrameTreeNode, DanglingUntriaged> child_frame_tree_node_;
W. James MacLean53a6e842018-10-17 16:19:019971 std::unique_ptr<RenderFrameSubmissionObserver> frame_observer_;
Paul Semel89e1f63c2023-06-19 13:34:109972 raw_ptr<TouchSelectionControllerClientTestWrapper, DanglingUntriaged>
Keishi Hattori0e45c022021-11-27 09:25:529973 selection_controller_client_;
W. James MacLean53a6e842018-10-17 16:19:019974
9975 std::unique_ptr<base::RunLoop> gesture_run_loop_;
9976};
9977
Fergal Daly2e7e1e12020-06-24 09:18:289978IN_PROC_BROWSER_TEST_P(TouchSelectionControllerClientAndroidSiteIsolationTest,
W. James MacLean12ba7972017-07-08 01:58:269979 BasicSelectionIsolatedIframe) {
W. James MacLean53a6e842018-10-17 16:19:019980 // Load test URL with cross-process child.
9981 SetupTest();
W. James MacLean12ba7972017-07-08 01:58:269982
9983 EXPECT_EQ(ui::TouchSelectionController::INACTIVE,
W. James MacLean53a6e842018-10-17 16:19:019984 root_rwhv()->touch_selection_controller()->active_status());
W. James MacLean12ba7972017-07-08 01:58:269985 // Find the location of some text to select.
W. James MacLean53a6e842018-10-17 16:19:019986 gfx::PointF point_f = GetPointInChild();
W. James MacLean12ba7972017-07-08 01:58:269987
9988 // Initiate selection with a sequence of events that go through the targeting
9989 // system.
W. James MacLean53a6e842018-10-17 16:19:019990 selection_controller_client()->InitWaitForSelectionEvent(
W. James MacLean12ba7972017-07-08 01:58:269991 ui::SELECTION_HANDLES_SHOWN);
9992
9993 SelectWithLongPress(gfx::Point(point_f.x(), point_f.y()));
9994
W. James MacLean53a6e842018-10-17 16:19:019995 selection_controller_client()->Wait();
W. James MacLean12ba7972017-07-08 01:58:269996
9997 // Check that selection is active and the quick menu is showing.
9998 EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE,
W. James MacLean53a6e842018-10-17 16:19:019999 root_rwhv()->touch_selection_controller()->active_status());
W. James MacLean12ba7972017-07-08 01:58:2610000
W. James MacLean53a6e842018-10-17 16:19:0110001 // Make sure handles are correctly positioned.
10002 VerifyHandlePosition();
W. James MacLeanfdef91872018-09-29 19:17:0310003
W. James MacLean12ba7972017-07-08 01:58:2610004 // Tap inside/outside the iframe and make sure the selection handles go away.
W. James MacLean53a6e842018-10-17 16:19:0110005 selection_controller_client()->InitWaitForSelectionEvent(
W. James MacLean12ba7972017-07-08 01:58:2610006 ui::SELECTION_HANDLES_CLEARED);
10007 // Since Android tests may run with page_scale_factor < 1, use an offset a
10008 // bigger than +/-1 for doing the inside/outside taps to cancel the selection
10009 // handles.
W. James MacLeana7f4ab12017-12-13 00:37:5510010 gfx::PointF point_inside_iframe =
W. James MacLean53a6e842018-10-17 16:19:0110011 child_rwhv()->TransformPointToRootCoordSpaceF(gfx::PointF(+5.f, +5.f));
W. James MacLean12ba7972017-07-08 01:58:2610012 SimpleTap(gfx::Point(point_inside_iframe.x(), point_inside_iframe.y()));
W. James MacLean53a6e842018-10-17 16:19:0110013 selection_controller_client()->Wait();
W. James MacLean12ba7972017-07-08 01:58:2610014
10015 EXPECT_EQ(ui::TouchSelectionController::INACTIVE,
W. James MacLean53a6e842018-10-17 16:19:0110016 root_rwhv()->touch_selection_controller()->active_status());
W. James MacLean12ba7972017-07-08 01:58:2610017
10018 // Let's wait for the previous events to clear the round-trip to the renders
10019 // and back.
Peter Kastinge5a38ed2021-10-02 03:06:3510020 DelayBy(base::Milliseconds(2000));
W. James MacLean12ba7972017-07-08 01:58:2610021
10022 // Initiate selection with a sequence of events that go through the targeting
10023 // system. Repeat of above but this time we'l cancel the selection by
10024 // tapping outside of the OOPIF.
W. James MacLean53a6e842018-10-17 16:19:0110025 selection_controller_client()->InitWaitForSelectionEvent(
W. James MacLean12ba7972017-07-08 01:58:2610026 ui::SELECTION_HANDLES_SHOWN);
10027
10028 SelectWithLongPress(gfx::Point(point_f.x(), point_f.y()));
10029
W. James MacLean53a6e842018-10-17 16:19:0110030 selection_controller_client()->Wait();
W. James MacLean12ba7972017-07-08 01:58:2610031
10032 // Check that selection is active and the quick menu is showing.
10033 EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE,
W. James MacLean53a6e842018-10-17 16:19:0110034 root_rwhv()->touch_selection_controller()->active_status());
W. James MacLean12ba7972017-07-08 01:58:2610035
10036 // Tap inside/outside the iframe and make sure the selection handles go away.
W. James MacLean53a6e842018-10-17 16:19:0110037 selection_controller_client()->InitWaitForSelectionEvent(
W. James MacLean12ba7972017-07-08 01:58:2610038 ui::SELECTION_HANDLES_CLEARED);
10039 // Since Android tests may run with page_scale_factor < 1, use an offset a
10040 // bigger than +/-1 for doing the inside/outside taps to cancel the selection
10041 // handles.
W. James MacLeana7f4ab12017-12-13 00:37:5510042 gfx::PointF point_outside_iframe =
W. James MacLean53a6e842018-10-17 16:19:0110043 child_rwhv()->TransformPointToRootCoordSpaceF(gfx::PointF(-5.f, -5.f));
W. James MacLean12ba7972017-07-08 01:58:2610044 SimpleTap(gfx::Point(point_outside_iframe.x(), point_outside_iframe.y()));
W. James MacLean53a6e842018-10-17 16:19:0110045 selection_controller_client()->Wait();
W. James MacLean12ba7972017-07-08 01:58:2610046
10047 EXPECT_EQ(ui::TouchSelectionController::INACTIVE,
W. James MacLean53a6e842018-10-17 16:19:0110048 root_rwhv()->touch_selection_controller()->active_status());
10049
10050 // Cleanup before shutting down.
10051 ShutdownTest();
W. James MacLean12ba7972017-07-08 01:58:2610052}
10053
W. James MacLean53a6e842018-10-17 16:19:0110054// This test verifies that the handles associated with an active touch selection
10055// are still correctly positioned after a pinch-zoom operation.
Xiaohan Wang1ecfd002022-01-19 22:33:1010056#if BUILDFLAG(IS_ANDROID) // Flaky on Android. See https://crbug.com/906204.
Donn Denmanf318fc42018-11-17 01:19:4510057#define MAYBE_SelectionThenPinchInOOPIF DISABLED_SelectionThenPinchInOOPIF
10058#else
10059#define MAYBE_SelectionThenPinchInOOPIF SelectionThenPinchInOOPIF
10060#endif
Fergal Daly2e7e1e12020-06-24 09:18:2810061IN_PROC_BROWSER_TEST_P(TouchSelectionControllerClientAndroidSiteIsolationTest,
Donn Denmanf318fc42018-11-17 01:19:4510062 MAYBE_SelectionThenPinchInOOPIF) {
W. James MacLean53a6e842018-10-17 16:19:0110063 // Load test URL with cross-process child.
10064 SetupTest();
10065
10066 EXPECT_EQ(ui::TouchSelectionController::INACTIVE,
10067 root_rwhv()->touch_selection_controller()->active_status());
10068 // Find the location of some text to select.
10069 gfx::PointF point_f = GetPointInChild();
10070
10071 // Initiate selection with a sequence of events that go through the targeting
10072 // system.
10073 selection_controller_client()->InitWaitForSelectionEvent(
10074 ui::SELECTION_HANDLES_SHOWN);
10075
10076 SelectWithLongPress(gfx::Point(point_f.x(), point_f.y()));
10077
10078 selection_controller_client()->Wait();
10079
10080 // Check that selection is active and the quick menu is showing.
10081 EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE,
10082 root_rwhv()->touch_selection_controller()->active_status());
10083
10084 // Make sure handles are correctly positioned.
10085 VerifyHandlePosition();
10086
10087 // Generate a pinch sequence, then re-verify handles are in the correct
10088 // location.
10089 float page_scale_delta = 2.f;
10090 float current_page_scale = PageScaleFactor();
10091 float target_page_scale = current_page_scale * page_scale_delta;
10092
10093 SyntheticPinchGestureParams params;
10094 // We'll use the selection point for the pinch center to minimize the
10095 // likelihood of the selection getting zoomed offscreen.
10096 params.anchor = point_f;
10097 // Note: the |scale_factor| in |params| is actually treated as a delta, not
10098 // absolute, page scale.
10099 params.scale_factor = page_scale_delta;
10100 auto synthetic_pinch_gesture =
10101 std::make_unique<SyntheticTouchscreenPinchGesture>(params);
10102
10103 auto* host =
10104 static_cast<RenderWidgetHostImpl*>(root_rwhv()->GetRenderWidgetHost());
10105 InputEventAckWaiter gesture_pinch_end_waiter(
Dave Tapuska347d60a2020-04-21 23:55:4710106 host, blink::WebInputEvent::Type::kGesturePinchEnd);
W. James MacLean53a6e842018-10-17 16:19:0110107 host->QueueSyntheticGesture(
10108 std::move(synthetic_pinch_gesture),
10109 base::BindOnce(&TouchSelectionControllerClientAndroidSiteIsolationTest::
10110 OnSyntheticGestureCompleted,
10111 base::Unretained(this)));
10112 OnSyntheticGestureSent();
10113 // Make sure the gesture is complete from the renderer's point of view.
10114 gesture_pinch_end_waiter.Wait();
10115
10116 VerifyHandlePosition();
W. James MacLeanf50e1aea2018-10-19 15:57:1710117 // TODO(wjmaclean): Investigate why SyntheticTouchscreenPinchGesture final
10118 // scales are so imprecise.
10119 // https://crbug.com/897173
10120 const float kScaleFactorTolerance = 0.05f;
10121 EXPECT_NEAR(target_page_scale, PageScaleFactor(), kScaleFactorTolerance);
W. James MacLean53a6e842018-10-17 16:19:0110122
10123 // Cleanup before shutting down.
10124 ShutdownTest();
10125}
Xiaohan Wang1ecfd002022-01-19 22:33:1010126#endif // BUILDFLAG(IS_ANDROID)
W. James MacLean12ba7972017-07-08 01:58:2610127
W. James MacLeanad0bd35f2018-12-12 22:10:0510128class TouchEventObserver : public RenderWidgetHost::InputEventObserver {
10129 public:
10130 TouchEventObserver(std::vector<uint32_t>* outgoing_touch_event_ids,
10131 std::vector<uint32_t>* acked_touch_event_ids)
10132 : outgoing_touch_event_ids_(outgoing_touch_event_ids),
10133 acked_touch_event_ids_(acked_touch_event_ids) {}
10134
Peter Boström9b036532021-10-28 23:37:2810135 TouchEventObserver(const TouchEventObserver&) = delete;
10136 TouchEventObserver& operator=(const TouchEventObserver&) = delete;
10137
Kartar Singh8a7923a22024-12-02 04:40:1510138 void OnInputEvent(const RenderWidgetHost& widget,
10139 const blink::WebInputEvent& event) override {
W. James MacLeanad0bd35f2018-12-12 22:10:0510140 if (!blink::WebInputEvent::IsTouchEventType(event.GetType()))
10141 return;
10142
10143 const auto& touch_event = static_cast<const blink::WebTouchEvent&>(event);
10144 outgoing_touch_event_ids_->push_back(touch_event.unique_touch_event_id);
10145 }
10146
Kartar Singh8a7923a22024-12-02 04:40:1510147 void OnInputEventAck(const RenderWidgetHost& widget,
10148 blink::mojom::InputEventResultSource source,
Dave Tapuskae01e0fde2020-04-20 18:28:4110149 blink::mojom::InputEventResultState state,
W. James MacLeanad0bd35f2018-12-12 22:10:0510150 const blink::WebInputEvent& event) override {
10151 if (!blink::WebInputEvent::IsTouchEventType(event.GetType()))
10152 return;
10153
10154 const auto& touch_event = static_cast<const blink::WebTouchEvent&>(event);
10155 acked_touch_event_ids_->push_back(touch_event.unique_touch_event_id);
10156 }
10157
10158 private:
Keishi Hattori0e45c022021-11-27 09:25:5210159 raw_ptr<std::vector<uint32_t>> outgoing_touch_event_ids_;
10160 raw_ptr<std::vector<uint32_t>> acked_touch_event_ids_;
W. James MacLeanad0bd35f2018-12-12 22:10:0510161};
10162
10163// This test verifies the ability of the TouchEventAckQueue to send TouchEvent
10164// acks to the root view in the correct order in the event of a slow renderer.
10165// This test uses a main-frame which acks instantly (no touch handler), and a
10166// child frame which acks very slowly. A synthetic gesture tap is sent to the
10167// child first, then the main frame. In this scenario, we expect the touch
10168// events sent to the main-frame to ack first, which will be problematic if
10169// the events are acked to the GestureRecognizer out of order.
Justin DeWitt136b01a2019-03-22 23:34:5710170//
10171// This test is disabled due to flakiness on all platforms, but especially on
10172// Android. See https://crbug.com/945025.
Xiaocheng Hu92066b34f2021-03-13 00:37:1510173IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
10174 DISABLED_TouchEventAckQueueOrdering) {
W. James MacLeanad0bd35f2018-12-12 22:10:0510175 GURL main_url(embedded_test_server()->GetURL(
10176 "a.com", "/cross_site_iframe_factory.html?a(b)"));
10177 EXPECT_TRUE(NavigateToURL(shell(), main_url));
10178
Carlos Caballero15caeeb2021-10-27 09:57:5510179 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLeanad0bd35f2018-12-12 22:10:0510180 ASSERT_EQ(1u, root->child_count());
10181 FrameTreeNode* child_node = root->child_at(0);
10182
Michael Spang3105cbf2019-02-23 02:54:2610183 // Add a *slow* & non-passive touch event handler in the child. It needs to
10184 // be non-passive to ensure TouchStart doesn't get acked until after the
10185 // touch handler completes.
Avi Drissmanc91bd8e2021-04-19 23:58:4410186 EXPECT_TRUE(ExecJs(child_node,
10187 "touch_event_count = 0;\
W. James MacLeanad0bd35f2018-12-12 22:10:0510188 function touch_handler(ev) {\
10189 var start = Date.now();\
10190 while (Date.now() < start + 1000) {}\
10191 touch_event_count++;\
10192 }\
10193 document.body.addEventListener('touchstart', touch_handler,\
10194 { passive : false });\
10195 document.body.addEventListener('touchend', touch_handler,\
10196 { passive : false });"));
10197
kylechara7c549b2019-07-29 17:47:2810198 WaitForHitTestData(child_node->current_frame_host());
W. James MacLeanad0bd35f2018-12-12 22:10:0510199
10200 auto* root_host = static_cast<RenderWidgetHostImpl*>(
10201 root->current_frame_host()->GetRenderWidgetHost());
10202 auto* child_host = static_cast<RenderWidgetHostImpl*>(
10203 child_node->current_frame_host()->GetRenderWidgetHost());
10204
10205 // Create InputEventObserver for both, with access to common queue for
10206 // logging.
10207 std::vector<uint32_t> outgoing_touch_event_ids;
10208 std::vector<uint32_t> acked_touch_event_ids;
10209
10210 TouchEventObserver parent_touch_event_observer(&outgoing_touch_event_ids,
10211 &acked_touch_event_ids);
10212 TouchEventObserver child_touch_event_observer(&outgoing_touch_event_ids,
10213 &acked_touch_event_ids);
10214
10215 root_host->AddInputEventObserver(&parent_touch_event_observer);
10216 child_host->AddInputEventObserver(&child_touch_event_observer);
10217
10218 InputEventAckWaiter root_ack_waiter(root_host,
Dave Tapuska347d60a2020-04-21 23:55:4710219 blink::WebInputEvent::Type::kTouchEnd);
W. James MacLeanad0bd35f2018-12-12 22:10:0510220 InputEventAckWaiter child_ack_waiter(child_host,
Dave Tapuska347d60a2020-04-21 23:55:4710221 blink::WebInputEvent::Type::kTouchEnd);
W. James MacLeanad0bd35f2018-12-12 22:10:0510222 InputEventAckWaiter child_gesture_tap_ack_waiter(
Dave Tapuska347d60a2020-04-21 23:55:4710223 child_host, blink::WebInputEvent::Type::kGestureTap);
W. James MacLeanad0bd35f2018-12-12 22:10:0510224
10225 // Create GestureTap for child.
10226 gfx::PointF child_tap_point;
10227 {
10228 // We need to know the center of the child's body, but in root view
10229 // coordinates.
Avi Drissmanc91bd8e2021-04-19 23:58:4410230 std::string str = EvalJs(child_node,
10231 "var rect = document.body.getBoundingClientRect();\
W. James MacLeanad0bd35f2018-12-12 22:10:0510232 var point = {\
10233 x: rect.left + rect.width / 2,\
10234 y: rect.top + rect.height / 2\
10235 };\
Avi Drissmanc91bd8e2021-04-19 23:58:4410236 JSON.stringify(point);")
10237 .ExtractString();
W. James MacLeanad0bd35f2018-12-12 22:10:0510238 ConvertJSONToPoint(str, &child_tap_point);
10239 child_tap_point = child_node->current_frame_host()
10240 ->GetView()
10241 ->TransformPointToRootCoordSpaceF(child_tap_point);
10242 }
10243 SyntheticTapGestureParams child_tap_params;
10244 child_tap_params.position = child_tap_point;
Gyuyoung Kimb37010642021-02-22 06:40:5810245 child_tap_params.gesture_source_type =
10246 content::mojom::GestureSourceType::kTouchInput;
W. James MacLeanad0bd35f2018-12-12 22:10:0510247 child_tap_params.duration_ms = 300.f;
10248 auto child_tap_gesture =
10249 std::make_unique<SyntheticTapGesture>(child_tap_params);
10250
10251 // Create GestureTap for root.
10252 SyntheticTapGestureParams root_tap_params;
10253 root_tap_params.position = gfx::PointF(5.f, 5.f);
10254 root_tap_params.duration_ms = 300.f;
Gyuyoung Kimb37010642021-02-22 06:40:5810255 root_tap_params.gesture_source_type =
10256 content::mojom::GestureSourceType::kTouchInput;
W. James MacLeanad0bd35f2018-12-12 22:10:0510257 auto root_tap_gesture =
10258 std::make_unique<SyntheticTapGesture>(root_tap_params);
10259
10260 // Queue both GestureTaps, child first.
Michael Spang3105cbf2019-02-23 02:54:2610261 // Note that we want the SyntheticGestureController to start sending the
10262 // root tap gesture as soon as it's finished sending the events for the
10263 // child tap gesture, otherwise it would wait for the acks from the child
10264 // before starting the root gesture which defeats the purpose of this test.
10265 root_host->QueueSyntheticGestureCompleteImmediately(
10266 std::move(child_tap_gesture));
W. James MacLeanad0bd35f2018-12-12 22:10:0510267 root_host->QueueSyntheticGesture(
10268 std::move(root_tap_gesture),
10269 base::BindOnce([](SyntheticGesture::Result result) {
10270 EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
10271 }));
10272
10273 root_ack_waiter.Wait();
10274 child_ack_waiter.Wait();
10275
10276 // Verify the child did receive two touch events.
Avi Drissmanc91bd8e2021-04-19 23:58:4410277 EXPECT_EQ(2, EvalJs(child_node, "touch_event_count;"));
W. James MacLeanad0bd35f2018-12-12 22:10:0510278
10279 // Verify Acks from parent arrive first.
10280 EXPECT_EQ(4u, outgoing_touch_event_ids.size());
10281 EXPECT_EQ(4u, acked_touch_event_ids.size());
10282 EXPECT_EQ(outgoing_touch_event_ids[2], acked_touch_event_ids[0]);
10283 EXPECT_EQ(outgoing_touch_event_ids[3], acked_touch_event_ids[1]);
10284
10285 // Verify no DCHECKs from GestureRecognizer, indicating acks happened in
10286 // order.
10287 child_gesture_tap_ack_waiter.Wait();
10288}
10289
Ian Clelland5cbaaf82017-11-27 22:00:0310290// Verify that sandbox flags specified by a CSP header are properly inherited by
10291// child frames, but are removed when the frame navigates.
Fergal Daly2e7e1e12020-06-24 09:18:2810292IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Ian Clelland5cbaaf82017-11-27 22:00:0310293 ActiveSandboxFlagsMaintainedAcrossNavigation) {
W. James MacLeanb0a37b7b2023-11-27 20:58:0410294 bool sandboxed_iframes_are_isolated =
10295 SiteIsolationPolicy::AreIsolatedSandboxedIframesEnabled();
Ian Clelland5cbaaf82017-11-27 22:00:0310296 GURL main_url(
10297 embedded_test_server()->GetURL("a.com", "/sandbox_main_frame_csp.html"));
W. James MacLeanb0a37b7b2023-11-27 20:58:0410298 RenderFrameDeletedObserver deleted_observer(
10299 web_contents()->GetPrimaryFrameTree().root()->current_frame_host());
Ian Clelland5cbaaf82017-11-27 22:00:0310300 EXPECT_TRUE(NavigateToURL(shell(), main_url));
W. James MacLeanb0a37b7b2023-11-27 20:58:0410301 if (sandboxed_iframes_are_isolated) {
10302 // The initial navigation is away from an initial un-sandboxed mainframe to
10303 // a sandboxed mainframe, so before we call DepictFrameTree below we need to
10304 // wait for the RenderFrameHost from the initial mainframe to be deleted and
10305 // its proxies removed.
Alison Gale81f4f2c72024-04-22 19:33:3110306 // TODO(crbug.com/40282613): See if we can reuse the initial RFH for
W. James MacLeanb0a37b7b2023-11-27 20:58:0410307 // a navigation to a sandboxed frame instead?
10308 deleted_observer.WaitUntilDeleted();
10309 }
Ian Clelland5cbaaf82017-11-27 22:00:0310310
10311 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:5510312 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Ian Clelland5cbaaf82017-11-27 22:00:0310313 ASSERT_EQ(1u, root->child_count());
10314
10315 EXPECT_EQ(
W. James MacLeanb0a37b7b2023-11-27 20:58:0410316 base::StringPrintf(" Site A\n"
10317 " +--Site A\n"
10318 "Where A = http://a.com/%s",
10319 sandboxed_iframes_are_isolated ? " (sandboxed)" : ""),
Ian Clelland5cbaaf82017-11-27 22:00:0310320 DepictFrameTree(root));
W. James MacLeanb0a37b7b2023-11-27 20:58:0410321 if (sandboxed_iframes_are_isolated &&
10322 blink::features::kIsolateSandboxedIframesGroupingParam.Get() ==
10323 blink::features::IsolateSandboxedIframesGrouping::kPerOrigin) {
10324 // In per-origin IsolatedSandboxedIframes mode, the server port is retained
10325 // in the site URL.
10326 GURL main_site(embedded_test_server()->GetURL("a.com", "/"));
10327 EXPECT_EQ(main_site,
10328 root->current_frame_host()->GetSiteInstance()->GetSiteURL());
10329 }
Ian Clelland5cbaaf82017-11-27 22:00:0310330
10331 FrameTreeNode* child_node = root->child_at(0);
10332
10333 EXPECT_EQ(shell()->web_contents()->GetSiteInstance(),
10334 child_node->current_frame_host()->GetSiteInstance());
10335
10336 // Main page is served with a CSP header applying sandbox flags allow-popups,
10337 // allow-pointer-lock and allow-scripts.
arthursonzognib93a4472020-04-10 07:38:0010338 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:0310339 root->pending_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:0010340 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:0310341 root->effective_frame_policy().sandbox_flags);
Arthur Sonzogni4ba3104f2022-03-09 09:04:3910342 EXPECT_EQ(
10343 network::mojom::WebSandboxFlags::kAll &
10344 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
10345 ~network::mojom::WebSandboxFlags::kPointerLock &
10346 ~network::mojom::WebSandboxFlags::kPopups &
10347 ~network::mojom::WebSandboxFlags::kScripts &
10348 ~network::mojom::WebSandboxFlags::kTopNavigationToCustomProtocols,
10349 root->active_sandbox_flags());
Ian Clelland5cbaaf82017-11-27 22:00:0310350
10351 // Child frame has iframe sandbox flags allow-popups, allow-scripts, and
10352 // allow-orientation-lock. It should receive the intersection of those with
10353 // the parent sandbox flags: allow-popups and allow-scripts.
Arthur Sonzogni4ba3104f2022-03-09 09:04:3910354 EXPECT_EQ(
10355 network::mojom::WebSandboxFlags::kAll &
10356 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
10357 ~network::mojom::WebSandboxFlags::kPopups &
10358 ~network::mojom::WebSandboxFlags::kScripts &
10359 ~network::mojom::WebSandboxFlags::kTopNavigationToCustomProtocols,
10360 root->child_at(0)->pending_frame_policy().sandbox_flags);
10361 EXPECT_EQ(
10362 network::mojom::WebSandboxFlags::kAll &
10363 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
10364 ~network::mojom::WebSandboxFlags::kPopups &
10365 ~network::mojom::WebSandboxFlags::kScripts &
10366 ~network::mojom::WebSandboxFlags::kTopNavigationToCustomProtocols,
10367 root->child_at(0)->effective_frame_policy().sandbox_flags);
Ian Clelland5cbaaf82017-11-27 22:00:0310368
10369 // Document in child frame is served with a CSP header giving sandbox flags
10370 // allow-scripts, allow-popups and allow-pointer-lock. The final effective
10371 // flags should only include allow-scripts and allow-popups.
Arthur Sonzogni4ba3104f2022-03-09 09:04:3910372 EXPECT_EQ(
10373 network::mojom::WebSandboxFlags::kAll &
10374 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
10375 ~network::mojom::WebSandboxFlags::kPopups &
10376 ~network::mojom::WebSandboxFlags::kScripts &
10377 ~network::mojom::WebSandboxFlags::kTopNavigationToCustomProtocols,
10378 root->child_at(0)->active_sandbox_flags());
Ian Clelland5cbaaf82017-11-27 22:00:0310379
10380 // Navigate the child frame to a new page. This should clear any CSP-applied
10381 // sandbox flags.
10382 GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2010383 EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), frame_url));
Ian Clelland5cbaaf82017-11-27 22:00:0310384
10385 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
10386 child_node->current_frame_host()->GetSiteInstance());
10387
10388 // Navigating should reset the sandbox flags to the frame owner flags:
10389 // allow-popups and allow-scripts.
Arthur Sonzogni4ba3104f2022-03-09 09:04:3910390 EXPECT_EQ(
10391 network::mojom::WebSandboxFlags::kAll &
10392 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
10393 ~network::mojom::WebSandboxFlags::kPopups &
10394 ~network::mojom::WebSandboxFlags::kScripts &
10395 ~network::mojom::WebSandboxFlags::kTopNavigationToCustomProtocols,
10396 root->child_at(0)->active_sandbox_flags());
10397 EXPECT_EQ(
10398 network::mojom::WebSandboxFlags::kAll &
10399 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
10400 ~network::mojom::WebSandboxFlags::kPopups &
10401 ~network::mojom::WebSandboxFlags::kScripts &
10402 ~network::mojom::WebSandboxFlags::kTopNavigationToCustomProtocols,
10403 root->child_at(0)->pending_frame_policy().sandbox_flags);
10404 EXPECT_EQ(
10405 network::mojom::WebSandboxFlags::kAll &
10406 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
10407 ~network::mojom::WebSandboxFlags::kPopups &
10408 ~network::mojom::WebSandboxFlags::kScripts &
10409 ~network::mojom::WebSandboxFlags::kTopNavigationToCustomProtocols,
10410 root->child_at(0)->effective_frame_policy().sandbox_flags);
Ian Clelland5cbaaf82017-11-27 22:00:0310411}
10412
Nasko Oskov0f3cbb12020-01-07 17:52:1410413// Test that after an RFH is unloaded, its old sandbox flags remain active.
Fergal Daly2e7e1e12020-06-24 09:18:2810414IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Nasko Oskov0f3cbb12020-01-07 17:52:1410415 ActiveSandboxFlagsRetainedAfterUnload) {
Ian Clelland5cbaaf82017-11-27 22:00:0310416 GURL main_url(embedded_test_server()->GetURL(
10417 "a.com", "/sandboxed_main_frame_script.html"));
10418 EXPECT_TRUE(NavigateToURL(shell(), main_url));
10419
10420 // It is safe to obtain the root frame tree node here, as it doesn't change.
10421 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:5510422 ->GetPrimaryFrameTree()
10423 .root();
Ian Clelland5cbaaf82017-11-27 22:00:0310424
10425 RenderFrameHostImpl* rfh =
Dave Tapuska327c06c92022-06-13 20:31:5110426 static_cast<WebContentsImpl*>(shell()->web_contents())
10427 ->GetPrimaryMainFrame();
Ian Clelland5cbaaf82017-11-27 22:00:0310428
10429 // Check sandbox flags on RFH before navigating away.
Arthur Sonzogni4ba3104f2022-03-09 09:04:3910430 EXPECT_EQ(
10431 network::mojom::WebSandboxFlags::kAll &
10432 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
10433 ~network::mojom::WebSandboxFlags::kPointerLock &
10434 ~network::mojom::WebSandboxFlags::kPopups &
10435 ~network::mojom::WebSandboxFlags::kScripts &
10436 ~network::mojom::WebSandboxFlags::kTopNavigationToCustomProtocols,
10437 rfh->active_sandbox_flags());
Ian Clelland5cbaaf82017-11-27 22:00:0310438
Nasko Oskov0f3cbb12020-01-07 17:52:1410439 // Set up a slow unload handler to force the RFH to linger in the unloaded but
10440 // not-yet-deleted state.
Avi Drissmanc91bd8e2021-04-19 23:58:4410441 EXPECT_TRUE(ExecJs(rfh, "window.onunload=function(e){ while(1); };\n"));
Ian Clelland5cbaaf82017-11-27 22:00:0310442
Nasko Oskov0f3cbb12020-01-07 17:52:1410443 rfh->DisableUnloadTimerForTesting();
Ian Clelland5cbaaf82017-11-27 22:00:0310444 RenderFrameDeletedObserver rfh_observer(rfh);
10445
10446 // Navigate to a page with no sandbox, but wait for commit, not for the actual
10447 // load to finish.
10448 TestFrameNavigationObserver commit_observer(root);
10449 shell()->LoadURL(
10450 GURL(embedded_test_server()->GetURL("b.com", "/title1.html")));
10451 commit_observer.WaitForCommit();
10452
Sreeja Kamishettyce8d5942020-08-19 11:25:5110453 // The previous RFH should be either:
10454 // 1) In the BackForwardCache, or
Dave Tapuskaca250702021-01-07 17:40:5510455 // 2) Pending deletion, waiting for the
10456 // mojo::AgentSchedulingGroupHost::DidUnloadRenderFrame. As a result, it must
10457 // still be alive.
Ian Clelland5cbaaf82017-11-27 22:00:0310458 ASSERT_TRUE(rfh->IsRenderFrameLive());
Sreeja Kamishettyce8d5942020-08-19 11:25:5110459 EXPECT_THAT(
10460 rfh->lifecycle_state(),
10461 testing::AnyOf(
10462 testing::Eq(
Sreeja Kamishetty299329ad2021-03-25 14:06:0110463 RenderFrameHostImpl::LifecycleStateImpl::kRunningUnloadHandlers),
Sreeja Kamishettyce8d5942020-08-19 11:25:5110464 testing::Eq(
Sreeja Kamishetty299329ad2021-03-25 14:06:0110465 RenderFrameHostImpl::LifecycleStateImpl::kInBackForwardCache)));
Sreeja Kamishettyce8d5942020-08-19 11:25:5110466
Ian Clelland5cbaaf82017-11-27 22:00:0310467 ASSERT_FALSE(rfh_observer.deleted());
10468
10469 // Check sandbox flags on old RFH -- they should be unchanged.
Arthur Sonzogni4ba3104f2022-03-09 09:04:3910470 EXPECT_EQ(
10471 network::mojom::WebSandboxFlags::kAll &
10472 ~network::mojom::WebSandboxFlags::kAutomaticFeatures &
10473 ~network::mojom::WebSandboxFlags::kPointerLock &
10474 ~network::mojom::WebSandboxFlags::kPopups &
10475 ~network::mojom::WebSandboxFlags::kScripts &
10476 ~network::mojom::WebSandboxFlags::kTopNavigationToCustomProtocols,
10477 rfh->active_sandbox_flags());
Ian Clelland5cbaaf82017-11-27 22:00:0310478
10479 // The FrameTreeNode should have flags which represent the new state.
arthursonzognib93a4472020-04-10 07:38:0010480 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:0310481 root->effective_frame_policy().sandbox_flags);
10482}
10483
10484// Verify that when CSP-set sandbox flags on a page change due to navigation,
10485// the new flags are propagated to proxies in other SiteInstances.
10486//
10487// A A A A
10488// \ \ \ \ .
10489// B -> B* -> B* -> B*
10490// / \ / \ / \ .
10491// B B A B C B
10492//
10493// (B* has CSP-set sandbox flags)
10494// The test checks sandbox flags for the proxy added in step 2, by checking
10495// whether the grandchild frames navigated to in step 3 and 4 see the correct
10496// sandbox flags.
Fergal Daly2e7e1e12020-06-24 09:18:2810497IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Ian Clelland5cbaaf82017-11-27 22:00:0310498 ActiveSandboxFlagsCorrectInProxies) {
W. James MacLeanb0a37b7b2023-11-27 20:58:0410499 bool sandboxed_iframes_are_isolated =
10500 SiteIsolationPolicy::AreIsolatedSandboxedIframesEnabled();
Ian Clelland5cbaaf82017-11-27 22:00:0310501 GURL main_url(embedded_test_server()->GetURL(
10502 "foo.com", "/cross_site_iframe_factory.html?foo(bar)"));
10503 EXPECT_TRUE(NavigateToURL(shell(), main_url));
10504
10505 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:5510506 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Ian Clelland5cbaaf82017-11-27 22:00:0310507 TestNavigationObserver observer(shell()->web_contents());
10508
10509 EXPECT_EQ(
10510 " Site A ------------ proxies for B\n"
10511 " +--Site B ------- proxies for A\n"
10512 "Where A = http://foo.com/\n"
10513 " B = http://bar.com/",
10514 DepictFrameTree(root));
10515
10516 // Navigate the child to a CSP-sandboxed page on the same origin as it is
10517 // currently. This should update the flags in its proxies as well.
W. James MacLeanb0a37b7b2023-11-27 20:58:0410518 auto* child = root->child_at(0);
10519 RenderFrameDeletedObserver deleted_observer_child(
10520 child->current_frame_host());
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2010521 EXPECT_TRUE(NavigateToURLFromRenderer(
Ian Clelland5cbaaf82017-11-27 22:00:0310522 root->child_at(0),
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2010523 embedded_test_server()->GetURL("bar.com", "/csp_sandboxed_frame.html")));
W. James MacLeanb0a37b7b2023-11-27 20:58:0410524 // DepictFrameTree remembers all the sites it has seen in the test, so the
10525 // expected output changes depending on whether we have additional sites from
10526 // process-isolated sandboxed frames. How many additional sites we have
10527 // depends on the grouping mode.
10528 if (sandboxed_iframes_are_isolated) {
10529 // Sandboxed iframes force the RFH to change; wait for the old one to go
10530 // away so that proxies in its SiteInstance don't affect DepictFrameTree
10531 // output.
10532 deleted_observer_child.WaitUntilDeleted();
10533 switch (blink::features::kIsolateSandboxedIframesGroupingParam.Get()) {
10534 case blink::features::IsolateSandboxedIframesGrouping::kPerSite:
10535 case blink::features::IsolateSandboxedIframesGrouping::kPerOrigin:
10536 EXPECT_EQ(
10537 " Site A ------------ proxies for C\n"
10538 " +--Site C ------- proxies for A\n"
10539 " |--Site C -- proxies for A\n"
10540 " +--Site C -- proxies for A\n"
10541 "Where A = http://foo.com/\n"
10542 " C = http://bar.com/ (sandboxed)",
10543 DepictFrameTree(root));
10544 break;
10545 case blink::features::IsolateSandboxedIframesGrouping::kPerDocument:
Alison Gale770f3fc2024-04-27 00:39:5810546 // TODO(crbug.com/40941714): Add output for the PerDocument
W. James MacLeanb0a37b7b2023-11-27 20:58:0410547 // case, and parameterize this test to run all variants (none, per-site,
10548 // per-origin, per-document).
10549 break;
10550 }
10551 } else {
10552 EXPECT_EQ(
10553 " Site A ------------ proxies for B\n"
10554 " +--Site B ------- proxies for A\n"
10555 " |--Site B -- proxies for A\n"
10556 " +--Site B -- proxies for A\n"
10557 "Where A = http://foo.com/\n"
10558 " B = http://bar.com/",
10559 DepictFrameTree(root));
10560 }
Ian Clelland5cbaaf82017-11-27 22:00:0310561
10562 // Now navigate the first grandchild to a page on the same origin as the main
10563 // frame. It should still be sandboxed, as it should get its flags from its
10564 // (remote) parent.
Alison Gale770f3fc2024-04-27 00:39:5810565 // TODO(crbug.com/40943240): When IsolateSandboxedIframes is enabled,
W. James MacLeanb0a37b7b2023-11-27 20:58:0410566 // this test no longer uses proxy inheritance; the grandchild and the main
10567 // frame won't be in the same SiteInstance anymore, so this test will no
10568 // longer exercise sandbox flags inheritance from an existing remote frame.
10569 // Restructure the test so it still provides coverage for proxy inheritance
10570 // when IsolateSandboxedIframes is enabled.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2010571 EXPECT_TRUE(NavigateToURLFromRenderer(
10572 root->child_at(0)->child_at(0),
10573 embedded_test_server()->GetURL("foo.com", "/title1.html")));
Ian Clelland5cbaaf82017-11-27 22:00:0310574
W. James MacLeanb0a37b7b2023-11-27 20:58:0410575 if (sandboxed_iframes_are_isolated) {
10576 switch (blink::features::kIsolateSandboxedIframesGroupingParam.Get()) {
10577 case blink::features::IsolateSandboxedIframesGrouping::kPerSite:
10578 case blink::features::IsolateSandboxedIframesGrouping::kPerOrigin:
10579 EXPECT_EQ(
10580 " Site A ------------ proxies for C D\n"
10581 " +--Site C ------- proxies for A D\n"
10582 " |--Site D -- proxies for A C\n"
10583 " +--Site C -- proxies for A D\n"
10584 "Where A = http://foo.com/\n"
10585 " C = http://bar.com/ (sandboxed)\n"
10586 " D = http://foo.com/ (sandboxed)",
10587 DepictFrameTree(root));
10588 break;
10589 case blink::features::IsolateSandboxedIframesGrouping::kPerDocument:
Alison Gale770f3fc2024-04-27 00:39:5810590 // TODO(crbug.com/40941714): Add output for the PerDocument
W. James MacLeanb0a37b7b2023-11-27 20:58:0410591 // case, and parameterize this test to run all variants (none, per-site,
10592 // per-origin, per-document).
10593 break;
10594 }
10595 } else {
10596 EXPECT_EQ(
10597 " Site A ------------ proxies for B\n"
10598 " +--Site B ------- proxies for A\n"
10599 " |--Site A -- proxies for B\n"
10600 " +--Site B -- proxies for A\n"
10601 "Where A = http://foo.com/\n"
10602 " B = http://bar.com/",
10603 DepictFrameTree(root));
10604 }
Ian Clelland5cbaaf82017-11-27 22:00:0310605
10606 // The child of the sandboxed frame should've inherited sandbox flags, so it
10607 // should not be able to create popups.
10608 EXPECT_EQ(
arthursonzognib93a4472020-04-10 07:38:0010609 network::mojom::WebSandboxFlags::kAll &
10610 ~network::mojom::WebSandboxFlags::kScripts &
10611 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clelland5cbaaf82017-11-27 22:00:0310612 root->child_at(0)->child_at(0)->effective_frame_policy().sandbox_flags);
10613 EXPECT_EQ(
10614 root->child_at(0)->child_at(0)->active_sandbox_flags(),
10615 root->child_at(0)->child_at(0)->effective_frame_policy().sandbox_flags);
Avi Drissmanc91bd8e2021-04-19 23:58:4410616 EXPECT_EQ(true, EvalJs(root->child_at(0)->child_at(0),
10617 "!window.open('data:text/html,dataurl');"));
Ian Clelland5cbaaf82017-11-27 22:00:0310618 EXPECT_EQ(1u, Shell::windows().size());
10619
10620 // Finally, navigate the grandchild frame to a new origin, creating a new site
10621 // instance. Again, the new document should be sandboxed, as it should get its
10622 // flags from its (remote) parent in B.
W. James MacLeane183c4d2023-11-29 19:28:4910623 RenderFrameDeletedObserver deleted_observer_grandchild(
10624 root->child_at(0)->child_at(0)->current_frame_host());
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2010625 EXPECT_TRUE(NavigateToURLFromRenderer(
10626 root->child_at(0)->child_at(0),
10627 embedded_test_server()->GetURL("baz.com", "/title1.html")));
Ian Clelland5cbaaf82017-11-27 22:00:0310628
W. James MacLeanfe9cfae2023-12-01 06:26:4410629 deleted_observer_grandchild.WaitUntilDeleted();
W. James MacLeanb0a37b7b2023-11-27 20:58:0410630 if (sandboxed_iframes_are_isolated) {
10631 switch (blink::features::kIsolateSandboxedIframesGroupingParam.Get()) {
10632 case blink::features::IsolateSandboxedIframesGrouping::kPerSite:
10633 case blink::features::IsolateSandboxedIframesGrouping::kPerOrigin:
10634 EXPECT_EQ(
10635 " Site A ------------ proxies for C E\n"
10636 " +--Site C ------- proxies for A E\n"
10637 " |--Site E -- proxies for A C\n"
10638 " +--Site C -- proxies for A E\n"
10639 "Where A = http://foo.com/\n"
10640 " C = http://bar.com/ (sandboxed)\n"
10641 " E = http://baz.com/ (sandboxed)",
10642 DepictFrameTree(root));
10643 break;
10644 case blink::features::IsolateSandboxedIframesGrouping::kPerDocument:
Alison Gale770f3fc2024-04-27 00:39:5810645 // TODO(crbug.com/40941714): Add output for the PerDocument
W. James MacLeanb0a37b7b2023-11-27 20:58:0410646 // case, and parameterize this test to run all variants (none, per-site,
10647 // per-origin, per-document).
10648 break;
10649 }
10650 } else {
10651 EXPECT_EQ(
10652 " Site A ------------ proxies for B C\n"
10653 " +--Site B ------- proxies for A C\n"
10654 " |--Site C -- proxies for A B\n"
10655 " +--Site B -- proxies for A C\n"
10656 "Where A = http://foo.com/\n"
10657 " B = http://bar.com/\n"
10658 " C = http://baz.com/",
10659 DepictFrameTree(root));
10660 }
Ian Clelland5cbaaf82017-11-27 22:00:0310661
10662 // The child of the sandboxed frame should've inherited sandbox flags, so it
10663 // should not be able to create popups.
10664 EXPECT_EQ(
arthursonzognib93a4472020-04-10 07:38:0010665 network::mojom::WebSandboxFlags::kAll &
10666 ~network::mojom::WebSandboxFlags::kScripts &
10667 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clelland5cbaaf82017-11-27 22:00:0310668 root->child_at(0)->child_at(0)->effective_frame_policy().sandbox_flags);
10669 EXPECT_EQ(
10670 root->child_at(0)->child_at(0)->active_sandbox_flags(),
10671 root->child_at(0)->child_at(0)->effective_frame_policy().sandbox_flags);
Avi Drissmanc91bd8e2021-04-19 23:58:4410672 EXPECT_EQ(true, EvalJs(root->child_at(0)->child_at(0),
10673 "!window.open('data:text/html,dataurl');"));
Ian Clelland5cbaaf82017-11-27 22:00:0310674 EXPECT_EQ(1u, Shell::windows().size());
10675}
10676
10677// Verify that when the sandbox iframe attribute changes on a page which also
10678// has CSP-set sandbox flags, that the correct combination of flags is set in
10679// the sandboxed page after navigation.
10680//
10681// A A A A
10682// \ \ \ \ .
10683// B -> B* -> B* -> (change sandbox attr) -> B*
10684// / \ / \ / \ .
10685// B B A B A' B
10686//
10687// (B* has CSP-set sandbox flags)
Fergal Daly2e7e1e12020-06-24 09:18:2810688IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Ian Clelland5cbaaf82017-11-27 22:00:0310689 ActiveSandboxFlagsCorrectAfterUpdate) {
10690 GURL main_url(embedded_test_server()->GetURL(
10691 "foo.com", "/cross_site_iframe_factory.html?foo(bar)"));
10692 EXPECT_TRUE(NavigateToURL(shell(), main_url));
10693
10694 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:5510695 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Ian Clelland5cbaaf82017-11-27 22:00:0310696 TestNavigationObserver observer(shell()->web_contents());
10697
10698 // Navigate the child to a CSP-sandboxed page on the same origin as it is
10699 // currently. This should update the flags in its proxies as well.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2010700 EXPECT_TRUE(NavigateToURLFromRenderer(
Ian Clelland5cbaaf82017-11-27 22:00:0310701 root->child_at(0),
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2010702 embedded_test_server()->GetURL("bar.com", "/csp_sandboxed_frame.html")));
Ian Clelland5cbaaf82017-11-27 22:00:0310703
10704 // Now navigate the first grandchild to a page on the same origin as the main
10705 // frame. It should still be sandboxed, as it should get its flags from its
10706 // (remote) parent.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2010707 EXPECT_TRUE(NavigateToURLFromRenderer(
10708 root->child_at(0)->child_at(0),
10709 embedded_test_server()->GetURL("foo.com", "/title1.html")));
Ian Clelland5cbaaf82017-11-27 22:00:0310710
10711 // The child of the sandboxed frame should've inherited sandbox flags, so it
10712 // should not be able to create popups.
10713 EXPECT_EQ(
arthursonzognib93a4472020-04-10 07:38:0010714 network::mojom::WebSandboxFlags::kAll &
10715 ~network::mojom::WebSandboxFlags::kScripts &
10716 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clelland5cbaaf82017-11-27 22:00:0310717 root->child_at(0)->child_at(0)->effective_frame_policy().sandbox_flags);
10718 EXPECT_EQ(
10719 root->child_at(0)->child_at(0)->active_sandbox_flags(),
10720 root->child_at(0)->child_at(0)->effective_frame_policy().sandbox_flags);
Avi Drissmanc91bd8e2021-04-19 23:58:4410721 EXPECT_EQ(true, EvalJs(root->child_at(0)->child_at(0),
10722 "!window.open('data:text/html,dataurl');"));
Ian Clelland5cbaaf82017-11-27 22:00:0310723 EXPECT_EQ(1u, Shell::windows().size());
10724
10725 // Update the sandbox attribute in the child frame. This should be overridden
10726 // by the CSP-set sandbox on this frame: The grandchild should *not* receive
10727 // an allowance for popups after it is navigated.
Avi Drissmanc91bd8e2021-04-19 23:58:4410728 EXPECT_TRUE(ExecJs(root->child_at(0),
10729 "document.querySelector('iframe').sandbox = "
10730 " 'allow-scripts allow-popups';"));
Ian Clelland5cbaaf82017-11-27 22:00:0310731 // Finally, navigate the grandchild frame to another page on the top-level
10732 // origin; the active sandbox flags should still come from the it's parent's
10733 // CSP and the frame owner attributes.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2010734 EXPECT_TRUE(NavigateToURLFromRenderer(
10735 root->child_at(0)->child_at(0),
10736 embedded_test_server()->GetURL("foo.com", "/title2.html")));
Ian Clelland5cbaaf82017-11-27 22:00:0310737 EXPECT_EQ(
arthursonzognib93a4472020-04-10 07:38:0010738 network::mojom::WebSandboxFlags::kAll &
10739 ~network::mojom::WebSandboxFlags::kScripts &
10740 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clelland5cbaaf82017-11-27 22:00:0310741 root->child_at(0)->child_at(0)->effective_frame_policy().sandbox_flags);
10742 EXPECT_EQ(
10743 root->child_at(0)->child_at(0)->active_sandbox_flags(),
10744 root->child_at(0)->child_at(0)->effective_frame_policy().sandbox_flags);
Avi Drissmanc91bd8e2021-04-19 23:58:4410745 EXPECT_EQ(true, EvalJs(root->child_at(0)->child_at(0),
10746 "!window.open('data:text/html,dataurl');"));
Ian Clelland5cbaaf82017-11-27 22:00:0310747 EXPECT_EQ(1u, Shell::windows().size());
10748}
10749
10750// Verify that when the sandbox iframe attribute is removed from a page which
10751// also has CSP-set sandbox flags, that the flags are cleared in the browser
10752// and renderers (including proxies) after navigation to a page without CSP-set
10753// flags.
Fergal Daly2e7e1e12020-06-24 09:18:2810754IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Ian Clelland5cbaaf82017-11-27 22:00:0310755 ActiveSandboxFlagsCorrectWhenCleared) {
10756 GURL main_url(
10757 embedded_test_server()->GetURL("foo.com", "/sandboxed_frames_csp.html"));
10758 EXPECT_TRUE(NavigateToURL(shell(), main_url));
10759
10760 // It is safe to obtain the root frame tree node here, as it doesn't change.
Carlos Caballero15caeeb2021-10-27 09:57:5510761 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Ian Clelland5cbaaf82017-11-27 22:00:0310762 TestNavigationObserver observer(shell()->web_contents());
10763
10764 // The second child has both iframe-attribute sandbox flags and CSP-set flags.
10765 // Verify that it the flags are combined correctly in the frame tree.
arthursonzognib93a4472020-04-10 07:38:0010766 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
10767 ~network::mojom::WebSandboxFlags::kPointerLock &
10768 ~network::mojom::WebSandboxFlags::kOrientationLock &
10769 ~network::mojom::WebSandboxFlags::kScripts &
10770 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clelland5cbaaf82017-11-27 22:00:0310771 root->child_at(1)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:0010772 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
10773 ~network::mojom::WebSandboxFlags::kPointerLock &
10774 ~network::mojom::WebSandboxFlags::kScripts &
10775 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clelland5cbaaf82017-11-27 22:00:0310776 root->child_at(1)->active_sandbox_flags());
10777
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2010778 EXPECT_TRUE(NavigateToURLFromRenderer(
10779 root->child_at(1), embedded_test_server()->GetURL(
10780 "bar.com", "/sandboxed_child_frame.html")));
arthursonzognib93a4472020-04-10 07:38:0010781 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
10782 ~network::mojom::WebSandboxFlags::kPointerLock &
10783 ~network::mojom::WebSandboxFlags::kOrientationLock &
10784 ~network::mojom::WebSandboxFlags::kScripts &
10785 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clelland5cbaaf82017-11-27 22:00:0310786 root->child_at(1)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:0010787 EXPECT_EQ(network::mojom::WebSandboxFlags::kAll &
10788 ~network::mojom::WebSandboxFlags::kPointerLock &
10789 ~network::mojom::WebSandboxFlags::kScripts &
10790 ~network::mojom::WebSandboxFlags::kAutomaticFeatures,
Ian Clelland5cbaaf82017-11-27 22:00:0310791 root->child_at(1)->active_sandbox_flags());
10792
10793 // Remove the sandbox attribute from the child frame.
Avi Drissmanc91bd8e2021-04-19 23:58:4410794 EXPECT_TRUE(ExecJs(root,
10795 "document.querySelectorAll('iframe')[1]"
10796 ".removeAttribute('sandbox');"));
Ian Clelland5cbaaf82017-11-27 22:00:0310797 // Finally, navigate that child frame to another page on the same origin with
10798 // no CSP-set sandbox. Its sandbox flags should be completely cleared, and
10799 // should be cleared in the proxy in the main frame's renderer as well.
10800 // We can check that the flags were properly cleared by nesting another frame
10801 // under the child, and ensuring that *it* saw no sandbox flags in the
10802 // browser, or in the RemoteSecurityContext in the main frame's renderer.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2010803 EXPECT_TRUE(NavigateToURLFromRenderer(
Ian Clelland5cbaaf82017-11-27 22:00:0310804 root->child_at(1),
10805 embedded_test_server()->GetURL(
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2010806 "bar.com", "/cross_site_iframe_factory.html?bar(foo)")));
Ian Clelland5cbaaf82017-11-27 22:00:0310807
10808 // Check the sandbox flags on the child frame in the browser process.
arthursonzognib93a4472020-04-10 07:38:0010809 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:0310810 root->child_at(1)->effective_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:0010811 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:0310812 root->child_at(1)->active_sandbox_flags());
10813
10814 // Check the sandbox flags on the grandchid frame in the browser process.
10815 EXPECT_EQ(
arthursonzognib93a4472020-04-10 07:38:0010816 network::mojom::WebSandboxFlags::kNone,
Ian Clelland5cbaaf82017-11-27 22:00:0310817 root->child_at(1)->child_at(0)->effective_frame_policy().sandbox_flags);
10818 EXPECT_EQ(
10819 root->child_at(1)->child_at(0)->active_sandbox_flags(),
10820 root->child_at(1)->child_at(0)->effective_frame_policy().sandbox_flags);
10821
10822 // Check the sandbox flags in the grandchild frame's renderer by attempting
10823 // to open a popup. This should succeed.
Avi Drissmanc91bd8e2021-04-19 23:58:4410824 EXPECT_EQ(true, EvalJs(root->child_at(1)->child_at(0),
10825 "!!window.open('data:text/html,dataurl');"));
Ian Clelland5cbaaf82017-11-27 22:00:0310826 EXPECT_EQ(2u, Shell::windows().size());
10827}
10828
Alex Moshchuk13587e752017-12-07 20:39:1010829// Check that a subframe that requires a dedicated process will attempt to
10830// reuse an existing process for the same site, even across BrowsingInstances.
10831// This helps consolidate processes when running under --site-per-process.
Fergal Daly2e7e1e12020-06-24 09:18:2810832IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Alex Moshchuk13587e752017-12-07 20:39:1010833 SubframeReusesExistingProcess) {
10834 GURL foo_url(
10835 embedded_test_server()->GetURL("foo.com", "/page_with_iframe.html"));
10836 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
Carlos Caballero15caeeb2021-10-27 09:57:5510837 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchuk13587e752017-12-07 20:39:1010838 FrameTreeNode* child = root->child_at(0);
10839
10840 // Open an unrelated tab in a separate BrowsingInstance, and navigate it to
10841 // to bar.com. This SiteInstance should have a default process reuse
10842 // policy - only subframes attempt process reuse.
10843 GURL bar_url(
10844 embedded_test_server()->GetURL("bar.com", "/page_with_iframe.html"));
10845 Shell* second_shell = CreateBrowser();
10846 EXPECT_TRUE(NavigateToURL(second_shell, bar_url));
10847 scoped_refptr<SiteInstanceImpl> second_shell_instance =
Dave Tapuska327c06c92022-06-13 20:31:5110848 static_cast<SiteInstanceImpl*>(second_shell->web_contents()
10849 ->GetPrimaryMainFrame()
10850 ->GetSiteInstance());
Alex Moshchuk13587e752017-12-07 20:39:1010851 EXPECT_FALSE(second_shell_instance->IsRelatedSiteInstance(
10852 root->current_frame_host()->GetSiteInstance()));
10853 RenderProcessHost* bar_process = second_shell_instance->GetProcess();
Charlie Reis9ce0ed222024-09-05 22:05:2610854 EXPECT_EQ(ProcessReusePolicy::DEFAULT,
Alex Moshchuk13587e752017-12-07 20:39:1010855 second_shell_instance->process_reuse_policy());
10856
10857 // Now navigate the first tab's subframe to bar.com. Confirm that it reuses
10858 // |bar_process|.
10859 NavigateIframeToURL(web_contents(), "test_iframe", bar_url);
10860 EXPECT_EQ(bar_url, child->current_url());
10861 EXPECT_EQ(bar_process, child->current_frame_host()->GetProcess());
10862 EXPECT_EQ(
Charlie Reis9ce0ed222024-09-05 22:05:2610863 ProcessReusePolicy::REUSE_PENDING_OR_COMMITTED_SITE_SUBFRAME,
Alex Moshchuk13587e752017-12-07 20:39:1010864 child->current_frame_host()->GetSiteInstance()->process_reuse_policy());
10865
10866 EXPECT_TRUE(child->current_frame_host()->IsCrossProcessSubframe());
10867 EXPECT_EQ(
10868 bar_url.host(),
10869 child->current_frame_host()->GetSiteInstance()->GetSiteURL().host());
10870
10871 // The subframe's SiteInstance should still be different from second_shell's
10872 // SiteInstance, and they should be in separate BrowsingInstances.
10873 EXPECT_NE(second_shell_instance,
10874 child->current_frame_host()->GetSiteInstance());
10875 EXPECT_FALSE(second_shell_instance->IsRelatedSiteInstance(
10876 child->current_frame_host()->GetSiteInstance()));
10877
10878 // Navigate the second tab to a foo.com URL with a same-site subframe. This
10879 // leaves only the first tab's subframe in the bar.com process.
10880 EXPECT_TRUE(NavigateToURL(second_shell, foo_url));
10881 EXPECT_NE(bar_process,
Dave Tapuska327c06c92022-06-13 20:31:5110882 second_shell->web_contents()->GetPrimaryMainFrame()->GetProcess());
Alex Moshchuk13587e752017-12-07 20:39:1010883
10884 // Navigate the second tab's subframe to bar.com, and check that this
10885 // new subframe reuses the process of the subframe in the first tab, even
10886 // though the two are in separate BrowsingInstances.
10887 NavigateIframeToURL(second_shell->web_contents(), "test_iframe", bar_url);
10888 FrameTreeNode* second_subframe =
10889 static_cast<WebContentsImpl*>(second_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:5510890 ->GetPrimaryFrameTree()
10891 .root()
Alex Moshchuk13587e752017-12-07 20:39:1010892 ->child_at(0);
10893 EXPECT_EQ(bar_process, second_subframe->current_frame_host()->GetProcess());
10894 EXPECT_NE(child->current_frame_host()->GetSiteInstance(),
10895 second_subframe->current_frame_host()->GetSiteInstance());
10896
10897 // Open a third, unrelated tab, navigate it to bar.com, and check that
10898 // its main frame doesn't share a process with the existing bar.com
10899 // subframes.
10900 Shell* third_shell = CreateBrowser();
10901 EXPECT_TRUE(NavigateToURL(third_shell, bar_url));
10902 SiteInstanceImpl* third_shell_instance = static_cast<SiteInstanceImpl*>(
Dave Tapuska327c06c92022-06-13 20:31:5110903 third_shell->web_contents()->GetPrimaryMainFrame()->GetSiteInstance());
Alex Moshchuk13587e752017-12-07 20:39:1010904 EXPECT_NE(third_shell_instance,
10905 second_subframe->current_frame_host()->GetSiteInstance());
10906 EXPECT_NE(third_shell_instance,
10907 child->current_frame_host()->GetSiteInstance());
10908 EXPECT_NE(third_shell_instance->GetProcess(), bar_process);
10909}
10910
Dave Tapuskabca0c282024-09-20 21:29:3510911class SitePerProcessNoSharingBrowserTest : public SitePerProcessBrowserTest {
10912 public:
10913 SitePerProcessNoSharingBrowserTest() {
10914 scoped_feature_list_.InitAndDisableFeature(
10915 features::kProcessPerSiteUpToMainFrameThreshold);
10916 }
10917
10918 private:
10919 base::test::ScopedFeatureList scoped_feature_list_;
10920};
10921
Alex Moshchuk7e26eca2018-03-03 01:34:2910922// Check that when a subframe reuses an existing process for the same site
10923// across BrowsingInstances, a browser-initiated navigation in that subframe's
10924// tab doesn't unnecessarily share the reused process. See
10925// https://crbug.com/803367.
Dave Tapuskabca0c282024-09-20 21:29:3510926IN_PROC_BROWSER_TEST_P(SitePerProcessNoSharingBrowserTest,
Alex Moshchuk7e26eca2018-03-03 01:34:2910927 NoProcessSharingAfterSubframeReusesExistingProcess) {
10928 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
10929 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
Carlos Caballero15caeeb2021-10-27 09:57:5510930 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchuk7e26eca2018-03-03 01:34:2910931 SiteInstanceImpl* foo_instance =
10932 root->current_frame_host()->GetSiteInstance();
10933
10934 // Open an unrelated tab in a separate BrowsingInstance, and navigate it to
10935 // to bar.com.
10936 GURL bar_url(
10937 embedded_test_server()->GetURL("bar.com", "/page_with_iframe.html"));
10938 Shell* second_shell = CreateBrowser();
10939 EXPECT_TRUE(NavigateToURL(second_shell, bar_url));
10940 FrameTreeNode* second_root =
10941 static_cast<WebContentsImpl*>(second_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:5510942 ->GetPrimaryFrameTree()
10943 .root();
Alex Moshchuk7e26eca2018-03-03 01:34:2910944 FrameTreeNode* second_child = second_root->child_at(0);
10945 scoped_refptr<SiteInstanceImpl> bar_instance =
10946 second_root->current_frame_host()->GetSiteInstance();
10947 EXPECT_FALSE(bar_instance->IsRelatedSiteInstance(foo_instance));
10948
10949 // Navigate the second tab's subframe to foo.com. Confirm that it reuses
10950 // first tab's process.
10951 NavigateIframeToURL(second_shell->web_contents(), "test_iframe", foo_url);
10952 EXPECT_EQ(foo_url, second_child->current_url());
10953 scoped_refptr<SiteInstanceImpl> second_child_foo_instance =
10954 second_child->current_frame_host()->GetSiteInstance();
Charlie Reis9ce0ed222024-09-05 22:05:2610955 EXPECT_EQ(ProcessReusePolicy::REUSE_PENDING_OR_COMMITTED_SITE_SUBFRAME,
W. James MacLeanb2e5e992024-08-27 16:51:3010956 second_child_foo_instance->process_reuse_policy());
Alex Moshchuk7e26eca2018-03-03 01:34:2910957 EXPECT_NE(foo_instance, second_child_foo_instance);
10958 EXPECT_EQ(foo_instance->GetProcess(),
10959 second_child_foo_instance->GetProcess());
10960
10961 // Perform a browser-initiated address bar navigation in the second tab to
10962 // foo.com. This should swap BrowsingInstances and end up in a separate
10963 // process from the first tab.
10964 EXPECT_TRUE(NavigateToURL(second_shell, foo_url));
10965 SiteInstanceImpl* new_instance =
10966 second_root->current_frame_host()->GetSiteInstance();
10967 EXPECT_NE(second_child_foo_instance, new_instance);
10968 EXPECT_FALSE(second_child_foo_instance->IsRelatedSiteInstance(new_instance));
10969 EXPECT_FALSE(bar_instance->IsRelatedSiteInstance(new_instance));
10970 EXPECT_FALSE(foo_instance->IsRelatedSiteInstance(new_instance));
10971 EXPECT_NE(new_instance->GetProcess(), foo_instance->GetProcess());
Jiacheng Guocd621762025-06-17 01:14:3210972 EXPECT_NE(new_instance->GetProcess(),
10973 bar_instance->GetOrCreateProcessForTesting());
Alex Moshchuk7e26eca2018-03-03 01:34:2910974}
10975
Balazs Engedy1cb5c0e2018-01-30 14:34:2010976namespace {
10977
10978// Intercepts the next DidCommitProvisionalLoad message for |deferred_url| in
10979// any frame of the |web_contents|, and holds off on dispatching it until
10980// *after* the DidCommitProvisionalLoad message for the next navigation in the
10981// |web_contents| has been dispatched.
10982//
10983// Reversing the order in which the commit messages are dispatched simulates a
10984// busy renderer that takes a very long time to actually commit the navigation
10985// to |deferred_url| after receiving FrameNavigationControl::CommitNavigation;
10986// whereas there is a fast cross-site navigation taking place in the same
10987// frame which starts second but finishes first.
Arthur Hemery2a0a28be2019-03-06 17:51:3610988class CommitMessageOrderReverser : public DidCommitNavigationInterceptor {
Balazs Engedy1cb5c0e2018-01-30 14:34:2010989 public:
10990 using DidStartDeferringCommitCallback =
10991 base::OnceCallback<void(RenderFrameHost*)>;
10992
10993 CommitMessageOrderReverser(
10994 WebContents* web_contents,
10995 const GURL& deferred_url,
10996 DidStartDeferringCommitCallback deferred_url_triggered_action)
Arthur Hemery2a0a28be2019-03-06 17:51:3610997 : DidCommitNavigationInterceptor(web_contents),
Balazs Engedy1cb5c0e2018-01-30 14:34:2010998 deferred_url_(deferred_url),
10999 deferred_url_triggered_action_(
11000 std::move(deferred_url_triggered_action)) {}
Peter Boström828b9022021-09-21 02:28:4311001
11002 CommitMessageOrderReverser(const CommitMessageOrderReverser&) = delete;
11003 CommitMessageOrderReverser& operator=(const CommitMessageOrderReverser&) =
11004 delete;
11005
Balazs Engedy1cb5c0e2018-01-30 14:34:2011006 ~CommitMessageOrderReverser() override = default;
11007
11008 void WaitForBothCommits() { outer_run_loop.Run(); }
11009
11010 protected:
Arthur Hemery2a0a28be2019-03-06 17:51:3611011 bool WillProcessDidCommitNavigation(
Balazs Engedy1cb5c0e2018-01-30 14:34:2011012 RenderFrameHost* render_frame_host,
Arthur Hemery2a0a28be2019-03-06 17:51:3611013 NavigationRequest* navigation_request,
arthursonzogni73fe3212020-11-17 13:24:0711014 mojom::DidCommitProvisionalLoadParamsPtr* params,
Lukasz Anforowicz3cfc1efd2019-02-22 02:29:2911015 mojom::DidCommitProvisionalLoadInterfaceParamsPtr* interface_params)
Oksana Zhuravlova8b88e572019-01-07 21:54:0011016 override {
Balazs Engedy1cb5c0e2018-01-30 14:34:2011017 // The DidCommitProvisionalLoad message is dispatched once this method
11018 // returns, so to defer committing the the navigation to |deferred_url_|,
11019 // run a nested message loop until the subsequent other commit message is
11020 // dispatched.
arthursonzogni73fe3212020-11-17 13:24:0711021 if ((**params).url == deferred_url_) {
Balazs Engedy1cb5c0e2018-01-30 14:34:2011022 std::move(deferred_url_triggered_action_).Run(render_frame_host);
11023
Gabriel Charettead1a7832018-04-17 14:33:0211024 base::RunLoop nested_run_loop(base::RunLoop::Type::kNestableTasksAllowed);
Balazs Engedy1cb5c0e2018-01-30 14:34:2011025 nested_loop_quit_ = nested_run_loop.QuitClosure();
11026 nested_run_loop.Run();
11027 outer_run_loop.Quit();
11028 } else if (nested_loop_quit_) {
11029 std::move(nested_loop_quit_).Run();
11030 }
clamyd3bfdb02018-07-12 13:52:1811031 return true;
Balazs Engedy1cb5c0e2018-01-30 14:34:2011032 }
11033
11034 private:
11035 base::RunLoop outer_run_loop;
11036 base::OnceClosure nested_loop_quit_;
11037
11038 const GURL deferred_url_;
11039 DidStartDeferringCommitCallback deferred_url_triggered_action_;
Balazs Engedy1cb5c0e2018-01-30 14:34:2011040};
11041
11042} // namespace
11043
Ken Buchananed449bb2018-02-01 21:02:0511044// Create an out-of-process iframe that causes itself to be detached during
11045// its layout/animate phase. See https://crbug.com/802932.
Mike Westa85060d2019-11-13 11:41:5711046//
Alison Gale53c77f62024-04-22 15:16:2711047// TODO(crbug.com/40561636): Disabled on Android, Mac, and ChromeOS due to
11048// flakiness.
Georg Neis35ff854b2024-12-17 02:02:0811049#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS)
Ken Buchanan6004ffda2018-02-06 20:31:5811050#define MAYBE_OOPIFDetachDuringAnimation DISABLED_OOPIFDetachDuringAnimation
11051#else
11052#define MAYBE_OOPIFDetachDuringAnimation OOPIFDetachDuringAnimation
11053#endif
Fergal Daly2e7e1e12020-06-24 09:18:2811054IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Ken Buchanan6004ffda2018-02-06 20:31:5811055 MAYBE_OOPIFDetachDuringAnimation) {
Ken Buchananed449bb2018-02-01 21:02:0511056 GURL main_url(embedded_test_server()->GetURL(
11057 "a.com", "/frame_tree/frame-detached-in-animationstart-event.html"));
11058 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:5511059 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Ken Buchananed449bb2018-02-01 21:02:0511060
11061 EXPECT_EQ(
11062 " Site A ------------ proxies for B\n"
11063 " +--Site B ------- proxies for A\n"
11064 " +--Site A -- proxies for B\n"
11065 "Where A = http://a.com/\n"
11066 " B = http://b.com/",
11067 DepictFrameTree(root));
11068
11069 FrameTreeNode* nested_child = root->child_at(0)->child_at(0);
kylechara7c549b2019-07-29 17:47:2811070 WaitForHitTestData(nested_child->current_frame_host());
Ken Buchananed449bb2018-02-01 21:02:0511071
Avi Drissmanc91bd8e2021-04-19 23:58:4411072 EXPECT_TRUE(ExecJs(nested_child->current_frame_host(), "startTest();"));
Ken Buchananed449bb2018-02-01 21:02:0511073
11074 // Test passes if the main renderer doesn't crash. Ping to verify.
Avi Drissmanc91bd8e2021-04-19 23:58:4411075 EXPECT_EQ(true, EvalJs(root->current_frame_host(), "true;"));
Ken Buchananed449bb2018-02-01 21:02:0511076}
11077
clamyaf4bf2d92018-02-06 10:54:3611078// Tests that a cross-process iframe asked to navigate to the same URL will
11079// successfully commit the navigation.
Fergal Daly2e7e1e12020-06-24 09:18:2811080IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
clamyaf4bf2d92018-02-06 10:54:3611081 IFrameSameDocumentNavigation) {
11082 GURL main_url(embedded_test_server()->GetURL(
11083 "foo.com", "/cross_site_iframe_factory.html?foo(bar)"));
11084 EXPECT_TRUE(NavigateToURL(shell(), main_url));
11085
Carlos Caballero15caeeb2021-10-27 09:57:5511086 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
clamyaf4bf2d92018-02-06 10:54:3611087 FrameTreeNode* iframe = root->child_at(0);
11088
11089 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
11090 iframe->current_frame_host()->GetSiteInstance());
11091
11092 // The iframe navigates same-document to a fragment.
11093 GURL iframe_fragment_url = GURL(iframe->current_url().spec() + "#foo");
11094 {
11095 TestNavigationObserver observer(shell()->web_contents());
Avi Drissmanc91bd8e2021-04-19 23:58:4411096 EXPECT_TRUE(ExecJs(iframe->current_frame_host(),
11097 JsReplace("location.href=$1", iframe_fragment_url)));
clamyaf4bf2d92018-02-06 10:54:3611098 observer.Wait();
11099 EXPECT_TRUE(observer.last_navigation_succeeded());
11100 EXPECT_EQ(iframe_fragment_url, iframe->current_url());
11101 }
11102
11103 // The parent frame wants the iframe do a navigation to the same URL. Because
11104 // the URL has a fragment, this will be treated as a same-document navigation,
11105 // and not as a normal load of the same URL. This should succeed.
11106 {
11107 TestNavigationObserver observer(shell()->web_contents());
Avi Drissmanc91bd8e2021-04-19 23:58:4411108 EXPECT_TRUE(ExecJs(root->current_frame_host(),
11109 JsReplace("document.getElementById('child-0').src=$1",
11110 iframe_fragment_url)));
clamyaf4bf2d92018-02-06 10:54:3611111 observer.Wait();
11112 EXPECT_TRUE(observer.last_navigation_succeeded());
11113 EXPECT_EQ(iframe_fragment_url, iframe->current_url());
11114 }
11115}
11116
Lucas Furukawa Gadanie5d27a362018-02-13 14:26:0211117// Verifies the the renderer has the size of the frame after commit.
11118// https://crbug/804046, https://crbug.com/801091
Fergal Daly2e7e1e12020-06-24 09:18:2811119IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, SizeAvailableAfterCommit) {
Lucas Furukawa Gadanie5d27a362018-02-13 14:26:0211120 GURL main_url(embedded_test_server()->GetURL(
11121 "a.com", "/cross_site_iframe_factory.html?a(a)"));
11122 EXPECT_TRUE(NavigateToURL(shell(), main_url));
11123
Carlos Caballero15caeeb2021-10-27 09:57:5511124 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Lucas Furukawa Gadanie5d27a362018-02-13 14:26:0211125 FrameTreeNode* child = root->child_at(0);
11126
11127 GURL b_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
11128 TestFrameNavigationObserver commit_observer(child);
11129 NavigationController::LoadURLParams params(b_url);
11130 params.transition_type = PageTransitionFromInt(ui::PAGE_TRANSITION_LINK);
11131 params.frame_tree_node_id = child->frame_tree_node_id();
Carlos Caballero04aab362021-02-15 17:38:1611132 child->navigator().controller().LoadURLWithParams(params);
Lucas Furukawa Gadanie5d27a362018-02-13 14:26:0211133 commit_observer.WaitForCommit();
11134
Avi Drissmanc91bd8e2021-04-19 23:58:4411135 EXPECT_GT(EvalJs(child, "window.innerHeight;").ExtractDouble(), 0);
Lucas Furukawa Gadanie5d27a362018-02-13 14:26:0211136}
Nasko Oskov0f3cbb12020-01-07 17:52:1411137
Dave Tapuskaca250702021-01-07 17:40:5511138// Test that a late mojo::AgentSchedulingGroupHost::DidUnloadRenderFrame won't
11139// incorrectly mark RenderViewHost as inactive if it's already been reused and
11140// switched to active by another navigation. See https://crbug.com/823567.
Fergal Daly2e7e1e12020-06-24 09:18:2811141IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Nasko Oskov0f3cbb12020-01-07 17:52:1411142 RenderViewHostStaysActiveWithLateUnloadACK) {
Alex Moshchuk45625822018-03-21 19:28:2711143 EXPECT_TRUE(NavigateToURL(
11144 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
11145
11146 // Open a popup and navigate it to a.com.
11147 Shell* popup = OpenPopup(
11148 shell(), embedded_test_server()->GetURL("a.com", "/title2.html"), "foo");
11149 WebContentsImpl* popup_contents =
11150 static_cast<WebContentsImpl*>(popup->web_contents());
Dave Tapuska327c06c92022-06-13 20:31:5111151 RenderFrameHostImpl* rfh = popup_contents->GetPrimaryMainFrame();
Alex Moshchuk45625822018-03-21 19:28:2711152 RenderViewHostImpl* rvh = rfh->render_view_host();
11153
Nasko Oskov0f3cbb12020-01-07 17:52:1411154 // Disable the unload ACK and the unload timer.
Dave Tapuskaca250702021-01-07 17:40:5511155 auto unload_ack_filter = base::BindRepeating([] { return true; });
11156 rfh->SetUnloadACKCallbackForTesting(unload_ack_filter);
Nasko Oskov0f3cbb12020-01-07 17:52:1411157 rfh->DisableUnloadTimerForTesting();
Alex Moshchuk45625822018-03-21 19:28:2711158
11159 // Navigate popup to b.com. Because there's an opener, the RVH for a.com
11160 // stays around in swapped-out state.
11161 EXPECT_TRUE(NavigateToURLInSameBrowsingInstance(
11162 popup, embedded_test_server()->GetURL("b.com", "/title3.html")));
11163 EXPECT_FALSE(rvh->is_active());
11164
11165 // The old RenderFrameHost is now pending deletion.
11166 ASSERT_TRUE(rfh->IsRenderFrameLive());
Sreeja Kamishettye25ac752020-05-12 18:15:4811167 ASSERT_TRUE(rfh->IsPendingDeletion());
Alex Moshchuk45625822018-03-21 19:28:2711168
11169 // Kill the b.com process.
Dave Tapuska327c06c92022-06-13 20:31:5111170 RenderProcessHost* b_process =
11171 popup_contents->GetPrimaryMainFrame()->GetProcess();
Alex Moshchuk45625822018-03-21 19:28:2711172 RenderProcessHostWatcher crash_observer(
11173 b_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
11174 b_process->Shutdown(0);
11175 crash_observer.Wait();
11176
11177 // Go back in the popup from b.com to a.com/title2.html. Because the current
11178 // b.com RFH is dead, the new RFH is committed right away (without waiting
11179 // for renderer to commit), so that users don't need to look at the sad tab.
11180 TestNavigationObserver back_observer(popup_contents);
11181 popup_contents->GetController().GoBack();
11182
11183 // Pretend that the original RFH in a.com now finishes running its unload
Dave Tapuskaca250702021-01-07 17:40:5511184 // handler and sends the mojo::AgentSchedulingGroupHost::DidUnloadRenderFrame.
Nasko Oskov0f3cbb12020-01-07 17:52:1411185 rfh->OnUnloaded();
Alex Moshchuk45625822018-03-21 19:28:2711186
11187 // Wait for the new a.com navigation to finish.
11188 back_observer.Wait();
11189
11190 // The RVH for a.com should've been reused, and it should be active. Its
11191 // main frame should've been updated to the RFH from the back navigation.
Dave Tapuska327c06c92022-06-13 20:31:5111192 EXPECT_EQ(popup_contents->GetPrimaryMainFrame()->render_view_host(), rvh);
Alex Moshchuk45625822018-03-21 19:28:2711193 EXPECT_TRUE(rvh->is_active());
Dave Tapuska327c06c92022-06-13 20:31:5111194 EXPECT_EQ(rvh->GetMainRenderFrameHost(),
11195 popup_contents->GetPrimaryMainFrame());
Alex Moshchuk45625822018-03-21 19:28:2711196}
Lucas Furukawa Gadanie5d27a362018-02-13 14:26:0211197
Alex Moshchukb13f850c2018-03-20 17:38:0911198// Check that when A opens a new window with B which embeds an A subframe, the
11199// subframe is visible and generates paint events. See
11200// https://crbug.com/638375.
Fergal Daly2e7e1e12020-06-24 09:18:2811201IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
danakje80fd032018-12-04 17:39:2611202 SubframeVisibleAfterRenderViewBecomesSwappedOut) {
Alex Moshchukb13f850c2018-03-20 17:38:0911203 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
11204 EXPECT_TRUE(NavigateToURL(shell(), main_url));
11205
11206 GURL popup_url(embedded_test_server()->GetURL(
11207 "b.com", "/cross_site_iframe_factory.html?b(b)"));
11208 Shell* popup_shell = OpenPopup(shell()->web_contents(), popup_url, "popup");
Alex Moshchukb13f850c2018-03-20 17:38:0911209 FrameTreeNode* popup_child =
11210 static_cast<WebContentsImpl*>(popup_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:5511211 ->GetPrimaryFrameTree()
11212 .root()
Alex Moshchukb13f850c2018-03-20 17:38:0911213 ->child_at(0);
11214
11215 // Navigate popup's subframe to a page on a.com, which will generate
11216 // continuous compositor frames by incrementing a counter on the page.
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2011217 EXPECT_TRUE(NavigateToURLFromRenderer(
11218 popup_child, embedded_test_server()->GetURL("a.com", "/counter.html")));
Alex Moshchukb13f850c2018-03-20 17:38:0911219
11220 RenderWidgetHostViewChildFrame* child_view =
11221 static_cast<RenderWidgetHostViewChildFrame*>(
11222 popup_child->current_frame_host()->GetView());
11223
11224 // Make sure the child frame keeps generating compositor frames.
jonrossafe76382018-05-25 16:44:3211225 RenderFrameSubmissionObserver frame_counter(
11226 child_view->host_->render_frame_metadata_provider());
11227 while (frame_counter.render_frame_count() < 10)
11228 frame_counter.WaitForAnyFrameSubmission();
Alex Moshchukb13f850c2018-03-20 17:38:0911229}
11230
Fergal Daly2e7e1e12020-06-24 09:18:2811231IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, FrameDepthSimple) {
Bo Liua13e7c02018-03-28 22:24:0211232 // Five nodes, from depth 0 to 4.
11233 GURL main_url(embedded_test_server()->GetURL(
11234 "a.com", "/cross_site_iframe_factory.html?a(b(c(d(e))))"));
11235 const size_t number_of_nodes = 5;
11236 EXPECT_TRUE(NavigateToURL(shell(), main_url));
11237
Carlos Caballero15caeeb2021-10-27 09:57:5511238 FrameTreeNode* node = web_contents()->GetPrimaryFrameTree().root();
Bo Liua13e7c02018-03-28 22:24:0211239 for (unsigned int expected_depth = 0; expected_depth < number_of_nodes;
11240 ++expected_depth) {
11241 CheckFrameDepth(expected_depth, node);
11242
11243 if (expected_depth + 1 < number_of_nodes)
11244 node = node->child_at(0);
11245 }
11246}
11247
Fergal Daly2e7e1e12020-06-24 09:18:2811248IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, FrameDepthTest) {
Bo Liua13e7c02018-03-28 22:24:0211249 GURL main_url(embedded_test_server()->GetURL(
11250 "a.com", "/cross_site_iframe_factory.html?a(a,b(a))"));
11251 EXPECT_TRUE(NavigateToURL(shell(), main_url));
11252
Carlos Caballero15caeeb2021-10-27 09:57:5511253 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Bo Liua13e7c02018-03-28 22:24:0211254 CheckFrameDepth(0u, root);
11255
11256 FrameTreeNode* child0 = root->child_at(0);
11257 {
Harkiran Bolaria2e3c0b772021-09-01 16:01:4011258 EXPECT_EQ(1u, child0->current_frame_host()->GetFrameDepth());
Lei Zhangc8d18022022-08-24 17:58:0211259 RenderProcessHostPriorityClient::Priority priority =
Bo Liua13e7c02018-03-28 22:24:0211260 child0->current_frame_host()->GetRenderWidgetHost()->GetPriority();
11261 // Same site instance as root.
11262 EXPECT_EQ(0u, priority.frame_depth);
Bo Liu2f75db32018-05-03 00:56:2111263 EXPECT_EQ(0u, child0->current_frame_host()->GetProcess()->GetFrameDepth());
Bo Liua13e7c02018-03-28 22:24:0211264 }
11265
11266 FrameTreeNode* child1 = root->child_at(1);
11267 CheckFrameDepth(1u, child1);
11268 // In addition, site b's inactive Widget should not contribute priority.
11269 RenderViewHostImpl* child1_rvh =
11270 child1->current_frame_host()->render_view_host();
11271 EXPECT_FALSE(child1_rvh->is_active());
11272 EXPECT_EQ(RenderProcessHostImpl::kMaxFrameDepthForPriority,
11273 child1_rvh->GetWidget()->GetPriority().frame_depth);
11274 EXPECT_FALSE(static_cast<RenderWidgetHostOwnerDelegate*>(child1_rvh)
11275 ->ShouldContributePriorityToProcess());
Shin Kawamura94219f22025-07-17 00:20:0711276 // The RenderWidgetHost of the RenderFrameHost is different from the
11277 // RenderWidgetHost of the RenderViewHost and contributes to the priority.
11278 EXPECT_NE(child1->current_frame_host()->GetRenderWidgetHost(),
11279 child1_rvh->GetWidget());
11280 EXPECT_EQ(1u, child1->current_frame_host()
11281 ->GetLocalRenderWidgetHost()
11282 ->GetPriority()
11283 .frame_depth);
11284 EXPECT_EQ(1u, child1->current_frame_host()->GetProcess()->GetFrameDepth());
Bo Liua13e7c02018-03-28 22:24:0211285
11286 FrameTreeNode* grand_child = root->child_at(1)->child_at(0);
11287 {
Harkiran Bolaria2e3c0b772021-09-01 16:01:4011288 EXPECT_EQ(2u, grand_child->current_frame_host()->GetFrameDepth());
Lei Zhangc8d18022022-08-24 17:58:0211289 RenderProcessHostPriorityClient::Priority priority =
Bo Liua13e7c02018-03-28 22:24:0211290 grand_child->current_frame_host()->GetRenderWidgetHost()->GetPriority();
11291 EXPECT_EQ(2u, priority.frame_depth);
11292 // Same process as root
Bo Liu2f75db32018-05-03 00:56:2111293 EXPECT_EQ(0u,
11294 grand_child->current_frame_host()->GetProcess()->GetFrameDepth());
Bo Liua13e7c02018-03-28 22:24:0211295 }
11296}
11297
Charlene Yaneb2c6a1f2020-11-05 20:45:4611298// Disabled due to flakiness. crbug.com/1146083
Xiaohan Wang1ecfd002022-01-19 22:33:1011299#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
Charlene Yaneb2c6a1f2020-11-05 20:45:4611300#define MAYBE_VisibilityFrameDepthTest DISABLED_VisibilityFrameDepthTest
11301#else
11302#define MAYBE_VisibilityFrameDepthTest VisibilityFrameDepthTest
11303#endif
11304IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
11305 MAYBE_VisibilityFrameDepthTest) {
Bo Liua13e7c02018-03-28 22:24:0211306 GURL main_url(embedded_test_server()->GetURL(
11307 "a.com", "/cross_site_iframe_factory.html?a(b)"));
11308 GURL popup_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
11309 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:5511310 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Bo Liua13e7c02018-03-28 22:24:0211311 Shell* new_shell = OpenPopup(root->child_at(0), popup_url, "");
11312 FrameTreeNode* popup_root =
11313 static_cast<WebContentsImpl*>(new_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:5511314 ->GetPrimaryFrameTree()
11315 .root();
Bo Liua13e7c02018-03-28 22:24:0211316
11317 // Subframe and popup share the same process. Both are visible, so depth
11318 // should be 0.
11319 RenderProcessHost* subframe_process =
11320 root->child_at(0)->current_frame_host()->GetProcess();
11321 RenderProcessHost* popup_process =
11322 popup_root->current_frame_host()->GetProcess();
11323 EXPECT_EQ(subframe_process, popup_process);
11324 EXPECT_EQ(2, popup_process->VisibleClientCount());
Bo Liu2f75db32018-05-03 00:56:2111325 EXPECT_EQ(0u, popup_process->GetFrameDepth());
Bo Liua13e7c02018-03-28 22:24:0211326
11327 // Hide popup. Process should have one visible client and depth should be 1,
11328 // since depth 0 popup is hidden.
11329 new_shell->web_contents()->WasHidden();
11330 EXPECT_EQ(1, popup_process->VisibleClientCount());
Bo Liu2f75db32018-05-03 00:56:2111331 EXPECT_EQ(1u, popup_process->GetFrameDepth());
Bo Liua13e7c02018-03-28 22:24:0211332
11333 // Navigate main page to same origin as popup in same BrowsingInstance,
11334 // s main page should run in the same process as the popup. The depth on the
11335 // process should be 0, from the main frame of main page.
11336 EXPECT_TRUE(NavigateToURLInSameBrowsingInstance(shell(), popup_url));
11337 // Performing a Load causes aura window to be focused (see
11338 // Shell::LoadURLForFrame) which recomputes window occlusion for all windows
11339 // (on chromeos) which unhides the popup. Hide popup again.
11340 new_shell->web_contents()->WasHidden();
11341 RenderProcessHost* new_root_process =
11342 root->current_frame_host()->GetProcess();
11343 EXPECT_EQ(new_root_process, popup_process);
11344 EXPECT_EQ(1, popup_process->VisibleClientCount());
Bo Liu2f75db32018-05-03 00:56:2111345 EXPECT_EQ(0u, popup_process->GetFrameDepth());
Bo Liua13e7c02018-03-28 22:24:0211346
11347 // Go back on main page. Should go back to same state as before navigation.
11348 TestNavigationObserver back_load_observer(shell()->web_contents());
11349 shell()->web_contents()->GetController().GoBack();
11350 back_load_observer.Wait();
Bo Liuc41a4622021-08-10 22:36:5911351 new_shell->web_contents()->WasHidden();
Bo Liua13e7c02018-03-28 22:24:0211352 EXPECT_EQ(1, popup_process->VisibleClientCount());
Bo Liu2f75db32018-05-03 00:56:2111353 EXPECT_EQ(1u, popup_process->GetFrameDepth());
Bo Liua13e7c02018-03-28 22:24:0211354
11355 // Unhide popup. Should go back to same state as before hide.
11356 new_shell->web_contents()->WasShown();
11357 EXPECT_EQ(2, popup_process->VisibleClientCount());
Bo Liu2f75db32018-05-03 00:56:2111358 EXPECT_EQ(0u, popup_process->GetFrameDepth());
Bo Liua13e7c02018-03-28 22:24:0211359}
11360
Alex Moshchuk07a131892018-04-13 23:02:1811361// Check that when a postMessage is called on a remote frame, it waits for the
11362// current script block to finish executing before forwarding the postMessage,
11363// so that if the script causes any other IPCs to be sent in the same event
11364// loop iteration, those IPCs are processed, and their side effects are
11365// observed by the target frame before it receives the forwarded postMessage.
11366// See https://crbug.com/828529.
Fergal Daly2e7e1e12020-06-24 09:18:2811367IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Alex Moshchuk07a131892018-04-13 23:02:1811368 CrossProcessPostMessageWaitsForCurrentScriptToFinish) {
11369 GURL main_url(embedded_test_server()->GetURL(
11370 "a.com", "/cross_site_iframe_factory.html?a(b)"));
11371 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:5511372 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Arthur Sonzognif6785ec2022-12-05 10:11:5011373 EXPECT_EQ(root, root->frame_tree().GetFocusedFrame());
Alex Moshchuk07a131892018-04-13 23:02:1811374
11375 // Add an onmessage handler to the subframe to send back a bool of whether
11376 // the subframe has focus.
Chris Fredrickson5b9810c2023-03-27 21:08:3011377 EXPECT_TRUE(
11378 ExecJs(root->child_at(0), WaitForMessageScript("document.hasFocus()")));
Alex Moshchuk07a131892018-04-13 23:02:1811379
11380 // Now, send a postMessage from main frame to subframe, and then focus the
11381 // subframe in the same script. postMessage should be scheduled after the
11382 // focus() call, so the IPC to focus the subframe should arrive before the
11383 // postMessage IPC, and the subframe should already know that it's focused in
11384 // the onmessage handler.
Chris Fredrickson5b9810c2023-03-27 21:08:3011385 EXPECT_EQ(true, ExecJs(root,
Avi Drissmanc91bd8e2021-04-19 23:58:4411386 "frames[0].postMessage('','*');\n"
Chris Fredrickson5b9810c2023-03-27 21:08:3011387 "frames[0].focus();\n"));
11388 EXPECT_EQ(true, EvalJs(root->child_at(0), "onMessagePromise"));
Alex Moshchuk07a131892018-04-13 23:02:1811389}
11390
11391// Ensure that if a cross-process postMessage is scheduled, and then the target
11392// frame is detached before the postMessage is forwarded, the source frame's
11393// renderer does not crash.
Fergal Daly2e7e1e12020-06-24 09:18:2811394IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Alex Moshchuk07a131892018-04-13 23:02:1811395 CrossProcessPostMessageAndDetachTarget) {
11396 GURL main_url(embedded_test_server()->GetURL(
11397 "a.com", "/cross_site_iframe_factory.html?a(b)"));
11398 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:5511399 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchuk07a131892018-04-13 23:02:1811400
11401 // Send a postMessage to the subframe and then immediately detach the
11402 // subframe.
Avi Drissmanc91bd8e2021-04-19 23:58:4411403 EXPECT_TRUE(ExecJs(root,
11404 "frames[0].postMessage('','*');\n"
11405 "document.body.removeChild(\n"
11406 " document.querySelector('iframe'));\n"));
Alex Moshchuk07a131892018-04-13 23:02:1811407
11408 // Test passes if the main renderer doesn't crash. Use setTimeout to ensure
11409 // this ping is evaluated after the (scheduled) postMessage is processed.
Avi Drissmanc91bd8e2021-04-19 23:58:4411410 EXPECT_EQ(
11411 true,
11412 EvalJs(
11413 root,
Chris Fredricksonb854bbf2023-03-27 17:27:4411414 "new Promise(resolve => setTimeout(() => { resolve(true); }, 0))"));
Alex Moshchuk07a131892018-04-13 23:02:1811415}
11416
Alex Moshchuk02c5156d2018-04-25 22:26:0411417// Tests that the last committed URL is preserved on an RFH even after the RFH
11418// goes into the pending deletion state.
Fergal Daly2e7e1e12020-06-24 09:18:2811419IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Nasko Oskov0f3cbb12020-01-07 17:52:1411420 LastCommittedURLRetainedAfterUnload) {
Alex Moshchuk02c5156d2018-04-25 22:26:0411421 // Navigate to a.com.
11422 GURL start_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
11423 EXPECT_TRUE(NavigateToURL(shell(), start_url));
Dave Tapuska327c06c92022-06-13 20:31:5111424 RenderFrameHostImpl* rfh = web_contents()->GetPrimaryMainFrame();
Alex Moshchuk02c5156d2018-04-25 22:26:0411425 EXPECT_EQ(start_url, rfh->GetLastCommittedURL());
11426
Nasko Oskov0f3cbb12020-01-07 17:52:1411427 // Disable the unload ACK and the unload timer.
Dave Tapuskaca250702021-01-07 17:40:5511428 auto unload_ack_filter = base::BindRepeating([] { return true; });
11429 rfh->SetUnloadACKCallbackForTesting(unload_ack_filter);
Nasko Oskov0f3cbb12020-01-07 17:52:1411430 rfh->DisableUnloadTimerForTesting();
Alex Moshchuk02c5156d2018-04-25 22:26:0411431
11432 // Open a popup on a.com to keep the process alive.
11433 OpenPopup(shell(), embedded_test_server()->GetURL("a.com", "/title2.html"),
11434 "foo");
11435
11436 // Navigate cross-process to b.com.
11437 EXPECT_TRUE(NavigateToURL(
11438 shell(), embedded_test_server()->GetURL("b.com", "/title3.html")));
11439
11440 // The old RFH should be pending deletion.
Sreeja Kamishettye25ac752020-05-12 18:15:4811441 EXPECT_TRUE(rfh->IsPendingDeletion());
Sreeja Kamishettye49854f82021-06-02 00:52:0311442 EXPECT_FALSE(rfh->IsActive());
Dave Tapuska327c06c92022-06-13 20:31:5111443 EXPECT_NE(rfh, web_contents()->GetPrimaryMainFrame());
Alex Moshchuk02c5156d2018-04-25 22:26:0411444
11445 // Check that it still has a valid last committed URL.
11446 EXPECT_EQ(start_url, rfh->GetLastCommittedURL());
11447}
11448
Xiaohan Wang1ecfd002022-01-19 22:33:1011449#if BUILDFLAG(IS_ANDROID)
David Bokan963f2752020-03-18 18:36:3711450
11451// This test ensures that gestures from child frames notify the gesture manager
11452// which exists only on the root frame. i.e. the gesture manager knows we're in
11453// a scroll gesture when it's happening in a cross-process child frame. This is
11454// important in cases like hiding the text selection popup during a scroll.
Fergal Daly2e7e1e12020-06-24 09:18:2811455IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
David Bokan963f2752020-03-18 18:36:3711456 GestureManagerListensToChildFrames) {
11457 GURL main_url(embedded_test_server()->GetURL(
11458 "a.com", "/cross_site_iframe_factory.html?a(b)"));
11459 EXPECT_TRUE(NavigateToURL(shell(), main_url));
11460
Carlos Caballero15caeeb2021-10-27 09:57:5511461 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
David Bokan963f2752020-03-18 18:36:3711462 FrameTreeNode* child = root->child_at(0);
11463 GURL b_url(embedded_test_server()->GetURL("b.com", "/scrollable_page.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2011464 EXPECT_TRUE(NavigateToURLFromRenderer(child, b_url));
David Bokan963f2752020-03-18 18:36:3711465
11466 EXPECT_EQ(
11467 " Site A ------------ proxies for B\n"
11468 " +--Site B ------- proxies for A\n"
11469 "Where A = http://a.com/\n"
11470 " B = http://b.com/",
11471 DepictFrameTree(root));
11472
11473 RenderWidgetHost* rwh = root->current_frame_host()->GetRenderWidgetHost();
11474 RenderWidgetHost* child_rwh =
11475 child->current_frame_host()->GetRenderWidgetHost();
11476
11477 RunUntilInputProcessed(rwh);
11478 RunUntilInputProcessed(child_rwh);
11479
11480 RenderWidgetHostViewAndroid* rwhv_root =
11481 static_cast<RenderWidgetHostViewAndroid*>(
11482 root->current_frame_host()->GetRenderWidgetHost()->GetView());
11483
11484 ASSERT_FALSE(
11485 rwhv_root->gesture_listener_manager_->IsScrollInProgressForTesting());
11486
11487 // Start a scroll gesture in the child frame, ensure the main frame's gesture
11488 // listener manager records that its in a scroll.
11489 {
11490 blink::WebGestureEvent gesture_scroll_begin(
Dave Tapuska347d60a2020-04-21 23:55:4711491 blink::WebGestureEvent::Type::kGestureScrollBegin,
David Bokan963f2752020-03-18 18:36:3711492 blink::WebInputEvent::kNoModifiers,
11493 blink::WebInputEvent::GetStaticTimeStampForTests(),
11494 blink::WebGestureDevice::kTouchscreen);
11495 gesture_scroll_begin.data.scroll_begin.delta_hint_units =
11496 ui::ScrollGranularity::kScrollByPrecisePixel;
11497 gesture_scroll_begin.data.scroll_begin.delta_x_hint = 0.f;
11498 // Note: Negative y-delta in a gesture event results in scrolling down on a
11499 // page (i.e. causes positive window.scrollY).
11500 gesture_scroll_begin.data.scroll_begin.delta_y_hint = -5.f;
11501
11502 blink::WebMouseEvent mouse_move(
Dave Tapuska347d60a2020-04-21 23:55:4711503 blink::WebInputEvent::Type::kMouseMove,
11504 blink::WebInputEvent::kNoModifiers,
David Bokan963f2752020-03-18 18:36:3711505 blink::WebInputEvent::GetStaticTimeStampForTests());
11506
11507 // We wait for the dummy mouse move event since the GestureScrollEnd ACK is
11508 // used change the gesture manager scrolling state but InputEventAckWaiter
11509 // is the first-in-line so the state won't yet be changed when it returns.
11510 // Thus we send a second event and when it's ACK'd we know the first has
11511 // already been processed (we do the same thing above but with a
11512 // ScrollUpdate).
Dave Tapuska347d60a2020-04-21 23:55:4711513 InputEventAckWaiter mouse_move_waiter(
11514 child_rwh, blink::WebInputEvent::Type::kMouseMove);
David Bokan963f2752020-03-18 18:36:3711515
11516 child_rwh->ForwardGestureEvent(gesture_scroll_begin);
11517 child_rwh->ForwardMouseEvent(mouse_move);
11518 mouse_move_waiter.Wait();
11519
11520 EXPECT_TRUE(
11521 rwhv_root->gesture_listener_manager_->IsScrollInProgressForTesting());
11522 }
11523
11524 // Finish the scroll, ensure the gesture manager sees the scroll end.
11525 {
11526 blink::WebGestureEvent gesture_scroll_end(
Dave Tapuska347d60a2020-04-21 23:55:4711527 blink::WebGestureEvent::Type::kGestureScrollEnd,
David Bokan963f2752020-03-18 18:36:3711528 blink::WebInputEvent::kNoModifiers,
11529 blink::WebInputEvent::GetStaticTimeStampForTests(),
11530 blink::WebGestureDevice::kTouchscreen);
11531
11532 // See comment above for why this is sent.
11533 blink::WebMouseEvent mouse_move(
Dave Tapuska347d60a2020-04-21 23:55:4711534 blink::WebInputEvent::Type::kMouseMove,
11535 blink::WebInputEvent::kNoModifiers,
David Bokan963f2752020-03-18 18:36:3711536 blink::WebInputEvent::GetStaticTimeStampForTests());
11537
Dave Tapuska347d60a2020-04-21 23:55:4711538 InputEventAckWaiter mouse_move_waiter(
11539 child_rwh, blink::WebInputEvent::Type::kMouseMove);
David Bokan963f2752020-03-18 18:36:3711540
11541 child_rwh->ForwardGestureEvent(gesture_scroll_end);
11542 child_rwh->ForwardMouseEvent(mouse_move);
11543 mouse_move_waiter.Wait();
11544
11545 EXPECT_FALSE(
11546 rwhv_root->gesture_listener_manager_->IsScrollInProgressForTesting());
11547 }
11548}
Xiaohan Wang1ecfd002022-01-19 22:33:1011549#endif // BUILDFLAG(IS_ANDROID)
David Bokan963f2752020-03-18 18:36:3711550
Stefan Zager806e4472020-12-17 22:00:3411551IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, DisplayLockThrottlesOOPIF) {
11552 GURL url_a(embedded_test_server()->GetURL(
11553 "a.com", "/cross_site_iframe_factory.html?a(b)"));
11554 EXPECT_TRUE(NavigateToURL(shell(), url_a));
Carlos Caballero15caeeb2021-10-27 09:57:5511555 FrameTreeNode* a_frame = web_contents()->GetPrimaryFrameTree().root();
Stefan Zager806e4472020-12-17 22:00:3411556 FrameTreeNode* b_frame = a_frame->child_at(0);
11557
11558 // Force a lifecycle update in both frames to get to steady state.
11559 ASSERT_TRUE(EvalJsAfterLifecycleUpdate(a_frame->current_frame_host(), "", "")
Chris Fredricksonbc209522025-07-25 17:05:5211560 .is_ok());
Stefan Zager806e4472020-12-17 22:00:3411561 ASSERT_TRUE(EvalJsAfterLifecycleUpdate(b_frame->current_frame_host(), "", "")
Chris Fredricksonbc209522025-07-25 17:05:5211562 .is_ok());
Stefan Zager806e4472020-12-17 22:00:3411563
11564 // Display lock an ancestor of the <iframe> element in a_frame. The display
11565 // lock status will be propagated to the OOPIF during lifecycle update.
11566 ASSERT_TRUE(EvalJsAfterLifecycleUpdate(
11567 a_frame->current_frame_host(),
11568 "document.body.style = 'content-visibility: hidden'", "")
Chris Fredricksonbc209522025-07-25 17:05:5211569 .is_ok());
Stefan Zager806e4472020-12-17 22:00:3411570
11571 // At this point, a_frame should have already sent an IPC to b_frame causing
11572 // b_frame to become throttled. Create an IntersectionObserver and observe a
11573 // visible element in b_frame. The display lock status should cause the
11574 // visible element to be reported as "not intersecting".
11575 static const char kObserverScript[] = R"(
11576 new Promise((resolve, reject) => {
11577 new IntersectionObserver((entries, observer) => {
11578 observer.unobserve(entries[0].target);
11579 resolve(String(entries[0].isIntersecting))
11580 }).observe(document.getElementById('siteNameHeading'))
11581 })
11582 )";
11583 EvalJsResult result1 = EvalJs(b_frame->current_frame_host(), kObserverScript);
Chris Fredricksonbc209522025-07-25 17:05:5211584 ASSERT_TRUE(result1.is_ok());
Stefan Zager806e4472020-12-17 22:00:3411585 EXPECT_EQ(result1.ExtractString(), "false");
11586
11587 // Unlock the element in a_frame, run through the same steps, and look for an
11588 // "is intersecting" notification.
11589 ASSERT_TRUE(EvalJsAfterLifecycleUpdate(a_frame->current_frame_host(),
11590 "document.body.style = ''", "")
Chris Fredricksonbc209522025-07-25 17:05:5211591 .is_ok());
Stefan Zager806e4472020-12-17 22:00:3411592 EvalJsResult result2 = EvalJs(b_frame->current_frame_host(), kObserverScript);
Stefan Zager806e4472020-12-17 22:00:3411593 EXPECT_EQ(result2.ExtractString(), "true");
11594}
11595
Alex Moshchukbf8684e2018-05-02 22:59:0211596namespace {
11597
11598// Helper class to intercept DidCommitProvisionalLoad messages and inject a
11599// call to close the current tab right before them.
Arthur Hemery2a0a28be2019-03-06 17:51:3611600class ClosePageBeforeCommitHelper : public DidCommitNavigationInterceptor {
Alex Moshchukbf8684e2018-05-02 22:59:0211601 public:
11602 explicit ClosePageBeforeCommitHelper(WebContents* web_contents)
Arthur Hemery2a0a28be2019-03-06 17:51:3611603 : DidCommitNavigationInterceptor(web_contents) {}
Alex Moshchukbf8684e2018-05-02 22:59:0211604
Peter Boström9b036532021-10-28 23:37:2811605 ClosePageBeforeCommitHelper(const ClosePageBeforeCommitHelper&) = delete;
11606 ClosePageBeforeCommitHelper& operator=(const ClosePageBeforeCommitHelper&) =
11607 delete;
11608
Alex Moshchukbf8684e2018-05-02 22:59:0211609 void Wait() {
Peter Boströmdd7e40ec2021-04-05 20:40:1011610 run_loop_ = std::make_unique<base::RunLoop>();
Alex Moshchukbf8684e2018-05-02 22:59:0211611 run_loop_->Run();
11612 run_loop_.reset();
11613 }
11614
11615 private:
Arthur Hemery2a0a28be2019-03-06 17:51:3611616 // DidCommitNavigationInterceptor:
11617 bool WillProcessDidCommitNavigation(
Alex Moshchukbf8684e2018-05-02 22:59:0211618 RenderFrameHost* render_frame_host,
Arthur Hemery2a0a28be2019-03-06 17:51:3611619 NavigationRequest* navigation_request,
arthursonzogni73fe3212020-11-17 13:24:0711620 mojom::DidCommitProvisionalLoadParamsPtr* params,
Lukasz Anforowicz3cfc1efd2019-02-22 02:29:2911621 mojom::DidCommitProvisionalLoadInterfaceParamsPtr* interface_params)
Oksana Zhuravlova8b88e572019-01-07 21:54:0011622 override {
Alex Moshchuke558aba2023-01-13 07:42:0211623 RenderFrameHostImpl* rfh =
11624 static_cast<RenderFrameHostImpl*>(render_frame_host);
11625 EXPECT_TRUE(rfh->render_view_host()->is_active());
Tommy Steimel5f491422023-02-21 12:51:3711626 rfh->GetMainFrame()->ClosePage(
11627 RenderFrameHostImpl::ClosePageSource::kBrowser);
Alex Moshchukbf8684e2018-05-02 22:59:0211628 if (run_loop_)
11629 run_loop_->Quit();
clamyd3bfdb02018-07-12 13:52:1811630 return true;
Alex Moshchukbf8684e2018-05-02 22:59:0211631 }
11632
11633 std::unique_ptr<base::RunLoop> run_loop_;
Alex Moshchukbf8684e2018-05-02 22:59:0211634};
11635
11636} // namespace
11637
11638// Verify that when a tab is closed just before a commit IPC arrives for a
11639// subframe in the tab, a subsequent resource timing IPC from the subframe RFH
11640// won't generate a renderer kill. See https://crbug.com/805705.
Fergal Daly2e7e1e12020-06-24 09:18:2811641IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Alex Moshchukbf8684e2018-05-02 22:59:0211642 CloseTabBeforeSubframeCommits) {
11643 GURL main_url(embedded_test_server()->GetURL(
11644 "a.com", "/cross_site_iframe_factory.html?a(b)"));
11645 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:5511646 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchukbf8684e2018-05-02 22:59:0211647
11648 // Open a popup in a.com to keep that process alive.
11649 GURL same_site_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
11650 Shell* new_shell = OpenPopup(root, same_site_url, "");
11651
11652 // Add a blank grandchild frame.
11653 RenderFrameHostCreatedObserver frame_observer(shell()->web_contents(), 1);
Avi Drissmanc91bd8e2021-04-19 23:58:4411654 EXPECT_TRUE(
11655 ExecJs(root->child_at(0),
11656 "document.body.appendChild(document.createElement('iframe'));"));
Alex Moshchukbf8684e2018-05-02 22:59:0211657 frame_observer.Wait();
11658 FrameTreeNode* grandchild = root->child_at(0)->child_at(0);
11659
11660 // Navigate grandchild to an a.com URL. Note that only a frame's initial
11661 // navigation forwards resource timing info to parent, so it's important that
11662 // this iframe was initially blank.
11663 //
11664 // Just before this URL commits, close the page.
11665 ClosePageBeforeCommitHelper close_page_helper(web_contents());
Avi Drissmanc91bd8e2021-04-19 23:58:4411666 EXPECT_TRUE(ExecJs(grandchild, JsReplace("location = $1", same_site_url)));
Alex Moshchukbf8684e2018-05-02 22:59:0211667 close_page_helper.Wait();
11668
11669 // Test passes if the a.com renderer doesn't crash. Ping to verify.
Avi Drissmanc91bd8e2021-04-19 23:58:4411670 EXPECT_EQ(true, EvalJs(new_shell, "true;"));
Alex Moshchukbf8684e2018-05-02 22:59:0211671}
11672
sunxd540a9962018-05-24 22:51:0611673class SitePerProcessBrowserTouchActionTest : public SitePerProcessBrowserTest {
11674 public:
David Bokanc8973d42020-04-07 05:58:0911675 SitePerProcessBrowserTouchActionTest() = default;
sunxd540a9962018-05-24 22:51:0611676
W. James MacLean537cb2b2019-10-17 23:21:2111677 bool GetTouchActionForceEnableZoom(RenderWidgetHost* rwh) {
Kartar Singhd084e582024-06-12 19:22:2411678 input::InputRouterImpl* input_router = static_cast<input::InputRouterImpl*>(
W. James MacLean537cb2b2019-10-17 23:21:2111679 static_cast<RenderWidgetHostImpl*>(rwh)->input_router());
11680 return input_router->touch_action_filter_.force_enable_zoom_;
11681 }
11682
Philip Rogersa2c73772020-06-22 14:54:1311683 // Computes the effective and allowed touch action for |rwhv_child| by
Xida Chen9d4c1be2018-10-13 00:52:1211684 // dispatching a touch to it through |rwhv_root|. |rwhv_root| is the root
11685 // frame containing |rwhv_child|. |rwhv_child| is the child (or indirect
11686 // descendent) of |rwhv_root| to get the touch action of. |event_position|
11687 // should be within |rwhv_child| in |rwhv_root|'s coordinate space.
11688 void GetTouchActionsForChild(
Kartar Singhb1bfa1a2024-06-24 13:14:5711689 input::RenderWidgetHostInputEventRouter* router,
sunxd540a9962018-05-24 22:51:0611690 RenderWidgetHostViewBase* rwhv_root,
11691 RenderWidgetHostViewBase* rwhv_child,
Xida Chen81f32ee2018-09-13 21:41:2811692 const gfx::Point& event_position,
Arthur Sonzognic686e8f2024-01-11 08:36:3711693 std::optional<cc::TouchAction>& effective_touch_action,
11694 std::optional<cc::TouchAction>& allowed_touch_action) {
sunxd540a9962018-05-24 22:51:0611695 InputEventAckWaiter ack_observer(
11696 rwhv_child->GetRenderWidgetHost(),
Dave Tapuskae01e0fde2020-04-20 18:28:4111697 base::BindRepeating([](blink::mojom::InputEventResultSource source,
11698 blink::mojom::InputEventResultState state,
sunxd540a9962018-05-24 22:51:0611699 const blink::WebInputEvent& event) {
Dave Tapuska347d60a2020-04-21 23:55:4711700 return event.GetType() == blink::WebGestureEvent::Type::kTouchStart ||
11701 event.GetType() == blink::WebGestureEvent::Type::kTouchMove ||
11702 event.GetType() == blink::WebGestureEvent::Type::kTouchEnd;
sunxd540a9962018-05-24 22:51:0611703 }));
11704
Kartar Singhd084e582024-06-12 19:22:2411705 input::InputRouterImpl* input_router = static_cast<input::InputRouterImpl*>(
Xida Chend74fd532018-10-16 17:46:2811706 static_cast<RenderWidgetHostImpl*>(rwhv_child->GetRenderWidgetHost())
11707 ->input_router());
11708 // Clear the touch actions that were set by previous touches.
11709 input_router->touch_action_filter_.allowed_touch_action_.reset();
sunxd540a9962018-05-24 22:51:0611710 // Send a touch start event to child to get the TAF filled with child
11711 // frame's touch action.
11712 ack_observer.Reset();
Dave Tapuska6db16e32020-06-09 13:58:0611713 blink::SyntheticWebTouchEvent touch_event;
sunxd540a9962018-05-24 22:51:0611714 int index = touch_event.PressPoint(event_position.x(), event_position.y());
Jonathan Ross7dc2b842024-07-12 16:16:1511715 router->RouteTouchEvent(rwhv_root, &touch_event, ui::LatencyInfo());
sunxd540a9962018-05-24 22:51:0611716 ack_observer.Wait();
Xida Chena964d8a92019-03-15 20:09:1911717 // Reset them to get the new value.
11718 effective_touch_action.reset();
Philip Rogersa2c73772020-06-22 14:54:1311719 allowed_touch_action.reset();
Xida Chen9d4c1be2018-10-13 00:52:1211720 effective_touch_action =
Xida Chend74fd532018-10-16 17:46:2811721 input_router->touch_action_filter_.allowed_touch_action_;
Xida Chen84d0845e2018-11-02 20:36:5411722 // Effective touch action are sent from a separate IPC
11723 // channel, so it is not guaranteed to have value when the ACK for the
11724 // touch start arrived because the ACK is from the main thread.
Philip Rogersa2c73772020-06-22 14:54:1311725 allowed_touch_action =
11726 input_router->touch_action_filter_.compositor_allowed_touch_action_;
sunxd540a9962018-05-24 22:51:0611727
11728 // Send a touch move and touch end to complete the sequence, this also
11729 // avoids triggering DCHECKs when sending followup events.
11730 ack_observer.Reset();
11731 touch_event.MovePoint(index, 1, 1);
Jonathan Ross7dc2b842024-07-12 16:16:1511732 router->RouteTouchEvent(rwhv_root, &touch_event, ui::LatencyInfo());
sunxd540a9962018-05-24 22:51:0611733 ack_observer.Wait();
11734
11735 ack_observer.Reset();
11736 touch_event.ReleasePoint(index);
Jonathan Ross7dc2b842024-07-12 16:16:1511737 router->RouteTouchEvent(rwhv_root, &touch_event, ui::LatencyInfo());
sunxd540a9962018-05-24 22:51:0611738 ack_observer.Wait();
sunxd540a9962018-05-24 22:51:0611739 }
11740
Xida Chen84d0845e2018-11-02 20:36:5411741 void GiveItSomeTime(const base::TimeDelta& t) {
11742 base::RunLoop run_loop;
Sean Maher5b9af51f2022-11-21 15:32:4711743 base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
Xida Chen84d0845e2018-11-02 20:36:5411744 FROM_HERE, run_loop.QuitClosure(), t);
11745 run_loop.Run();
11746 }
11747
sunxd540a9962018-05-24 22:51:0611748 // Waits until the parent frame has had enough time to propagate the effective
11749 // touch action to the child frame and the child frame has had enough time to
11750 // process it.
jonrosse9c5a518d2018-06-15 16:13:2211751 void WaitForTouchActionUpdated(
11752 MainThreadFrameObserver* root_thread_observer,
11753 MainThreadFrameObserver* child_thread_observer) {
11754 // Sends an event to the root frame's renderer main thread, upon return the
11755 // root frame should have calculated the new effective touch action for the
11756 // child frame.
11757 root_thread_observer->Wait();
11758 // Sends an event to the child frame's renderer main thread, upon return the
11759 // child frame should have received the effective touch action from parent
11760 // and propagated it.
11761 child_thread_observer->Wait();
11762 // The child's handling of the touch action may lead to further propagation
11763 // back to the parent. This sends an event to the root frame's renderer main
11764 // thread, upon return it should have handled any touch action update.
11765 root_thread_observer->Wait();
sunxd540a9962018-05-24 22:51:0611766 }
11767};
11768
Xiaohan Wang1ecfd002022-01-19 22:33:1011769#if BUILDFLAG(IS_ANDROID)
W. James MacLean537cb2b2019-10-17 23:21:2111770// Class to set |force_enable_zoom| to true in WebkitPrefs.
Scott Violet99861992023-02-08 01:20:1211771class EnableForceZoomContentClient
11772 : public ContentBrowserTestContentBrowserClient {
W. James MacLean537cb2b2019-10-17 23:21:2111773 public:
11774 EnableForceZoomContentClient() = default;
11775
Peter Boström9b036532021-10-28 23:37:2811776 EnableForceZoomContentClient(const EnableForceZoomContentClient&) = delete;
11777 EnableForceZoomContentClient& operator=(const EnableForceZoomContentClient&) =
11778 delete;
11779
Dave Tapuska183b8d702025-01-07 18:47:1811780 void OverrideWebPreferences(WebContents* web_contents,
11781 SiteInstance& main_frame_site,
11782 blink::web_pref::WebPreferences* prefs) override {
W. James MacLean537cb2b2019-10-17 23:21:2111783 prefs->force_enable_zoom = true;
11784 }
W. James MacLean537cb2b2019-10-17 23:21:2111785};
11786
Aman Verma2e897242025-01-14 12:08:2611787class AndroidInputBrowserTest : public SitePerProcessBrowserTest {
11788 public:
11789 AndroidInputBrowserTest() {
11790 scoped_feature_list_.InitWithFeatureStates(
11791 {{input::features::kInputOnViz, true},
11792 {viz::mojom::EnableVizTestApis, true}});
11793 }
11794
11795 bool GetRenderInputRouterForceEnableZoom(RenderWidgetHostImpl* rwh) {
11796 return rwh->GetRenderInputRouter()->GetForceEnableZoom();
11797 }
11798
11799 RenderWidgetHostImpl* GetRenderWidgetHost() const {
11800 RenderWidgetHostImpl* const rwh =
11801 RenderWidgetHostImpl::From(shell()
11802 ->web_contents()
11803 ->GetRenderWidgetHostView()
11804 ->GetRenderWidgetHost());
11805 CHECK(rwh);
11806 return rwh;
11807 }
11808
11809 private:
11810 base::test::ScopedFeatureList scoped_feature_list_;
11811};
11812
11813// Check if browser's |force_enable_zoom| state is in sync with Viz's state with
11814// InputVizard enabled.
11815IN_PROC_BROWSER_TEST_P(AndroidInputBrowserTest, CheckForceEnableZoomValue) {
11816 // Return early if transferring input to Viz isn't supported.
Kartar Singhde37b142025-05-21 11:31:3911817 if (!input::InputUtils::IsTransferInputToVizSupported()) {
Aman Verma2e897242025-01-14 12:08:2611818 return;
11819 }
11820
11821 mojo::ScopedAllowSyncCallForTesting allowed_for_testing;
11822 content::RenderFrameSubmissionObserver render_frame_submission_observer(
11823 shell()->web_contents());
11824
11825 EXPECT_TRUE(NavigateToURL(
11826 shell(), embedded_test_server()->GetURL("foo.com", "/title1.html")));
11827 if (render_frame_submission_observer.render_frame_count() == 0) {
11828 render_frame_submission_observer.WaitForAnyFrameSubmission();
11829 }
11830
11831 EXPECT_FALSE(GetRenderInputRouterForceEnableZoom(GetRenderWidgetHost()));
11832 bool enabled = false;
11833 content::GetHostFrameSinkManager()
11834 ->GetFrameSinkManagerTestApi()
11835 .GetForceEnableZoomState(GetRenderWidgetHost()->GetFrameSinkId(),
11836 &enabled);
11837 EXPECT_FALSE(enabled);
11838
11839 EnableForceZoomContentClient new_client;
11840
11841 web_contents()->OnWebPreferencesChanged();
11842 if (render_frame_submission_observer.render_frame_count() == 0) {
11843 render_frame_submission_observer.WaitForAnyFrameSubmission();
11844 }
11845
11846 EXPECT_TRUE(GetRenderInputRouterForceEnableZoom(GetRenderWidgetHost()));
11847 content::GetHostFrameSinkManager()
11848 ->GetFrameSinkManagerTestApi()
11849 .GetForceEnableZoomState(GetRenderWidgetHost()->GetFrameSinkId(),
11850 &enabled);
11851 EXPECT_TRUE(enabled);
11852
11853 // Navigate to a cross-site website.
11854 EXPECT_TRUE(NavigateToURL(
11855 shell(), embedded_test_server()->GetURL("bar.com", "/title2.html")));
11856 if (render_frame_submission_observer.render_frame_count() == 0) {
11857 render_frame_submission_observer.WaitForAnyFrameSubmission();
11858 }
11859
11860 EXPECT_TRUE(GetRenderInputRouterForceEnableZoom(GetRenderWidgetHost()));
11861 content::GetHostFrameSinkManager()
11862 ->GetFrameSinkManagerTestApi()
11863 .GetForceEnableZoomState(GetRenderWidgetHost()->GetFrameSinkId(),
11864 &enabled);
11865 EXPECT_TRUE(enabled);
11866}
11867
Aman Verma44335c72025-02-19 18:43:5711868class GpuInfoUpdateObserver : public GpuDataManagerObserver {
11869 public:
11870 explicit GpuInfoUpdateObserver(base::OnceClosure callback)
11871 : callback_(std::move(callback)) {
11872 observation_.Observe(GpuDataManager::GetInstance());
11873 }
11874 ~GpuInfoUpdateObserver() override = default;
11875
11876 void OnGpuInfoUpdate() override {
11877 if (callback_) {
11878 std::move(callback_).Run();
11879 }
11880 }
11881
11882 private:
11883 base::OnceClosure callback_;
11884 base::ScopedObservation<GpuDataManager, GpuDataManagerObserver> observation_{
11885 this};
11886};
11887
11888// Checks if RenderInputRouterDelegate mojo connection is reset when GPU process
11889// restarts.
11890IN_PROC_BROWSER_TEST_P(AndroidInputBrowserTest,
11891 RestartingGPUProcessResetsMojoConnection) {
Aman Vermae2a8d912025-05-30 14:30:2711892 base::test::TestTraceProcessor ttp;
11893 ttp.StartTrace("viz");
Aman Verma44335c72025-02-19 18:43:5711894 RenderFrameSubmissionObserver render_frame_submission_observer(
11895 web_contents());
11896 EXPECT_TRUE(NavigateToURL(
11897 shell(), embedded_test_server()->GetURL("foo.com", "/title1.html")));
11898 if (render_frame_submission_observer.render_frame_count() == 0) {
11899 render_frame_submission_observer.WaitForAnyFrameSubmission();
11900 }
11901
Aman Verma44335c72025-02-19 18:43:5711902 base::RunLoop run_loop;
11903 // This observer is begin used here to signal if the GPU process has
11904 // restarted.
11905 GpuInfoUpdateObserver gpu_observer(run_loop.QuitClosure());
11906
Aman Vermae2a8d912025-05-30 14:30:2711907 RenderFrameSubmissionObserver render_frame_submission_observer2(
11908 web_contents());
11909
Aman Verma44335c72025-02-19 18:43:5711910 // Kill GPU process explicitly, this should trigger a restart.
11911 KillGpuProcess();
11912 run_loop.Run();
11913
Aman Vermae2a8d912025-05-30 14:30:2711914 if (render_frame_submission_observer2.render_frame_count() == 0) {
11915 render_frame_submission_observer2.WaitForAnyFrameSubmission();
Aman Verma44335c72025-02-19 18:43:5711916 }
11917
11918 absl::Status status = ttp.StopAndParseTrace();
11919 ASSERT_TRUE(status.ok()) << status.message();
11920
11921 std::string query = R"(
11922 SELECT COUNT(*) AS cnt
11923 FROM slice
11924 WHERE name = 'InputManager::SetupRenderInputRouterDelegateConnection'
11925 ORDER BY ts ASC
11926 )";
11927 auto result = ttp.RunQuery(query);
11928 ASSERT_TRUE(result.has_value());
11929
11930 // `result.value()` would look something like this: {{"cnt"}, {"<num>"}}.
Kartar Singhde37b142025-05-21 11:31:3911931 EXPECT_THAT(
11932 result.value(),
11933 testing::ElementsAre(
11934 testing::ElementsAre("cnt"),
11935 testing::ElementsAre(
Aman Vermae2a8d912025-05-30 14:30:2711936 input::InputUtils::IsTransferInputToVizSupported() ? "2" : "0")));
Aman Verma44335c72025-02-19 18:43:5711937}
11938
Fergal Daly2e7e1e12020-06-24 09:18:2811939IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTouchActionTest,
W. James MacLean537cb2b2019-10-17 23:21:2111940 ForceEnableZoomPropagatesToChild) {
11941 GURL main_url(embedded_test_server()->GetURL(
11942 "a.com", "/cross_site_iframe_factory.html?a(b)"));
11943 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:5511944 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLean537cb2b2019-10-17 23:21:2111945 ASSERT_EQ(1U, root->child_count());
11946 GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
11947 FrameTreeNode* child = root->child_at(0);
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2011948 EXPECT_TRUE(NavigateToURLFromRenderer(child, b_url));
W. James MacLean537cb2b2019-10-17 23:21:2111949 WaitForHitTestData(child->current_frame_host());
11950
11951 // Get access to child's TouchActionFilter.
11952 RenderWidgetHost* child_rwh =
11953 child->current_frame_host()->GetRenderWidgetHost();
11954 EXPECT_FALSE(GetTouchActionForceEnableZoom(child_rwh));
11955
11956 EnableForceZoomContentClient new_client;
W. James MacLean537cb2b2019-10-17 23:21:2111957
Rakina Zata Amni4029b6d2020-07-28 02:36:2011958 web_contents()->OnWebPreferencesChanged();
W. James MacLean537cb2b2019-10-17 23:21:2111959
11960 EXPECT_TRUE(GetTouchActionForceEnableZoom(child_rwh));
11961
11962 // Add a new oopif child frame, and make sure it initializes with the correct
11963 // value of ForceEnableZoom.
11964 GURL c_url = embedded_test_server()->GetURL("c.com", "/title1.html");
11965 std::string create_frame_script = base::StringPrintf(
11966 "var new_iframe = document.createElement('iframe');"
11967 "new_iframe.src = '%s';"
11968 "document.body.appendChild(new_iframe);",
11969 c_url.spec().c_str());
Avi Drissmanc91bd8e2021-04-19 23:58:4411970 EXPECT_TRUE(ExecJs(root, create_frame_script));
W. James MacLean537cb2b2019-10-17 23:21:2111971 EXPECT_TRUE(WaitForLoadStop(web_contents()));
11972 ASSERT_EQ(2U, root->child_count());
11973
11974 FrameTreeNode* new_child = root->child_at(1);
11975 EXPECT_NE(root->current_frame_host()->GetRenderWidgetHost(),
11976 new_child->current_frame_host()->GetRenderWidgetHost());
11977 EXPECT_TRUE(GetTouchActionForceEnableZoom(
11978 new_child->current_frame_host()->GetRenderWidgetHost()));
W. James MacLean537cb2b2019-10-17 23:21:2111979}
yongha78.lee18e71522021-03-23 19:10:3511980
11981IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTouchActionTest,
11982 CheckForceEnableZoomValue) {
11983 EXPECT_TRUE(NavigateToURL(
11984 shell(), embedded_test_server()->GetURL("foo.com", "/title1.html")));
11985 EXPECT_FALSE(GetTouchActionForceEnableZoom(
Dave Tapuska327c06c92022-06-13 20:31:5111986 web_contents()->GetPrimaryMainFrame()->GetRenderViewHost()->GetWidget()));
yongha78.lee18e71522021-03-23 19:10:3511987
11988 EnableForceZoomContentClient new_client;
yongha78.lee18e71522021-03-23 19:10:3511989
11990 web_contents()->OnWebPreferencesChanged();
11991
11992 EXPECT_TRUE(GetTouchActionForceEnableZoom(
Dave Tapuska327c06c92022-06-13 20:31:5111993 web_contents()->GetPrimaryMainFrame()->GetRenderViewHost()->GetWidget()));
yongha78.lee18e71522021-03-23 19:10:3511994
11995 EXPECT_TRUE(NavigateToURL(
11996 shell(), embedded_test_server()->GetURL("bar.com", "/title2.html")));
11997
11998 EXPECT_TRUE(GetTouchActionForceEnableZoom(
Dave Tapuska327c06c92022-06-13 20:31:5111999 web_contents()->GetPrimaryMainFrame()->GetRenderViewHost()->GetWidget()));
yongha78.lee18e71522021-03-23 19:10:3512000}
12001
Xiaohan Wang1ecfd002022-01-19 22:33:1012002#endif // BUILDFLAG(IS_ANDROID)
W. James MacLean537cb2b2019-10-17 23:21:2112003
arthursonzogni662d8812019-03-26 11:31:1312004// Flaky on every platform, failing most of the time on Android.
12005// See https://crbug.com/945734
Fergal Daly2e7e1e12020-06-24 09:18:2812006IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTouchActionTest,
arthursonzogni662d8812019-03-26 11:31:1312007 DISABLED_EffectiveTouchActionPropagatesAcrossFrames) {
sunxd540a9962018-05-24 22:51:0612008 GURL main_url(embedded_test_server()->GetURL(
12009 "a.com", "/cross_site_iframe_factory.html?a(b)"));
12010 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:5512011 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
sunxd540a9962018-05-24 22:51:0612012 FrameTreeNode* child = root->child_at(0);
12013 RenderWidgetHostViewBase* rwhv_root = static_cast<RenderWidgetHostViewBase*>(
12014 root->current_frame_host()->GetRenderWidgetHost()->GetView());
12015 RenderWidgetHostViewBase* rwhv_child = static_cast<RenderWidgetHostViewBase*>(
12016 child->current_frame_host()->GetRenderWidgetHost()->GetView());
jonrosse9c5a518d2018-06-15 16:13:2212017 std::unique_ptr<MainThreadFrameObserver> root_thread_observer(
12018 new MainThreadFrameObserver(
12019 root->current_frame_host()->GetRenderWidgetHost()));
12020 root_thread_observer->Wait();
sunxd540a9962018-05-24 22:51:0612021
12022 GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2012023 EXPECT_TRUE(NavigateToURLFromRenderer(child, b_url));
sunxd540a9962018-05-24 22:51:0612024
12025 // Force the renderer to generate a new frame.
Avi Drissmanc91bd8e2021-04-19 23:58:4412026 EXPECT_TRUE(ExecJs(shell(), "document.body.style.touchAction = 'none'"));
sunxd540a9962018-05-24 22:51:0612027 // Waits for the next frame.
kylechara7c549b2019-07-29 17:47:2812028 WaitForHitTestData(child->current_frame_host());
jonrosse9c5a518d2018-06-15 16:13:2212029 std::unique_ptr<MainThreadFrameObserver> child_thread_observer(
12030 new MainThreadFrameObserver(
12031 child->current_frame_host()->GetRenderWidgetHost()));
sunxd540a9962018-05-24 22:51:0612032
12033 RenderWidgetHostViewChildFrame* child_view =
12034 static_cast<RenderWidgetHostViewChildFrame*>(
12035 child->current_frame_host()->GetRenderWidgetHost()->GetView());
12036 gfx::Point point_inside_child = ToFlooredPoint(
12037 child_view->TransformPointToRootCoordSpaceF(gfx::PointF(+5.f, +5.f)));
12038
Kartar Singhb1bfa1a2024-06-24 13:14:5712039 input::RenderWidgetHostInputEventRouter* router =
sunxd540a9962018-05-24 22:51:0612040 static_cast<WebContentsImpl*>(web_contents())->GetInputEventRouter();
12041
jonrosse9c5a518d2018-06-15 16:13:2212042 WaitForTouchActionUpdated(root_thread_observer.get(),
12043 child_thread_observer.get());
Arthur Sonzognic686e8f2024-01-11 08:36:3712044 std::optional<cc::TouchAction> effective_touch_action;
12045 std::optional<cc::TouchAction> allowed_touch_action;
Henrique Ferreiro00e24f12019-12-17 01:14:4812046 cc::TouchAction expected_touch_action = cc::TouchAction::kPan;
sunxd540a9962018-05-24 22:51:0612047 // Gestures are filtered by the intersection of touch-action values of the
12048 // touched element and all its ancestors up to the one that implements the
12049 // gesture. Since iframe allows scrolling, touch action pan restrictions will
Henrique Ferreiro00e24f12019-12-17 01:14:4812050 // not affect iframe's descendants, so we expect TouchAction::kPan instead of
12051 // TouchAction::kAuto in iframe's child.
Xida Chen9d4c1be2018-10-13 00:52:1212052 GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
Philip Rogersa2c73772020-06-22 14:54:1312053 effective_touch_action, allowed_touch_action);
12054 if (allowed_touch_action.has_value())
12055 EXPECT_EQ(expected_touch_action, allowed_touch_action.value());
sunxd540a9962018-05-24 22:51:0612056
Avi Drissmanc91bd8e2021-04-19 23:58:4412057 EXPECT_TRUE(ExecJs(shell(), "document.body.style.touchAction = 'auto'"));
jonrosse9c5a518d2018-06-15 16:13:2212058 WaitForTouchActionUpdated(root_thread_observer.get(),
12059 child_thread_observer.get());
Henrique Ferreiro00e24f12019-12-17 01:14:4812060 expected_touch_action = cc::TouchAction::kAuto;
Xida Chen9d4c1be2018-10-13 00:52:1212061 GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
Philip Rogersa2c73772020-06-22 14:54:1312062 effective_touch_action, allowed_touch_action);
David Bokanc8973d42020-04-07 05:58:0912063 EXPECT_EQ(expected_touch_action, effective_touch_action.has_value()
12064 ? effective_touch_action.value()
12065 : cc::TouchAction::kAuto);
Philip Rogersa2c73772020-06-22 14:54:1312066 if (allowed_touch_action.has_value())
12067 EXPECT_EQ(expected_touch_action, allowed_touch_action.value());
sunxd540a9962018-05-24 22:51:0612068}
12069
Xida Chena8f7a252019-06-21 15:00:1912070// Flaky on all platform. http://crbug.com/9515270
12071IN_PROC_BROWSER_TEST_F(
12072 SitePerProcessBrowserTouchActionTest,
12073 DISABLED_EffectiveTouchActionPropagatesAcrossNestedFrames) {
sunxd540a9962018-05-24 22:51:0612074 GURL main_url(embedded_test_server()->GetURL(
12075 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
12076 EXPECT_TRUE(NavigateToURL(shell(), main_url));
12077
Carlos Caballero15caeeb2021-10-27 09:57:5512078 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
sunxd540a9962018-05-24 22:51:0612079 FrameTreeNode* parent = root->child_at(0);
12080 GURL b_url(embedded_test_server()->GetURL(
12081 "b.com", "/frame_tree/page_with_iframe_in_div.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2012082 EXPECT_TRUE(NavigateToURLFromRenderer(parent, b_url));
sunxd540a9962018-05-24 22:51:0612083
12084 ASSERT_EQ(1U, parent->child_count());
12085 EXPECT_EQ(
12086 " Site A ------------ proxies for B C\n"
12087 " +--Site B ------- proxies for A C\n"
12088 " +--Site C -- proxies for A B\n"
12089 "Where A = http://a.com/\n"
12090 " B = http://b.com/\n"
12091 " C = http://bar.com/",
12092 DepictFrameTree(root));
12093
12094 FrameTreeNode* child = root->child_at(0)->child_at(0);
12095 RenderWidgetHostViewBase* rwhv_root = static_cast<RenderWidgetHostViewBase*>(
12096 root->current_frame_host()->GetRenderWidgetHost()->GetView());
12097 RenderWidgetHostViewBase* rwhv_child = static_cast<RenderWidgetHostViewBase*>(
12098 child->current_frame_host()->GetRenderWidgetHost()->GetView());
jonrosse9c5a518d2018-06-15 16:13:2212099 std::unique_ptr<MainThreadFrameObserver> root_thread_observer(
12100 new MainThreadFrameObserver(
12101 root->current_frame_host()->GetRenderWidgetHost()));
12102 root_thread_observer->Wait();
sunxd540a9962018-05-24 22:51:0612103
Avi Drissmanc91bd8e2021-04-19 23:58:4412104 EXPECT_TRUE(ExecJs(shell(), "document.body.style.touchAction = 'none'"));
sunxd540a9962018-05-24 22:51:0612105
12106 // Wait for child frame ready in order to get the correct point inside child.
kylechara7c549b2019-07-29 17:47:2812107 WaitForHitTestData(child->current_frame_host());
jonrosse9c5a518d2018-06-15 16:13:2212108 std::unique_ptr<MainThreadFrameObserver> child_thread_observer(
12109 new MainThreadFrameObserver(
12110 child->current_frame_host()->GetRenderWidgetHost()));
sunxd540a9962018-05-24 22:51:0612111 RenderWidgetHostViewChildFrame* child_view =
12112 static_cast<RenderWidgetHostViewChildFrame*>(
12113 child->current_frame_host()->GetRenderWidgetHost()->GetView());
12114 gfx::Point point_inside_child = ToFlooredPoint(
12115 child_view->TransformPointToRootCoordSpaceF(gfx::PointF(+5.f, +5.f)));
12116
Kartar Singhb1bfa1a2024-06-24 13:14:5712117 input::RenderWidgetHostInputEventRouter* router =
sunxd540a9962018-05-24 22:51:0612118 static_cast<WebContentsImpl*>(web_contents())->GetInputEventRouter();
12119
12120 // Child should inherit effective touch action none from root.
jonrosse9c5a518d2018-06-15 16:13:2212121 WaitForTouchActionUpdated(root_thread_observer.get(),
12122 child_thread_observer.get());
Arthur Sonzognic686e8f2024-01-11 08:36:3712123 std::optional<cc::TouchAction> effective_touch_action;
12124 std::optional<cc::TouchAction> allowed_touch_action;
Henrique Ferreiro00e24f12019-12-17 01:14:4812125 cc::TouchAction expected_touch_action = cc::TouchAction::kPan;
Xida Chen9d4c1be2018-10-13 00:52:1212126 GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
Philip Rogersa2c73772020-06-22 14:54:1312127 effective_touch_action, allowed_touch_action);
Philip Rogersa2c73772020-06-22 14:54:1312128 if (allowed_touch_action.has_value())
12129 EXPECT_EQ(expected_touch_action, allowed_touch_action.value());
sunxd540a9962018-05-24 22:51:0612130
12131 // Child should inherit effective touch action none from parent.
Avi Drissmanc91bd8e2021-04-19 23:58:4412132 EXPECT_TRUE(ExecJs(shell(), "document.body.style.touchAction = 'auto'"));
12133 EXPECT_TRUE(ExecJs(
sunxd540a9962018-05-24 22:51:0612134 parent,
12135 "document.getElementById('parent-div').style.touchAction = 'none';"));
jonrosse9c5a518d2018-06-15 16:13:2212136 WaitForTouchActionUpdated(root_thread_observer.get(),
12137 child_thread_observer.get());
Xida Chen9d4c1be2018-10-13 00:52:1212138 GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
Philip Rogersa2c73772020-06-22 14:54:1312139 effective_touch_action, allowed_touch_action);
Philip Rogersa2c73772020-06-22 14:54:1312140 if (allowed_touch_action.has_value())
12141 EXPECT_EQ(expected_touch_action, allowed_touch_action.value());
sunxd540a9962018-05-24 22:51:0612142
12143 // Child should inherit effective touch action auto from root and parent.
Avi Drissmanc91bd8e2021-04-19 23:58:4412144 EXPECT_TRUE(ExecJs(
sunxd540a9962018-05-24 22:51:0612145 parent,
12146 "document.getElementById('parent-div').style.touchAction = 'auto'"));
jonrosse9c5a518d2018-06-15 16:13:2212147 WaitForTouchActionUpdated(root_thread_observer.get(),
12148 child_thread_observer.get());
Henrique Ferreiro00e24f12019-12-17 01:14:4812149 expected_touch_action = cc::TouchAction::kAuto;
Xida Chen9d4c1be2018-10-13 00:52:1212150 GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
Philip Rogersa2c73772020-06-22 14:54:1312151 effective_touch_action, allowed_touch_action);
Philip Rogersa2c73772020-06-22 14:54:1312152 if (allowed_touch_action.has_value())
12153 EXPECT_EQ(expected_touch_action, allowed_touch_action.value());
sunxd540a9962018-05-24 22:51:0612154}
12155
Fergal Daly2e7e1e12020-06-24 09:18:2812156IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTouchActionTest,
sunxd540a9962018-05-24 22:51:0612157 EffectiveTouchActionPropagatesWhenChildFrameNavigates) {
12158 GURL main_url(embedded_test_server()->GetURL(
12159 "a.com", "/cross_site_iframe_factory.html?a(b)"));
12160 EXPECT_TRUE(NavigateToURL(shell(), main_url));
12161
Carlos Caballero15caeeb2021-10-27 09:57:5512162 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
sunxd540a9962018-05-24 22:51:0612163 FrameTreeNode* child = root->child_at(0);
12164 GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2012165 EXPECT_TRUE(NavigateToURLFromRenderer(child, b_url));
sunxd540a9962018-05-24 22:51:0612166
12167 EXPECT_EQ(
12168 " Site A ------------ proxies for B\n"
12169 " +--Site B ------- proxies for A\n"
12170 "Where A = http://a.com/\n"
12171 " B = http://b.com/",
12172 DepictFrameTree(root));
12173
12174 RenderWidgetHostViewBase* rwhv_root = static_cast<RenderWidgetHostViewBase*>(
12175 root->current_frame_host()->GetRenderWidgetHost()->GetView());
12176 RenderWidgetHostViewBase* rwhv_child = static_cast<RenderWidgetHostViewBase*>(
12177 child->current_frame_host()->GetRenderWidgetHost()->GetView());
jonrosse9c5a518d2018-06-15 16:13:2212178 std::unique_ptr<MainThreadFrameObserver> root_thread_observer(
12179 new MainThreadFrameObserver(
12180 root->current_frame_host()->GetRenderWidgetHost()));
12181 root_thread_observer->Wait();
sunxd540a9962018-05-24 22:51:0612182
Avi Drissmanc91bd8e2021-04-19 23:58:4412183 EXPECT_TRUE(ExecJs(shell(), "document.body.style.touchAction = 'none'"));
sunxd540a9962018-05-24 22:51:0612184
12185 // Wait for child frame ready in order to get the correct point inside child.
kylechara7c549b2019-07-29 17:47:2812186 WaitForHitTestData(child->current_frame_host());
jonrosse9c5a518d2018-06-15 16:13:2212187 std::unique_ptr<MainThreadFrameObserver> child_thread_observer(
12188 new MainThreadFrameObserver(
12189 child->current_frame_host()->GetRenderWidgetHost()));
sunxd540a9962018-05-24 22:51:0612190 RenderWidgetHostViewChildFrame* child_view =
12191 static_cast<RenderWidgetHostViewChildFrame*>(
12192 child->current_frame_host()->GetRenderWidgetHost()->GetView());
12193 gfx::Point point_inside_child = gfx::ToFlooredPoint(
12194 child_view->TransformPointToRootCoordSpaceF(gfx::PointF(+5.f, +5.f)));
12195
Kartar Singhb1bfa1a2024-06-24 13:14:5712196 input::RenderWidgetHostInputEventRouter* router =
sunxd540a9962018-05-24 22:51:0612197 static_cast<WebContentsImpl*>(web_contents())->GetInputEventRouter();
12198 // Child should inherit effective touch action none from root.
jonrosse9c5a518d2018-06-15 16:13:2212199 WaitForTouchActionUpdated(root_thread_observer.get(),
12200 child_thread_observer.get());
Arthur Sonzognic686e8f2024-01-11 08:36:3712201 std::optional<cc::TouchAction> effective_touch_action;
12202 std::optional<cc::TouchAction> allowed_touch_action;
Shimi Zhang9287e2cd2020-10-31 00:40:3512203 cc::TouchAction expected_touch_action =
Mahesh Machavolu5cf127f2022-06-17 09:26:2412204 cc::TouchAction::kPan | cc::TouchAction::kInternalPanXScrolls |
12205 cc::TouchAction::kInternalNotWritable;
Xida Chen9d4c1be2018-10-13 00:52:1212206 GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
Philip Rogersa2c73772020-06-22 14:54:1312207 effective_touch_action, allowed_touch_action);
12208 if (allowed_touch_action.has_value())
12209 EXPECT_EQ(expected_touch_action, allowed_touch_action.value());
sunxd540a9962018-05-24 22:51:0612210
12211 // After navigation, child should still inherit effective touch action none
12212 // from parent.
12213 GURL new_url(embedded_test_server()->GetURL("c.com", "/title2.html"));
jonrosse9c5a518d2018-06-15 16:13:2212214 // Reset before navigation, as navigation destroys the underlying
12215 // RenderWidgetHost being observed.
12216 child_thread_observer.reset();
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2012217 EXPECT_TRUE(NavigateToURLFromRenderer(child, new_url));
kylechara7c549b2019-07-29 17:47:2812218 WaitForHitTestData(child->current_frame_host());
jonrosse9c5a518d2018-06-15 16:13:2212219 // Navigation destroys the previous RenderWidgetHost, so we need to begin
12220 // observing the new renderer main thread associated with the child frame.
Peter Boströmdd7e40ec2021-04-05 20:40:1012221 child_thread_observer = std::make_unique<MainThreadFrameObserver>(
12222 child->current_frame_host()->GetRenderWidgetHost());
jonrosse9c5a518d2018-06-15 16:13:2212223
sunxd540a9962018-05-24 22:51:0612224 rwhv_child = static_cast<RenderWidgetHostViewBase*>(
12225 child->current_frame_host()->GetRenderWidgetHost()->GetView());
12226
jonrosse9c5a518d2018-06-15 16:13:2212227 WaitForTouchActionUpdated(root_thread_observer.get(),
12228 child_thread_observer.get());
Xida Chen9d4c1be2018-10-13 00:52:1212229 GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
Philip Rogersa2c73772020-06-22 14:54:1312230 effective_touch_action, allowed_touch_action);
12231 if (allowed_touch_action.has_value())
12232 EXPECT_EQ(expected_touch_action, allowed_touch_action.value());
sunxd540a9962018-05-24 22:51:0612233}
12234
Fergal Daly2e7e1e12020-06-24 09:18:2812235IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Lukasz Anforowicz873e6082018-05-17 00:12:3312236 ChildFrameCrashMetrics_KilledMainFrame) {
12237 GURL main_url(embedded_test_server()->GetURL(
12238 "a.com", "/cross_site_iframe_factory.html?a(a(b(b,c)))"));
12239 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:5512240 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Lukasz Anforowicz873e6082018-05-17 00:12:3312241
12242 // Kill the main frame.
12243 base::HistogramTester histograms;
12244 RenderProcessHost* child_process = root->current_frame_host()->GetProcess();
12245 RenderProcessHostWatcher crash_observer(
12246 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
12247 child_process->Shutdown(0);
12248 crash_observer.Wait();
12249
12250 // Verify that no child frame metrics got logged.
12251 histograms.ExpectTotalCount("Stability.ChildFrameCrash.Visibility", 0);
12252}
12253
Fergal Daly2e7e1e12020-06-24 09:18:2812254IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Lukasz Anforowicz873e6082018-05-17 00:12:3312255 ChildFrameCrashMetrics_NeverShown) {
12256 // Set-up a frame tree that helps verify what the metrics tracks:
12257 // 1) frames (12 frames are affected if B process gets killed) or
12258 // 2) widgets (10 b widgets and 1 c widget are affected if B is killed) or
12259 // 3) crashes (1 crash if B process gets killed)?
12260 GURL main_url(embedded_test_server()->GetURL(
12261 "a.com", "/cross_site_iframe_factory.html?a(b(b,c),b,b,b,b,b,b,b,b,b)"));
12262 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:5512263 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Lukasz Anforowicz873e6082018-05-17 00:12:3312264
12265 // Hide the web contents (UpdateWebContentsVisibility is called twice to avoid
12266 // hitting the |!did_first_set_visible_| case).
12267 web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
12268 web_contents()->UpdateWebContentsVisibility(Visibility::HIDDEN);
12269
12270 // Kill the subframe.
12271 base::HistogramTester histograms;
12272 RenderProcessHost* child_process =
12273 root->child_at(0)->current_frame_host()->GetProcess();
12274 RenderProcessHostWatcher crash_observer(
12275 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
12276 child_process->Shutdown(0);
12277 crash_observer.Wait();
12278
12279 // Navigate away - this will trigger logging of the UMA.
12280 EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
Dominique Fauteux-Chapleau3e8d3452021-07-14 17:20:0212281
12282 // Wait until the page with the crashed frame gets unloaded (triggering its
12283 // evicton if it got into the back/forward cache), so that the histogram will
12284 // be recorded when the renderer process is gone.
Alison Gale81f4f2c72024-04-22 19:33:3112285 // TODO(crbug.com/40175240): Ensure pages with crashed subframes won't
Dominique Fauteux-Chapleau3e8d3452021-07-14 17:20:0212286 // get into back/forward cache.
12287 InactiveRenderFrameHostDeletionObserver inactive_rfh_deletion_observer(
12288 web_contents());
12289 inactive_rfh_deletion_observer.Wait();
12290
Sharon Yangb8cf5be2021-06-07 14:29:4912291 histograms.ExpectUniqueSample("Stability.ChildFrameCrash.Visibility",
12292 CrashVisibility::kNeverVisibleAfterCrash, 10);
Lukasz Anforowicz873e6082018-05-17 00:12:3312293}
12294
Fergal Daly2e7e1e12020-06-24 09:18:2812295IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Lukasz Anforowicz873e6082018-05-17 00:12:3312296 ChildFrameCrashMetrics_ScrolledIntoView) {
12297 GURL main_url(embedded_test_server()->GetURL(
12298 "a.com", "/cross_site_iframe_factory.html?a(b)"));
12299 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:5512300 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Lukasz Anforowicz873e6082018-05-17 00:12:3312301
12302 // Fill the main frame so that the subframe is pushed below the fold (is
12303 // scrolled outside of the current view) and wait until the main frame redraws
12304 // itself (i.e. making sure CPFC::OnUpdateViewportIntersection has arrived).
12305 std::string filling_script = R"(
12306 var frame = document.body.querySelectorAll("iframe")[0];
12307 for (var i = 0; i < 100; i++) {
12308 var p = document.createElement("p");
12309 p.innerText = "blah";
12310 document.body.insertBefore(p, frame);
12311 }
12312 )";
Avi Drissmanc91bd8e2021-04-19 23:58:4412313 EXPECT_TRUE(ExecJs(root, filling_script));
Marko Ivanovich6da09f72020-10-06 06:42:5512314 // This will ensure that browser has received the
12315 // FrameHostMsg_UpdateViewportIntersection IPC message from the renderer main
12316 // thread.
12317 EXPECT_EQ(true,
12318 EvalJsAfterLifecycleUpdate(root->current_frame_host(), "", "true"));
Lukasz Anforowicz873e6082018-05-17 00:12:3312319
12320 // Kill the child frame.
12321 base::HistogramTester histograms;
12322 RenderProcessHost* child_process =
12323 root->child_at(0)->current_frame_host()->GetProcess();
12324 RenderProcessHostWatcher crash_observer(
12325 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
12326 child_process->Shutdown(0);
12327 crash_observer.Wait();
12328
12329 // Verify that no child frame metrics got logged (yet - while the subframe is
12330 // below the fold / is not scrolled into view).
12331 histograms.ExpectTotalCount("Stability.ChildFrameCrash.Visibility", 0);
Alex Moshchuk2b0c91d2018-10-10 18:41:1612332 histograms.ExpectTotalCount(
12333 "Stability.ChildFrameCrash.ShownAfterCrashingReason", 0);
Lukasz Anforowicz873e6082018-05-17 00:12:3312334
12335 // Scroll the subframe into view and wait until the scrolled frame draws
12336 // itself.
12337 std::string scrolling_script = R"(
12338 var frame = document.body.querySelectorAll("iframe")[0];
12339 frame.scrollIntoView();
12340 )";
Avi Drissmanc91bd8e2021-04-19 23:58:4412341 EXPECT_TRUE(ExecJs(root, scrolling_script));
Marko Ivanovich6da09f72020-10-06 06:42:5512342 // Wait for FrameHostMsg_UpdateViewportIntersection again.
12343 EXPECT_EQ(true,
12344 EvalJsAfterLifecycleUpdate(root->current_frame_host(), "", "true"));
Lukasz Anforowicz873e6082018-05-17 00:12:3312345
12346 // Verify that the expected metrics got logged.
12347 histograms.ExpectUniqueSample(
12348 "Stability.ChildFrameCrash.Visibility",
12349 CrossProcessFrameConnector::CrashVisibility::kShownAfterCrashing, 1);
Alex Moshchuk2b0c91d2018-10-10 18:41:1612350 histograms.ExpectUniqueSample(
12351 "Stability.ChildFrameCrash.ShownAfterCrashingReason",
12352 CrossProcessFrameConnector::ShownAfterCrashingReason::
12353 kViewportIntersection,
12354 1);
12355}
12356
Alex Moshchukaefae1b12018-05-30 20:02:5712357class SitePerProcessAndProcessPerSiteBrowserTest
12358 : public SitePerProcessBrowserTest {
12359 public:
12360 SitePerProcessAndProcessPerSiteBrowserTest() {}
12361
Peter Boström9b036532021-10-28 23:37:2812362 SitePerProcessAndProcessPerSiteBrowserTest(
12363 const SitePerProcessAndProcessPerSiteBrowserTest&) = delete;
12364 SitePerProcessAndProcessPerSiteBrowserTest& operator=(
12365 const SitePerProcessAndProcessPerSiteBrowserTest&) = delete;
12366
Alex Moshchukaefae1b12018-05-30 20:02:5712367 protected:
12368 void SetUpCommandLine(base::CommandLine* command_line) override {
Fergal Daly2e7e1e12020-06-24 09:18:2812369 SitePerProcessBrowserTestBase::SetUpCommandLine(command_line);
Alex Moshchukaefae1b12018-05-30 20:02:5712370 command_line->AppendSwitch(switches::kProcessPerSite);
12371 }
Alex Moshchukaefae1b12018-05-30 20:02:5712372};
12373
12374// Verify that when --site-per-process is combined with --process-per-site, a
12375// cross-site, browser-initiated navigation with a generated page transition
12376// does not stay in the old SiteInstance. See https://crbug.com/825411.
Fergal Daly2e7e1e12020-06-24 09:18:2812377IN_PROC_BROWSER_TEST_P(SitePerProcessAndProcessPerSiteBrowserTest,
Alex Moshchukaefae1b12018-05-30 20:02:5712378 GeneratedTransitionsSwapProcesses) {
12379 EXPECT_TRUE(NavigateToURL(
12380 shell(), embedded_test_server()->GetURL("foo.com", "/title1.html")));
12381 scoped_refptr<SiteInstance> foo_site_instance(
12382 web_contents()->GetSiteInstance());
12383
12384 // Navigate cross-site via a generated transition. This would normally
12385 // happen for search queries.
12386 TestNavigationObserver observer(web_contents());
12387 NavigationController::LoadURLParams params(
12388 embedded_test_server()->GetURL("bar.com", "/title2.html"));
12389 params.transition_type = ui::PAGE_TRANSITION_GENERATED;
12390 web_contents()->GetController().LoadURLWithParams(params);
12391 observer.Wait();
12392
12393 // Ensure the original SiteInstance wasn't reused.
12394 EXPECT_NE(foo_site_instance, web_contents()->GetSiteInstance());
12395
12396 // Ensure the new page can access cookies without getting killed.
Avi Drissmanc91bd8e2021-04-19 23:58:4412397 EXPECT_TRUE(ExecJs(web_contents(), "document.cookie = 'foo=bar';"));
12398 EXPECT_EQ("foo=bar", EvalJs(web_contents(), "document.cookie;"));
Alex Moshchukaefae1b12018-05-30 20:02:5712399}
12400
Alex Moshchuka5f40232018-06-04 19:25:5312401namespace {
12402
12403// Helper for waiting until next same-document navigation commits in
12404// |web_contents|.
12405class SameDocumentCommitObserver : public WebContentsObserver {
12406 public:
Lukasz Anforowicz5a92f052018-08-15 20:38:4712407 explicit SameDocumentCommitObserver(WebContents* web_contents)
12408 : WebContentsObserver(web_contents) {
Alex Moshchuka5f40232018-06-04 19:25:5312409 EXPECT_TRUE(web_contents);
12410 }
12411
Peter Boström9b036532021-10-28 23:37:2812412 SameDocumentCommitObserver(const SameDocumentCommitObserver&) = delete;
12413 SameDocumentCommitObserver& operator=(const SameDocumentCommitObserver&) =
12414 delete;
12415
Lukasz Anforowicz5a92f052018-08-15 20:38:4712416 void Wait() { run_loop_.Run(); }
Alex Moshchuka5f40232018-06-04 19:25:5312417
12418 const GURL& last_committed_url() { return last_committed_url_; }
12419
12420 private:
12421 void DidFinishNavigation(NavigationHandle* navigation_handle) override {
12422 if (navigation_handle->IsSameDocument()) {
12423 last_committed_url_ = navigation_handle->GetURL();
Lukasz Anforowicz5a92f052018-08-15 20:38:4712424 run_loop_.Quit();
Alex Moshchuka5f40232018-06-04 19:25:5312425 }
12426 }
12427
12428 GURL last_committed_url_;
Lukasz Anforowicz5a92f052018-08-15 20:38:4712429 base::RunLoop run_loop_;
Alex Moshchuka5f40232018-06-04 19:25:5312430};
12431
12432} // namespace
12433
12434// Ensure that a same-document navigation does not cancel an ongoing
12435// cross-process navigation. See https://crbug.com/825677.
Fergal Daly2e7e1e12020-06-24 09:18:2812436IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Alex Moshchuka5f40232018-06-04 19:25:5312437 ReplaceStateDoesNotCancelCrossSiteNavigation) {
12438 GURL url(embedded_test_server()->GetURL("a.com", "/title1.html"));
12439 EXPECT_TRUE(NavigateToURL(shell(), url));
Carlos Caballero15caeeb2021-10-27 09:57:5512440 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchuka5f40232018-06-04 19:25:5312441
12442 // Give the page a beforeunload handler that does a replaceState. Do this
12443 // from setTimeout so that the navigation that triggers beforeunload is
12444 // already started when the replaceState happens.
Avi Drissmanc91bd8e2021-04-19 23:58:4412445 EXPECT_TRUE(ExecJs(root,
12446 "window.onbeforeunload = function (e) {"
12447 " setTimeout(() => {"
12448 " history.replaceState({}, 'footitle', 'foo');"
12449 " }, 0);"
12450 "};\n"));
Alex Moshchuka5f40232018-06-04 19:25:5312451
12452 GURL url2 = embedded_test_server()->GetURL("b.com", "/title1.html");
12453 TestNavigationManager cross_site_navigation(web_contents(), url2);
12454 SameDocumentCommitObserver replace_state_observer(web_contents());
12455
12456 // Start a cross-site navigation. Using a renderer-initiated navigation
12457 // rather than a browser-initiated one is important here, since
12458 // https://crbug.com/825677 was triggered only when replaceState ran while
Chris Fredricksond3bb2682023-05-10 03:32:2612459 // having a user gesture, which will be the case here since ExecJs
Alex Moshchuka5f40232018-06-04 19:25:5312460 // runs with a user gesture.
Avi Drissmanc91bd8e2021-04-19 23:58:4412461 EXPECT_TRUE(ExecJs(root, JsReplace("location.href = $1", url2)));
Alex Moshchuka5f40232018-06-04 19:25:5312462 EXPECT_TRUE(cross_site_navigation.WaitForRequestStart());
12463
12464 // Now wait for the replaceState to commit while the cross-process navigation
12465 // is paused.
12466 replace_state_observer.Wait();
12467 GURL replace_state_url = embedded_test_server()->GetURL("a.com", "/foo");
12468 EXPECT_EQ(replace_state_url, replace_state_observer.last_committed_url());
12469
12470 // The cross-process navigation should not be canceled after the
12471 // replaceState.
12472 ASSERT_TRUE(root->IsLoading());
12473 ASSERT_TRUE(root->navigation_request());
12474
12475 // Resume and finish the cross-process navigation.
12476 cross_site_navigation.ResumeNavigation();
Fergal Daly83bc3cd2023-01-18 00:22:5412477 ASSERT_TRUE(cross_site_navigation.WaitForNavigationFinished());
Alex Moshchuka5f40232018-06-04 19:25:5312478 EXPECT_TRUE(cross_site_navigation.was_successful());
12479 EXPECT_EQ(url2, web_contents()->GetLastCommittedURL());
12480}
12481
Alex Moshchukade42bd2018-06-04 23:37:1812482// Test that a pending frame policy, such as an updated sandbox attribute, does
12483// not take effect after a same-document navigation. See
12484// https://crbug.com/849311.
Fergal Daly2e7e1e12020-06-24 09:18:2812485IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Alex Moshchukade42bd2018-06-04 23:37:1812486 SameDocumentNavigationDoesNotCommitPendingFramePolicy) {
12487 GURL main_url(embedded_test_server()->GetURL(
12488 "a.com", "/cross_site_iframe_factory.html?a(b)"));
12489 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:5512490 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchukade42bd2018-06-04 23:37:1812491 FrameTreeNode* subframe = root->child_at(0);
12492
12493 // The subframe should not be sandboxed.
arthursonzognib93a4472020-04-10 07:38:0012494 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Alex Moshchukade42bd2018-06-04 23:37:1812495 subframe->pending_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:0012496 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Alex Moshchukade42bd2018-06-04 23:37:1812497 subframe->effective_frame_policy().sandbox_flags);
12498
12499 // Set the "sandbox" attribute on the subframe; pending policy should update.
Avi Drissmanc91bd8e2021-04-19 23:58:4412500 EXPECT_TRUE(ExecJs(
Alex Moshchukade42bd2018-06-04 23:37:1812501 root, "document.querySelector('iframe').sandbox = 'allow-scripts';"));
12502 // "allow-scripts" resets both SandboxFlags::Scripts and
12503 // SandboxFlags::AutomaticFeatures bits per blink::ParseSandboxPolicy().
arthursonzognib93a4472020-04-10 07:38:0012504 network::mojom::WebSandboxFlags expected_flags =
12505 network::mojom::WebSandboxFlags::kAll &
12506 ~network::mojom::WebSandboxFlags::kScripts &
12507 ~network::mojom::WebSandboxFlags::kAutomaticFeatures;
Alex Moshchukade42bd2018-06-04 23:37:1812508 EXPECT_EQ(expected_flags, subframe->pending_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:0012509 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Alex Moshchukade42bd2018-06-04 23:37:1812510 subframe->effective_frame_policy().sandbox_flags);
12511
12512 // Commit a same-document navigation with replaceState. The new sandbox
12513 // flags should still be pending but not effective.
12514 SameDocumentCommitObserver replace_state_observer(web_contents());
Avi Drissmanc91bd8e2021-04-19 23:58:4412515 EXPECT_TRUE(ExecJs(subframe, "history.replaceState({}, 'footitle', 'foo');"));
Alex Moshchukade42bd2018-06-04 23:37:1812516 replace_state_observer.Wait();
12517
12518 EXPECT_EQ(expected_flags, subframe->pending_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:0012519 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Alex Moshchukade42bd2018-06-04 23:37:1812520 subframe->effective_frame_policy().sandbox_flags);
12521
12522 // Also try a same-document navigation to a fragment, which also shouldn't
12523 // commit the pending sandbox flags.
12524 GURL fragment_url = GURL(subframe->current_url().spec() + "#foo");
12525 {
12526 SameDocumentCommitObserver fragment_observer(web_contents());
Avi Drissmanc91bd8e2021-04-19 23:58:4412527 EXPECT_TRUE(ExecJs(subframe, JsReplace("location.href=$1", fragment_url)));
Alex Moshchukade42bd2018-06-04 23:37:1812528 fragment_observer.Wait();
12529 EXPECT_EQ(fragment_url, subframe->current_url());
12530 }
12531
12532 EXPECT_EQ(expected_flags, subframe->pending_frame_policy().sandbox_flags);
arthursonzognib93a4472020-04-10 07:38:0012533 EXPECT_EQ(network::mojom::WebSandboxFlags::kNone,
Alex Moshchukade42bd2018-06-04 23:37:1812534 subframe->effective_frame_policy().sandbox_flags);
12535}
12536
Alex Moshchukb1f87482018-07-19 01:51:5112537// Ensure that when two cross-site frames have subframes with unique origins,
12538// and those subframes create blob URLs and navigate to them, the blob URLs end
12539// up in different processes. See https://crbug.com/863623.
Fergal Daly2e7e1e12020-06-24 09:18:2812540IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Alex Moshchukb1f87482018-07-19 01:51:5112541 TwoBlobURLsWithNullOriginDontShareProcess) {
12542 GURL main_url(embedded_test_server()->GetURL(
12543 "a.com", "/navigation_controller/page_with_data_iframe.html"));
12544 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:5512545 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchukb1f87482018-07-19 01:51:5112546 FrameTreeNode* subframe = root->child_at(0);
12547
12548 // Create a blob URL in the subframe, and navigate to it.
12549 TestNavigationObserver observer(shell()->web_contents());
12550 std::string blob_script =
12551 "var blob = new Blob(['foo'], {type : 'text/html'});"
12552 "var url = URL.createObjectURL(blob);"
12553 "location = url;";
Avi Drissmanc91bd8e2021-04-19 23:58:4412554 EXPECT_TRUE(ExecJs(subframe, blob_script));
Alex Moshchukb1f87482018-07-19 01:51:5112555 observer.Wait();
12556 RenderFrameHostImpl* subframe_rfh = subframe->current_frame_host();
12557 EXPECT_TRUE(subframe_rfh->GetLastCommittedURL().SchemeIsBlob());
12558
12559 // Open a cross-site popup and repeat these steps.
12560 GURL popup_url(embedded_test_server()->GetURL(
12561 "b.com", "/navigation_controller/page_with_data_iframe.html"));
12562 Shell* new_shell = OpenPopup(root, popup_url, "");
12563 FrameTreeNode* popup_root =
12564 static_cast<WebContentsImpl*>(new_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:5512565 ->GetPrimaryFrameTree()
12566 .root();
Alex Moshchukb1f87482018-07-19 01:51:5112567 FrameTreeNode* popup_subframe = popup_root->child_at(0);
12568
12569 TestNavigationObserver popup_observer(new_shell->web_contents());
Avi Drissmanc91bd8e2021-04-19 23:58:4412570 EXPECT_TRUE(ExecJs(popup_subframe, blob_script));
Alex Moshchukb1f87482018-07-19 01:51:5112571 popup_observer.Wait();
12572 RenderFrameHostImpl* popup_subframe_rfh =
12573 popup_subframe->current_frame_host();
12574 EXPECT_TRUE(popup_subframe_rfh->GetLastCommittedURL().SchemeIsBlob());
12575
12576 // Ensure that the two blob subframes don't share a process or SiteInstance.
12577 EXPECT_NE(subframe->current_frame_host()->GetSiteInstance(),
12578 popup_subframe->current_frame_host()->GetSiteInstance());
12579 EXPECT_NE(
12580 subframe->current_frame_host()->GetSiteInstance()->GetProcess(),
12581 popup_subframe->current_frame_host()->GetSiteInstance()->GetProcess());
12582 EXPECT_NE(
12583 subframe->current_frame_host()->GetSiteInstance()->GetSiteURL(),
12584 popup_subframe->current_frame_host()->GetSiteInstance()->GetSiteURL());
12585}
12586
Alex Moshchukac99cce2018-08-02 04:48:2312587// Ensure that when a process is about to be destroyed after the last active
12588// frame in it goes away, an attempt to reuse a proxy in that process doesn't
12589// result in a crash. See https://crbug.com/794625.
Alison Gale923a33e2024-04-22 23:34:2812590// TODO(crbug.com/42050611): This is flaky on Fuchsia because the
Fabrice de Gansc5c1b7662022-04-26 17:00:1912591// MessagePort is not cleared on the other side, resulting in Zircon killing the
12592// process. See the comment referencing the same bug in
12593// //mojo/core/channel_fuchsia.cc
12594#if BUILDFLAG(IS_FUCHSIA)
12595#define MAYBE_RenderFrameProxyNotRecreatedDuringProcessShutdown \
Fabrice de Gansd8f64c22022-04-26 20:57:2612596 DISABLED_RenderFrameProxyNotRecreatedDuringProcessShutdown
Fabrice de Gansc5c1b7662022-04-26 17:00:1912597#else
12598#define MAYBE_RenderFrameProxyNotRecreatedDuringProcessShutdown \
12599 RenderFrameProxyNotRecreatedDuringProcessShutdown
12600#endif
12601IN_PROC_BROWSER_TEST_P(
12602 SitePerProcessBrowserTest,
12603 MAYBE_RenderFrameProxyNotRecreatedDuringProcessShutdown) {
Fergal Daly58742ef2023-12-21 12:36:2012604 DisableBackForwardCacheForTesting(
12605 web_contents(), content::BackForwardCache::TEST_REQUIRES_NO_CACHING);
Alex Moshchukac99cce2018-08-02 04:48:2312606 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
12607 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:5512608 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Alex Moshchukac99cce2018-08-02 04:48:2312609
12610 GURL popup_url(embedded_test_server()->GetURL(
12611 "b.com", "/title1.html"));
12612 Shell* new_shell = OpenPopup(root, popup_url, "foo");
12613 FrameTreeNode* popup_root =
12614 static_cast<WebContentsImpl*>(new_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:5512615 ->GetPrimaryFrameTree()
12616 .root();
Alex Moshchukac99cce2018-08-02 04:48:2312617 auto* rfh = popup_root->current_frame_host();
12618
Nasko Oskov0f3cbb12020-01-07 17:52:1412619 // Disable the unload timer to prevent flakiness.
12620 rfh->DisableUnloadTimerForTesting();
Alex Moshchukac99cce2018-08-02 04:48:2312621
12622 // This will be used to monitor that b.com process exits cleanly.
12623 RenderProcessHostWatcher b_process_observer(
12624 popup_root->current_frame_host()->GetProcess(),
12625 RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
12626
12627 // In the first tab, install a postMessage handler to navigate the popup to a
12628 // hung b.com URL once the first message is received.
12629 GURL hung_b_url(embedded_test_server()->GetURL("b.com", "/hung"));
12630 TestNavigationManager manager(new_shell->web_contents(), hung_b_url);
Avi Drissmanc91bd8e2021-04-19 23:58:4412631 EXPECT_TRUE(ExecJs(shell(), JsReplace(R"(
Alex Moshchukac99cce2018-08-02 04:48:2312632 window.done = false;
12633 window.onmessage = () => {
12634 if (!window.done) {
danakj824a7ff2019-02-07 20:34:0212635 window.open($1, 'foo');
Alex Moshchukac99cce2018-08-02 04:48:2312636 window.done = true;
12637 }
danakj824a7ff2019-02-07 20:34:0212638 };)",
Avi Drissmanc91bd8e2021-04-19 23:58:4412639 hung_b_url)));
Alex Moshchukac99cce2018-08-02 04:48:2312640
Fergal Daly58742ef2023-12-21 12:36:2012641 // In the popup, install a pagehide handler to send a lot of postMessages to
Alex Moshchukac99cce2018-08-02 04:48:2312642 // the opener. This keeps the MessageLoop in the b.com process busy after
12643 // navigating away from the current document. In https://crbug.com/794625,
12644 // this was needed so that a subsequent IPC to recreate a proxy arrives
12645 // before the process fully shuts down.
Avi Drissmanc91bd8e2021-04-19 23:58:4412646 EXPECT_TRUE(ExecJs(new_shell, R"(
Fergal Daly58742ef2023-12-21 12:36:2012647 window.onpagehide = () => {
Alex Moshchukac99cce2018-08-02 04:48:2312648 for (var i=0; i<10000; i++)
12649 opener.postMessage('hi','*');
12650 })"));
12651
Nasko Oskov0f3cbb12020-01-07 17:52:1412652 // Navigate popup to a.com. This unloads the last active frame in the b.com
12653 // process, and hence initiates process shutdown.
Alex Moshchukac99cce2018-08-02 04:48:2312654 TestFrameNavigationObserver commit_observer(popup_root);
12655 GURL another_a_url(embedded_test_server()->GetURL("a.com", "/title3.html"));
Avi Drissmanc91bd8e2021-04-19 23:58:4412656 EXPECT_TRUE(ExecJs(new_shell, JsReplace("location = $1", another_a_url)));
Alex Moshchukac99cce2018-08-02 04:48:2312657 commit_observer.WaitForCommit();
12658
12659 // At this point, popup's original RFH is pending deletion.
Sreeja Kamishettye25ac752020-05-12 18:15:4812660 EXPECT_TRUE(rfh->IsPendingDeletion());
Alex Moshchukac99cce2018-08-02 04:48:2312661
Fergal Daly58742ef2023-12-21 12:36:2012662 // When the opener receives a postMessage from the popup's pagehide handler,
12663 // it should start a navigation back to b.com. Wait for it. This navigation
Alex Moshchukac99cce2018-08-02 04:48:2312664 // creates a speculative RFH which reuses the proxy that was created as part
Nasko Oskov0f3cbb12020-01-07 17:52:1412665 // of navigating from |popup_url| to |another_a_url|.
Alex Moshchukac99cce2018-08-02 04:48:2312666 EXPECT_TRUE(manager.WaitForRequestStart());
12667
12668 // Cancel the started navigation (to /hung) in the popup and make sure the
12669 // b.com renderer process exits cleanly without a crash. In
12670 // https://crbug.com/794625, the crash was caused by trying to recreate the
12671 // reused proxy, which had been incorrectly set as non-live.
Rakina Zata Amni58681c62024-06-25 06:32:1312672 popup_root->ResetNavigationRequest(
12673 NavigationDiscardReason::kExplicitCancellation);
Alex Moshchukac99cce2018-08-02 04:48:2312674 b_process_observer.Wait();
12675 EXPECT_TRUE(b_process_observer.did_exit_normally());
12676}
12677
Fergal Daly2e7e1e12020-06-24 09:18:2812678IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Lukasz Anforowicz5a92f052018-08-15 20:38:4712679 CommitTimeoutForHungRenderer) {
12680 // Navigate first tab to a.com.
Lukasz Anforowicz4f0593d2018-09-21 18:33:5412681 GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
12682 EXPECT_TRUE(NavigateToURL(shell(), a_url));
Lukasz Anforowicz5a92f052018-08-15 20:38:4712683 RenderProcessHost* a_process =
Dave Tapuska327c06c92022-06-13 20:31:5112684 shell()->web_contents()->GetPrimaryMainFrame()->GetProcess();
Lukasz Anforowicz5a92f052018-08-15 20:38:4712685
Lukasz Anforowicz4f0593d2018-09-21 18:33:5412686 // Open b.com in a second tab. Using a renderer-initiated navigation is
12687 // important to leave a.com and b.com SiteInstances in the same
Lukasz Anforowicz5a92f052018-08-15 20:38:4712688 // BrowsingInstance (so the b.com -> a.com navigation in the next test step
12689 // will reuse the process associated with the first a.com tab).
Lukasz Anforowicz4f0593d2018-09-21 18:33:5412690 GURL b_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
12691 Shell* new_shell = OpenPopup(shell()->web_contents(), b_url, "newtab");
12692 WebContents* new_contents = new_shell->web_contents();
12693 EXPECT_TRUE(WaitForLoadStop(new_contents));
Dave Tapuska327c06c92022-06-13 20:31:5112694 RenderProcessHost* b_process =
12695 new_contents->GetPrimaryMainFrame()->GetProcess();
Lukasz Anforowicz5a92f052018-08-15 20:38:4712696 EXPECT_NE(a_process, b_process);
12697
12698 // Hang the first tab's renderer.
12699 const char* kHungScript = "setTimeout(function() { for (;;) {}; }, 0);";
Avi Drissmanc91bd8e2021-04-19 23:58:4412700 EXPECT_TRUE(ExecJs(shell()->web_contents(), kHungScript));
Lukasz Anforowicz5a92f052018-08-15 20:38:4712701
12702 // Attempt to navigate the second tab to a.com. This will attempt to reuse
12703 // the hung process.
Peter Kastinge5a38ed2021-10-02 03:06:3512704 NavigationRequest::SetCommitTimeoutForTesting(base::Milliseconds(100));
Lukasz Anforowicz4f0593d2018-09-21 18:33:5412705 GURL hung_url(embedded_test_server()->GetURL("a.com", "/title3.html"));
12706 UnresponsiveRendererObserver unresponsive_renderer_observer(new_contents);
12707 EXPECT_TRUE(
12708 ExecJs(new_contents, JsReplace("window.location = $1", hung_url)));
Lukasz Anforowicz5a92f052018-08-15 20:38:4712709
12710 // Verify that we will be notified about the unresponsive renderer. Before
12711 // changes in https://crrev.com/c/1089797, the test would hang here forever.
12712 RenderProcessHost* hung_process = unresponsive_renderer_observer.Wait();
12713 EXPECT_EQ(hung_process, a_process);
12714
12715 // Reset the timeout.
Mohamed Abdelhalim8adf3f62019-07-31 12:40:3612716 NavigationRequest::SetCommitTimeoutForTesting(base::TimeDelta());
Lukasz Anforowicz5a92f052018-08-15 20:38:4712717}
12718
Lukasz Anforowicz4f0593d2018-09-21 18:33:5412719// This is a regression test for https://crbug.com/881812 which complained that
12720// the hung renderer dialog used to undesirably show up for background tabs
12721// (typically during session restore when many navigations would be happening in
12722// backgrounded processes).
Georg Neis0dce3332024-12-13 01:51:1812723// TODO(crbug.com/40196588): Flaky on Mac and Windows.
12724#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
Lukasz Anforowicze7ac6df2021-09-07 18:58:0712725#define MAYBE_NoCommitTimeoutForInvisibleWebContents \
12726 DISABLED_NoCommitTimeoutForInvisibleWebContents
12727#else
12728#define MAYBE_NoCommitTimeoutForInvisibleWebContents \
12729 NoCommitTimeoutForInvisibleWebContents
12730#endif
Fergal Daly2e7e1e12020-06-24 09:18:2812731IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Lukasz Anforowicze7ac6df2021-09-07 18:58:0712732 MAYBE_NoCommitTimeoutForInvisibleWebContents) {
Lukasz Anforowicz4f0593d2018-09-21 18:33:5412733 // Navigate first tab to a.com.
12734 GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
12735 EXPECT_TRUE(NavigateToURL(shell(), a_url));
12736 RenderProcessHost* a_process =
Dave Tapuska327c06c92022-06-13 20:31:5112737 shell()->web_contents()->GetPrimaryMainFrame()->GetProcess();
Lukasz Anforowicz4f0593d2018-09-21 18:33:5412738
12739 // Open b.com in a second tab. Using a renderer-initiated navigation is
12740 // important to leave a.com and b.com SiteInstances in the same
12741 // BrowsingInstance (so the b.com -> a.com navigation in the next test step
12742 // will reuse the process associated with the first a.com tab).
12743 GURL b_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
12744 Shell* new_shell = OpenPopup(shell()->web_contents(), b_url, "newtab");
12745 WebContents* new_contents = new_shell->web_contents();
12746 EXPECT_TRUE(WaitForLoadStop(new_contents));
Dave Tapuska327c06c92022-06-13 20:31:5112747 RenderProcessHost* b_process =
12748 new_contents->GetPrimaryMainFrame()->GetProcess();
Lukasz Anforowicz4f0593d2018-09-21 18:33:5412749 EXPECT_NE(a_process, b_process);
12750
12751 // Hang the first tab's renderer.
12752 const char* kHungScript = "setTimeout(function() { for (;;) {}; }, 0);";
Avi Drissmanc91bd8e2021-04-19 23:58:4412753 EXPECT_TRUE(ExecJs(shell()->web_contents(), kHungScript));
Lukasz Anforowicz4f0593d2018-09-21 18:33:5412754
12755 // Hide the second tab. This should prevent reporting of hangs in this tab
12756 // (see https://crbug.com/881812).
12757 new_contents->WasHidden();
12758 EXPECT_EQ(Visibility::HIDDEN, new_contents->GetVisibility());
12759
12760 // Attempt to navigate the second tab to a.com. This will attempt to reuse
12761 // the hung process.
Peter Kastinge5a38ed2021-10-02 03:06:3512762 base::TimeDelta kTimeout = base::Milliseconds(100);
Mohamed Abdelhalim8adf3f62019-07-31 12:40:3612763 NavigationRequest::SetCommitTimeoutForTesting(kTimeout);
Lukasz Anforowicz4f0593d2018-09-21 18:33:5412764 GURL hung_url(embedded_test_server()->GetURL("a.com", "/title3.html"));
12765 UnresponsiveRendererObserver unresponsive_renderer_observer(new_contents);
12766 EXPECT_TRUE(
12767 ExecJs(new_contents, JsReplace("window.location = $1", hung_url)));
12768
12769 // Verify that we will not be notified about the unresponsive renderer.
12770 // Before changes in https://crrev.com/c/1089797, the test would get notified
12771 // and therefore |hung_process| would be non-null.
12772 RenderProcessHost* hung_process =
12773 unresponsive_renderer_observer.Wait(kTimeout * 10);
12774 EXPECT_FALSE(hung_process);
12775
12776 // Reset the timeout.
Mohamed Abdelhalim8adf3f62019-07-31 12:40:3612777 NavigationRequest::SetCommitTimeoutForTesting(base::TimeDelta());
Lukasz Anforowicz4f0593d2018-09-21 18:33:5412778}
12779
Lucas Furukawa Gadanif9ea4fc2018-08-03 23:57:1312780// Tests that an inner WebContents will reattach to its outer WebContents after
12781// a navigation that causes a process swap.
Fergal Daly2e7e1e12020-06-24 09:18:2812782IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, ProcessSwapOnInnerContents) {
Lucas Furukawa Gadanif9ea4fc2018-08-03 23:57:1312783 GURL main_url(embedded_test_server()->GetURL(
12784 "a.com", "/cross_site_iframe_factory.html?a(a)"));
12785 EXPECT_TRUE(NavigateToURL(shell(), main_url));
12786
12787 FrameTreeNode* child_frame =
Carlos Caballero15caeeb2021-10-27 09:57:5512788 web_contents()->GetPrimaryFrameTree().root()->child_at(0);
Lucas Furukawa Gadanif9ea4fc2018-08-03 23:57:1312789 WebContentsImpl* inner_contents =
12790 static_cast<WebContentsImpl*>(CreateAndAttachInnerContents(
12791 ToRenderFrameHost(child_frame).render_frame_host()));
Carlos Caballero15caeeb2021-10-27 09:57:5512792 FrameTreeNode* inner_contents_root =
12793 inner_contents->GetPrimaryFrameTree().root();
Lucas Furukawa Gadanif9ea4fc2018-08-03 23:57:1312794 RenderFrameProxyHost* outer_proxy =
12795 inner_contents_root->render_manager()->GetProxyToOuterDelegate();
12796 CrossProcessFrameConnector* outer_connector =
12797 outer_proxy->cross_process_frame_connector();
12798 EXPECT_NE(nullptr, outer_connector->get_view_for_testing());
12799
12800 GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2012801 EXPECT_TRUE(NavigateToURLFromRenderer(inner_contents_root, a_url));
Lucas Furukawa Gadanif9ea4fc2018-08-03 23:57:1312802 SiteInstance* a_site_instance =
Dave Tapuska327c06c92022-06-13 20:31:5112803 inner_contents->GetPrimaryMainFrame()->GetSiteInstance();
Lucas Furukawa Gadanif9ea4fc2018-08-03 23:57:1312804 RenderProcessHost* a_process = a_site_instance->GetProcess();
12805 RenderWidgetHostViewChildFrame* a_view =
12806 outer_connector->get_view_for_testing();
12807
12808 GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2012809 EXPECT_TRUE(NavigateToURLFromRenderer(inner_contents_root, b_url));
Lucas Furukawa Gadanif9ea4fc2018-08-03 23:57:1312810 SiteInstance* b_site_instance =
Dave Tapuska327c06c92022-06-13 20:31:5112811 inner_contents->GetPrimaryMainFrame()->GetSiteInstance();
Lucas Furukawa Gadanif9ea4fc2018-08-03 23:57:1312812 RenderProcessHost* b_process = b_site_instance->GetProcess();
12813 RenderWidgetHostViewChildFrame* b_view =
12814 outer_connector->get_view_for_testing();
12815
12816 // Ensure that the SiteInstances have changed, we've completed a process swap
12817 // and reattached the inner WebContents creating a new RenderWidgetHostView.
12818 EXPECT_NE(a_site_instance, b_site_instance);
12819 EXPECT_NE(a_process, b_process);
12820 EXPECT_NE(nullptr, a_view);
12821 EXPECT_NE(nullptr, b_view);
12822 EXPECT_NE(a_view, b_view);
12823}
12824
Alex Moshchukc4f0a722019-11-19 22:41:2812825// This test ensures that WebContentsImpl::FocusOwningWebContents() focuses an
12826// inner WebContents when it is given an OOPIF's RenderWidgetHost inside that
12827// inner WebContents. This setup isn't currently supported in Chrome
12828// (requiring issue 614463), but it can happen in embedders. See
12829// https://crbug.com/1026056.
Fergal Daly2e7e1e12020-06-24 09:18:2812830IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, FocusInnerContentsFromOOPIF) {
Alex Moshchukc4f0a722019-11-19 22:41:2812831 GURL main_url(embedded_test_server()->GetURL(
12832 "a.com", "/cross_site_iframe_factory.html?a(a)"));
12833 EXPECT_TRUE(NavigateToURL(shell(), main_url));
12834
12835 // Set up and attach an artificial inner WebContents.
12836 FrameTreeNode* child_frame =
Carlos Caballero15caeeb2021-10-27 09:57:5512837 web_contents()->GetPrimaryFrameTree().root()->child_at(0);
Alex Moshchukc4f0a722019-11-19 22:41:2812838 WebContentsImpl* inner_contents =
12839 static_cast<WebContentsImpl*>(CreateAndAttachInnerContents(
12840 ToRenderFrameHost(child_frame).render_frame_host()));
Carlos Caballero15caeeb2021-10-27 09:57:5512841 FrameTreeNode* inner_contents_root =
12842 inner_contents->GetPrimaryFrameTree().root();
Alex Moshchukc4f0a722019-11-19 22:41:2812843
12844 // Navigate inner WebContents to b.com, and then navigate a subframe on that
12845 // page to c.com.
12846 GURL b_url(embedded_test_server()->GetURL(
12847 "b.com", "/cross_site_iframe_factory.html?b(b)"));
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2012848 EXPECT_TRUE(NavigateToURLFromRenderer(inner_contents_root, b_url));
Alex Moshchukc4f0a722019-11-19 22:41:2812849 GURL c_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
12850 FrameTreeNode* inner_child = inner_contents_root->child_at(0);
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2012851 EXPECT_TRUE(NavigateToURLFromRenderer(inner_child, c_url));
Alex Moshchukc4f0a722019-11-19 22:41:2812852
12853 // Because |inner_contents| was set up without kGuestScheme, it can actually
12854 // have OOPIFs. Ensure that the subframe is in an OOPIF.
12855 EXPECT_NE(inner_contents_root->current_frame_host()->GetSiteInstance(),
12856 inner_child->current_frame_host()->GetSiteInstance());
12857 EXPECT_TRUE(inner_child->current_frame_host()->IsCrossProcessSubframe());
12858
12859 // Make sure the outer WebContents is focused to start with.
12860 web_contents()->Focus();
12861 web_contents()->SetAsFocusedWebContentsIfNecessary();
12862 EXPECT_EQ(web_contents(), web_contents()->GetFocusedWebContents());
12863
12864 // Focus the inner WebContents as if an event were received and dispatched
12865 // directly on the |inner_child|'s RenderWidgetHost, and ensure that this
12866 // took effect.
12867 inner_contents->FocusOwningWebContents(
12868 inner_child->current_frame_host()->GetRenderWidgetHost());
12869 EXPECT_EQ(inner_contents, web_contents()->GetFocusedWebContents());
12870}
12871
Alex Moshchuk5f8671e2018-10-19 02:10:1112872// Check that a web frame can't navigate a remote subframe to a file: URL. The
12873// frame should stay at the old URL, and the navigation attempt should produce
12874// a console error message. See https://crbug.com/894399.
Fergal Daly2e7e1e12020-06-24 09:18:2812875IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Alex Moshchuk5f8671e2018-10-19 02:10:1112876 FileURLBlockedWithConsoleErrorInRemoteFrameNavigation) {
12877 GURL main_url(embedded_test_server()->GetURL(
12878 "a.com", "/cross_site_iframe_factory.html?a(b)"));
12879 EXPECT_TRUE(NavigateToURL(shell(), main_url));
12880
Carlos Caballero15caeeb2021-10-27 09:57:5512881 FrameTreeNode* child =
12882 web_contents()->GetPrimaryFrameTree().root()->child_at(0);
Alex Moshchuk5f8671e2018-10-19 02:10:1112883 GURL original_frame_url(child->current_frame_host()->GetLastCommittedURL());
12884 EXPECT_EQ("b.com", original_frame_url.host());
12885
Devlin Cronined376d82020-05-01 18:37:5512886 WebContentsConsoleObserver console_observer(web_contents());
12887 console_observer.SetPattern("Not allowed to load local resource: file:*");
Alex Moshchuk5f8671e2018-10-19 02:10:1112888
12889 GURL file_url("file:///");
12890 EXPECT_TRUE(
12891 ExecJs(web_contents(),
12892 JsReplace("document.querySelector('iframe').src = $1", file_url)));
Fergal Daly7723f9d2022-10-29 07:03:1312893 ASSERT_TRUE(console_observer.Wait());
Alex Moshchuk5f8671e2018-10-19 02:10:1112894
12895 // The iframe should've stayed at the original URL.
12896 EXPECT_EQ(original_frame_url,
12897 child->current_frame_host()->GetLastCommittedURL());
12898}
12899
W. James MacLeand973a55b2018-11-29 21:39:1312900// Touchscreen DoubleTapZoom is only supported on Android & ChromeOS at present.
Georg Neis35ff854b2024-12-17 02:02:0812901#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
Rakina Zata Amni347b70902020-07-22 10:49:0412902// A test ContentBrowserClient implementation which enforces
12903// WebPreferences' |double_tap_to_zoom_enabled| to be true.
Scott Violet99861992023-02-08 01:20:1212904class DoubleTapZoomContentBrowserClient
12905 : public ContentBrowserTestContentBrowserClient {
Rakina Zata Amni347b70902020-07-22 10:49:0412906 public:
12907 DoubleTapZoomContentBrowserClient() = default;
W. James MacLeand973a55b2018-11-29 21:39:1312908
Peter Boström9b036532021-10-28 23:37:2812909 DoubleTapZoomContentBrowserClient(const DoubleTapZoomContentBrowserClient&) =
12910 delete;
12911 DoubleTapZoomContentBrowserClient& operator=(
12912 const DoubleTapZoomContentBrowserClient&) = delete;
12913
Dave Tapuska183b8d702025-01-07 18:47:1812914 void OverrideWebPreferences(
Aaron Colwellbd02c6c2021-01-16 00:34:2912915 content::WebContents* web_contents,
Dave Tapuska183b8d702025-01-07 18:47:1812916 SiteInstance& main_frame_site,
Gyuyoung Kim1ac4ca782020-09-11 03:32:5112917 blink::web_pref::WebPreferences* web_prefs) override {
Rakina Zata Amni347b70902020-07-22 10:49:0412918 web_prefs->double_tap_to_zoom_enabled = true;
12919 }
Rakina Zata Amni347b70902020-07-22 10:49:0412920};
W. James MacLeand973a55b2018-11-29 21:39:1312921
Fergal Daly2e7e1e12020-06-24 09:18:2812922IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
W. James MacLeand973a55b2018-11-29 21:39:1312923 TouchscreenAnimateDoubleTapZoomInOOPIF) {
Rakina Zata Amni347b70902020-07-22 10:49:0412924 // Install a client forcing double-tap zoom to be enabled.
12925 DoubleTapZoomContentBrowserClient content_browser_client;
Rakina Zata Amni4029b6d2020-07-28 02:36:2012926 web_contents()->OnWebPreferencesChanged();
Rakina Zata Amni347b70902020-07-22 10:49:0412927
W. James MacLeand973a55b2018-11-29 21:39:1312928 GURL main_url(embedded_test_server()->GetURL(
12929 "a.com", "/cross_site_iframe_factory.html?a(b)"));
12930 EXPECT_TRUE(NavigateToURL(shell(), main_url));
12931
Carlos Caballero15caeeb2021-10-27 09:57:5512932 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
W. James MacLeand973a55b2018-11-29 21:39:1312933 ASSERT_EQ(1u, root->child_count());
12934 FrameTreeNode* child_b = root->child_at(0);
12935 ASSERT_TRUE(child_b);
12936
W. James MacLeand973a55b2018-11-29 21:39:1312937 RenderFrameSubmissionObserver observer_a(root);
12938 // We need to observe a root frame submission to pick up the initial page
12939 // scale factor.
12940 observer_a.WaitForAnyFrameSubmission();
12941 float original_page_scale =
12942 observer_a.LastRenderFrameMetadata().page_scale_factor;
12943
12944 // Must do this before it's safe to use the coordinate transform functions.
kylechara7c549b2019-07-29 17:47:2812945 WaitForHitTestData(child_b->current_frame_host());
W. James MacLeand973a55b2018-11-29 21:39:1312946
12947 // Select a tap point inside the OOPIF.
12948 gfx::PointF tap_position =
12949 child_b->current_frame_host()
12950 ->GetRenderWidgetHost()
12951 ->GetView()
12952 ->TransformPointToRootCoordSpaceF(gfx::PointF(10, 10));
12953
12954 // Generate a double-tap.
Peter Kasting4524c8392023-10-17 15:55:0812955 static constexpr char kActionsTemplate[] = R"HTML(
W. James MacLeand973a55b2018-11-29 21:39:1312956 [{
12957 "source" : "touch",
12958 "actions" : [
12959 { "name": "pointerDown", "x": %f, "y": %f},
12960 { "name": "pointerUp"},
Lan Weia80bbf22019-02-22 02:08:1512961 { "name": "pause", "duration": 50 },
W. James MacLeand973a55b2018-11-29 21:39:1312962 { "name": "pointerDown", "x": %f, "y": %f},
12963 { "name": "pointerUp"}
12964 ]
12965 }]
12966 )HTML";
12967 std::string double_tap_actions_json =
Peter Kasting4524c8392023-10-17 15:55:0812968 base::StringPrintf(kActionsTemplate, tap_position.x(), tap_position.y(),
12969 tap_position.x(), tap_position.y());
Claudio DeSouza8e71b8672022-06-25 12:00:4812970 auto parsed_json =
Nigel Taoe5d46d12020-06-04 10:10:4912971 base::JSONReader::ReadAndReturnValueWithError(double_tap_actions_json);
Claudio DeSouza8e71b8672022-06-25 12:00:4812972 ASSERT_TRUE(parsed_json.has_value()) << parsed_json.error().message;
12973 ActionsParser actions_parser(std::move(*parsed_json));
W. James MacLeand973a55b2018-11-29 21:39:1312974
Lan Wei78f45262021-01-27 15:43:4112975 ASSERT_TRUE(actions_parser.Parse());
David Bokan6d776aa2023-07-14 21:13:3812976 auto synthetic_gesture_doubletap = std::make_unique<SyntheticPointerAction>(
12977 actions_parser.pointer_action_params());
W. James MacLeand973a55b2018-11-29 21:39:1312978
12979 // Queue the event and wait for it to be acked.
12980 InputEventAckWaiter ack_waiter(
12981 child_b->current_frame_host()->GetRenderWidgetHost(),
Dave Tapuska347d60a2020-04-21 23:55:4712982 blink::WebInputEvent::Type::kGestureDoubleTap);
W. James MacLeand973a55b2018-11-29 21:39:1312983 auto* host = static_cast<RenderWidgetHostImpl*>(
12984 root->current_frame_host()->GetRenderWidgetHost());
12985 host->QueueSyntheticGesture(
12986 std::move(synthetic_gesture_doubletap),
12987 base::BindOnce([](SyntheticGesture::Result result) {
12988 EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
12989 }));
12990 // Waiting for the ack on the child frame ensures the event actually routed
12991 // through the oopif.
12992 ack_waiter.Wait();
12993
12994 // Wait for page scale to change. We'll assume the OOPIF is scaled up by
12995 // at least 10%.
12996 float target_scale = 1.1f * original_page_scale;
12997 float new_page_scale = original_page_scale;
12998 do {
12999 observer_a.WaitForAnyFrameSubmission();
13000 new_page_scale = observer_a.LastRenderFrameMetadata().page_scale_factor;
13001 } while (new_page_scale < target_scale);
13002}
Georg Neis35ff854b2024-12-17 02:02:0813003#endif // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
W. James MacLeand973a55b2018-11-29 21:39:1313004
Ehsan Karamad192a8da2018-10-21 03:48:0813005class CrossProcessNavigationObjectElementTest
Fergal Daly2e7e1e12020-06-24 09:18:2813006 : public SitePerProcessBrowserTestBase,
Ehsan Karamad192a8da2018-10-21 03:48:0813007 public testing::WithParamInterface<
13008 std::tuple<std::string, std::string, std::string>> {};
13009
13010// This test verifies the correctness of rendering fallback in <object> when the
13011// a cross-origin navigation leads to a 404 error. Assuming the page's origin
13012// is "a.com", the test cases are:
13013// 1- Navigating an <object> from "a.com" to invalid "b.com" resource. In this
13014// case the load fails for a provisional frame and at that time there is no
13015// proxy to parent.
13016// 2- Navigating an <object> from "b.com" to invalid "b.com". Since navigation
13017// is not cross-origin the failure happens for a non-provisional frame.
13018// 3- Navigation an <object> from "b.com" to invalid "c.com". The load fails for
13019// a provisional frame, and at that time there is a proxy to parent.
13020IN_PROC_BROWSER_TEST_P(CrossProcessNavigationObjectElementTest, FallbackShown) {
13021 const GURL main_url = embedded_test_server()->GetURL(
13022 base::StringPrintf("%s.com", std::get<0>(GetParam()).c_str()),
13023 "/page_with_object_fallback.html");
13024 const GURL object_valid_url = embedded_test_server()->GetURL(
13025 base::StringPrintf("%s.com", std::get<1>(GetParam()).c_str()),
13026 "/title1.html");
13027 const GURL object_invalid_url = embedded_test_server()->GetURL(
13028 base::StringPrintf("%s.com", std::get<2>(GetParam()).c_str()),
13029 "/does-not-exist-throws-404.html");
13030
13031 ASSERT_TRUE(NavigateToURL(shell(), main_url));
13032
13033 // Load the contents of <object> (first navigation which is to a valid
13034 // existing resource) and wait for 'load' event on <object>.
Chris Fredricksonb854bbf2023-03-27 17:27:4413035 ASSERT_EQ("OBJECT_LOAD",
13036 EvalJs(web_contents(), JsReplace("setUrl($1);", object_valid_url)));
Ehsan Karamad192a8da2018-10-21 03:48:0813037
13038 // Verify fallback content is not shown.
Chris Fredricksonb854bbf2023-03-27 17:27:4413039 ASSERT_EQ(false, EvalJs(web_contents(), "fallbackVisible()"));
Ehsan Karamad192a8da2018-10-21 03:48:0813040
13041 // Navigate the <object>'s frame to invalid origin. Make sure we do not report
13042 // the 'load' event (the 404 content loads inside the <object>'s frame and the
13043 // 'load' event might fire before fallback is detected).
Chris Fredricksonb854bbf2023-03-27 17:27:4413044 ASSERT_EQ(true, EvalJs(web_contents(), JsReplace("setUrl($1);"
13045 "notifyWhenFallbackShown();",
13046 object_invalid_url)));
Ehsan Karamad192a8da2018-10-21 03:48:0813047}
13048
Victor Costanaa7b61f2019-02-13 07:57:5713049INSTANTIATE_TEST_SUITE_P(SitePerProcess,
13050 CrossProcessNavigationObjectElementTest,
13051 testing::Values(std::make_tuple("a", "a", "b"),
13052 std::make_tuple("a", "b", "b"),
13053 std::make_tuple("a", "b", "c")));
Ehsan Karamad192a8da2018-10-21 03:48:0813054
Xiaohan Wang1ecfd002022-01-19 22:33:1013055#if !BUILDFLAG(IS_ANDROID)
Ehsan Karamada03e1582018-12-05 23:07:5613056// This test verifies that after occluding a WebContents the RAF inside a
13057// cross-process child frame is throttled.
Lei Zhangd54ea8a2022-02-05 05:10:0513058// Disabled due to flakiness. crbug.com/1293207
Fergal Daly2e7e1e12020-06-24 09:18:2813059IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Lei Zhange8d3517f2022-02-08 21:58:3513060 DISABLED_OccludedRenderWidgetThrottlesRAF) {
Ehsan Karamada03e1582018-12-05 23:07:5613061 GURL main_url(embedded_test_server()->GetURL(
13062 "a.com", "/cross_site_iframe_factory.html?a(b)"));
13063 EXPECT_TRUE(NavigateToURL(shell(), main_url));
Carlos Caballero15caeeb2021-10-27 09:57:5513064 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Ehsan Karamada03e1582018-12-05 23:07:5613065 FrameTreeNode* subframe = root->child_at(0);
13066 GURL page_with_raf_counter =
13067 embedded_test_server()->GetURL("a.com", "/page_with_raf_counter.html");
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2013068 EXPECT_TRUE(NavigateToURLFromRenderer(subframe, page_with_raf_counter));
Ehsan Karamada03e1582018-12-05 23:07:5613069
13070 // Initially page is visible - wait some time and then ensure a good number of
Fergal Daly15100dc2020-07-01 05:18:3313071 // rafs have been generated. On Mac the number of RAFs that occur in 500ms is
13072 // quite low, see https://crbug.com/1098715.
13073 auto allow_time_for_rafs = []() {
Ehsan Karamada03e1582018-12-05 23:07:5613074 base::RunLoop run_loop;
Sean Maher5b9af51f2022-11-21 15:32:4713075 base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
Peter Kastinge5a38ed2021-10-02 03:06:3513076 FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(1000));
Ehsan Karamada03e1582018-12-05 23:07:5613077 run_loop.Run();
13078 };
13079
Avi Drissmanc91bd8e2021-04-19 23:58:4413080 ASSERT_TRUE(ExecJs(subframe, "reset_count();"));
Fergal Daly15100dc2020-07-01 05:18:3313081 allow_time_for_rafs();
Ehsan Karamada03e1582018-12-05 23:07:5613082 int32_t default_raf_count = EvalJs(subframe, "raf_count").ExtractInt();
13083 // On a 60 fps we should expect more than 30 counts - however purely for
13084 // sanity checking and avoiding unnecessary flakes adding a comparison for a
13085 // much lower value. This verifies that we did get *some* rAFs.
13086 EXPECT_GT(default_raf_count, 5);
13087 web_contents()->WasOccluded();
Avi Drissmanc91bd8e2021-04-19 23:58:4413088 ASSERT_TRUE(ExecJs(subframe, "reset_count();"));
Fergal Daly15100dc2020-07-01 05:18:3313089 allow_time_for_rafs();
Ehsan Karamada03e1582018-12-05 23:07:5613090 int32_t raf_count = EvalJs(subframe, "raf_count").ExtractInt();
13091 // If the frame is throttled, we should expect 0 rAFs.
13092 EXPECT_EQ(raf_count, 0);
13093 // Sanity-check: unoccluding will reverse the effect.
13094 web_contents()->WasShown();
Avi Drissmanc91bd8e2021-04-19 23:58:4413095 ASSERT_TRUE(ExecJs(subframe, "reset_count();"));
Fergal Daly15100dc2020-07-01 05:18:3313096 allow_time_for_rafs();
Avi Drissmanc91bd8e2021-04-19 23:58:4413097 raf_count = EvalJs(subframe, "raf_count").ExtractInt();
Ehsan Karamada03e1582018-12-05 23:07:5613098 EXPECT_GT(raf_count, 5);
13099}
13100#endif
Lukasz Anforowicz1da30d7c2018-12-21 00:44:2713101
13102// Test that a renderer locked to origin A will be terminated if it tries to
13103// commit a navigation to origin B. See also https://crbug.com/770239.
Fergal Daly2e7e1e12020-06-24 09:18:2813104IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Lukasz Anforowicz1da30d7c2018-12-21 00:44:2713105 CommittedOriginIncompatibleWithOriginLock) {
13106 GURL start_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
13107 EXPECT_TRUE(NavigateToURL(shell(), start_url));
13108 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:5513109 ->GetPrimaryFrameTree()
13110 .root();
Lukasz Anforowicz1da30d7c2018-12-21 00:44:2713111
Daniel Chengfa626cc2020-11-25 10:57:2413112 GURL another_url(embedded_test_server()->GetURL("a.com", "/title2.html"));
13113 const GURL bad_url = GURL("https://b.com");
Lukasz Anforowicz1da30d7c2018-12-21 00:44:2713114
Daniel Chengfa626cc2020-11-25 10:57:2413115 // Sanity check the process lock logic.
Sharon Yang57481e52021-12-01 00:05:2213116 auto process_lock =
13117 root->current_frame_host()->GetProcess()->GetProcessLock();
Daniel Chengfa626cc2020-11-25 10:57:2413118 IsolationContext isolation_context(
13119 shell()->web_contents()->GetBrowserContext());
Sharon Yang2c077a72021-11-30 02:27:5813120 ProcessLock start_url_lock = ProcessLock::FromSiteInfo(
Aaron Colwell9d0f9392021-02-11 21:51:5213121 SiteInfo::CreateForTesting(isolation_context, start_url));
Sharon Yang2c077a72021-11-30 02:27:5813122 ProcessLock another_url_lock = ProcessLock::FromSiteInfo(
Aaron Colwell9d0f9392021-02-11 21:51:5213123 SiteInfo::CreateForTesting(isolation_context, another_url));
Sharon Yang2c077a72021-11-30 02:27:5813124 ProcessLock bad_url_lock = ProcessLock::FromSiteInfo(
Aaron Colwell9d0f9392021-02-11 21:51:5213125 SiteInfo::CreateForTesting(isolation_context, bad_url));
Sharon Yang57481e52021-12-01 00:05:2213126 EXPECT_EQ(start_url_lock, process_lock);
13127 EXPECT_EQ(another_url_lock, process_lock);
13128 EXPECT_NE(bad_url_lock, process_lock);
Nasko Oskovfd52b6f2019-02-06 19:21:1513129
Daniel Chengfa626cc2020-11-25 10:57:2413130 // Leave the commit URL alone, so the URL checks will pass, but change the
13131 // origin to one that does not match the origin lock of the process.
13132 PwnCommitIPC(shell()->web_contents(), another_url, another_url,
13133 url::Origin::Create(bad_url));
Lukasz Anforowicz1da30d7c2018-12-21 00:44:2713134 EXPECT_TRUE(
Daniel Chengfa626cc2020-11-25 10:57:2413135 BeginNavigateToURLFromRenderer(shell()->web_contents(), another_url));
Lukasz Anforowicz1da30d7c2018-12-21 00:44:2713136
Daniel Chengfa626cc2020-11-25 10:57:2413137 // Due to the origin lock mismatch, the render process should be killed when
13138 // it tries to commit.
13139 RenderProcessHostBadIpcMessageWaiter kill_waiter(
13140 root->current_frame_host()->GetProcess());
Lukasz Anforowicz1da30d7c2018-12-21 00:44:2713141 EXPECT_EQ(bad_message::RFH_INVALID_ORIGIN_ON_COMMIT, kill_waiter.Wait());
13142}
13143
Ehsan Karamad830c8fc2019-01-28 18:29:2513144// This test verifies that plugin elements containing cross-process-frames do
13145// not become unresponsive during style changes. (see https://crbug.com/781880).
Fergal Daly2e7e1e12020-06-24 09:18:2813146IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Ehsan Karamad830c8fc2019-01-28 18:29:2513147 PluginElementResponsiveInCrossProcessNavigations) {
13148 GURL main_frame_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
13149 ASSERT_TRUE(NavigateToURL(shell(), main_frame_url));
13150 GURL cross_origin(embedded_test_server()->GetURL("b.com", "/title1.html"));
13151 std::string msg =
Chris Fredricksonb854bbf2023-03-27 17:27:4413152 EvalJs(shell(), JsReplace("var object = document.createElement('object');"
13153 "document.body.appendChild(object);"
13154 "object.data = $1;"
13155 "object.type='text/html';"
13156 "object.notify = true;"
13157 "new Promise(resolve => {"
13158 " object.onload = () => {"
13159 " if (!object.notify) return;"
13160 " object.notify = false;"
13161 " resolve('done');"
13162 " };"
13163 "});",
13164 cross_origin))
Ehsan Karamad830c8fc2019-01-28 18:29:2513165 .ExtractString();
13166 ASSERT_EQ("done", msg);
13167 // To track the frame's visibility an EmbeddedContentView is needed. The
13168 // following steps make sure the visibility is tracked properly on the browser
13169 // side.
13170 auto* frame_connector = web_contents()
Carlos Caballero15caeeb2021-10-27 09:57:5513171 ->GetPrimaryFrameTree()
13172 .root()
Ehsan Karamad830c8fc2019-01-28 18:29:2513173 ->child_at(0)
13174 ->render_manager()
13175 ->GetProxyToParent()
13176 ->cross_process_frame_connector();
13177 ASSERT_FALSE(frame_connector->IsHidden());
13178 ASSERT_TRUE(ExecJs(
13179 shell(), "document.querySelector('object').style.display = 'none';"));
Sonjaae009ec2024-02-01 13:33:2213180 EXPECT_TRUE(
13181 base::test::RunUntil([&]() { return frame_connector->IsHidden(); }));
Ehsan Karamad830c8fc2019-01-28 18:29:2513182 ASSERT_TRUE(ExecJs(
13183 shell(), "document.querySelector('object').style.display = 'block';"));
Sonjaae009ec2024-02-01 13:33:2213184 EXPECT_TRUE(
13185 base::test::RunUntil([&]() { return !frame_connector->IsHidden(); }));
Ehsan Karamad830c8fc2019-01-28 18:29:2513186}
13187
arthursonzogni6aff2922019-02-06 12:59:5613188// Pending navigations must be canceled when a frame becomes pending deletion.
13189//
13190// 1) Initial state: A(B).
Jiacheng Guo7d3174d2025-02-17 10:10:4113191// 2) Navigation from B to C. Pause when the speculative RFH is created.
arthursonzogni6aff2922019-02-06 12:59:5613192// 3) Deletion of B.
Fergal Daly2e7e1e12020-06-24 09:18:2813193IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
arthursonzogni6aff2922019-02-06 12:59:5613194 NavigationCommitInIframePendingDeletionAB) {
13195 GURL url_a(embedded_test_server()->GetURL(
13196 "a.com", "/cross_site_iframe_factory.html?a(b)"));
Jiacheng Guo7d3174d2025-02-17 10:10:4113197 GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html"));
arthursonzogni6aff2922019-02-06 12:59:5613198
13199 // 1) Initial state: A(B).
13200 EXPECT_TRUE(NavigateToURL(shell(), url_a));
Dave Tapuska327c06c92022-06-13 20:31:5113201 RenderFrameHostImpl* rfh_a = web_contents()->GetPrimaryMainFrame();
arthursonzogni6aff2922019-02-06 12:59:5613202 RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
13203
13204 // RFH B has an unload handler.
Miyoung Shin00b48252020-07-27 04:03:2013205 rfh_b->DoNotDeleteForTesting();
arthursonzogni6aff2922019-02-06 12:59:5613206 EXPECT_TRUE(ExecJs(rfh_b, "onunload=function(){}"));
13207
Jiacheng Guo7d3174d2025-02-17 10:10:4113208 // 2) Navigation from B to C. The navigation will be paused
13209 // when the speculative RFH is created.
arthursonzogni6aff2922019-02-06 12:59:5613210 TestNavigationManager navigation_observer(web_contents(), url_c);
13211 EXPECT_TRUE(ExecJs(rfh_b, JsReplace("location.href=$1;", url_c)));
Jiacheng Guo4bdd0be2024-06-11 23:35:2113212 navigation_observer.WaitForSpeculativeRenderFrameHostCreation();
arthursonzogni6aff2922019-02-06 12:59:5613213 RenderFrameHostImpl* rfh_c =
13214 rfh_b->frame_tree_node()->render_manager()->speculative_frame_host();
13215
Sreeja Kamishetty299329ad2021-03-25 14:06:0113216 EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
Sreeja Kamishetty19c72912020-04-30 15:49:3613217 rfh_a->lifecycle_state());
Sreeja Kamishetty299329ad2021-03-25 14:06:0113218 EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
Sreeja Kamishetty19c72912020-04-30 15:49:3613219 rfh_b->lifecycle_state());
Sreeja Kamishetty299329ad2021-03-25 14:06:0113220 EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kSpeculative,
Sreeja Kamishetty19c72912020-04-30 15:49:3613221 rfh_c->lifecycle_state());
arthursonzogni6aff2922019-02-06 12:59:5613222
13223 // 3) Deletion of B. The unload handler takes times to execute.
13224 RenderFrameDeletedObserver delete_b(rfh_b), delete_c(rfh_c);
13225 EXPECT_TRUE(
13226 ExecJs(rfh_a, JsReplace("document.querySelector('iframe').remove();")));
13227 EXPECT_FALSE(delete_b.deleted());
13228 EXPECT_TRUE(delete_c.deleted()); // The speculative RFH is deleted.
Sreeja Kamishetty299329ad2021-03-25 14:06:0113229 EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
Sreeja Kamishetty19c72912020-04-30 15:49:3613230 rfh_a->lifecycle_state());
Sreeja Kamishetty299329ad2021-03-25 14:06:0113231 EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kRunningUnloadHandlers,
Sreeja Kamishetty19c72912020-04-30 15:49:3613232 rfh_b->lifecycle_state());
arthursonzogni6aff2922019-02-06 12:59:5613233
13234 // The navigation has been canceled.
Fergal Daly83bc3cd2023-01-18 00:22:5413235 ASSERT_TRUE(navigation_observer.WaitForNavigationFinished());
arthursonzogni6aff2922019-02-06 12:59:5613236 EXPECT_FALSE(navigation_observer.was_successful());
13237
13238 // |rfh_b| will complete its deletion at some point:
13239 EXPECT_FALSE(delete_b.deleted());
Miyoung Shin00b48252020-07-27 04:03:2013240 rfh_b->DetachForTesting();
arthursonzogni6aff2922019-02-06 12:59:5613241 EXPECT_TRUE(delete_b.deleted());
13242}
13243
13244// Pending navigations must be canceled when a frame becomes pending deletion.
13245//
13246// 1) Initial state: A(B(C)).
Jiacheng Guo7d3174d2025-02-17 10:10:4113247// 2) Navigation from C to D. Pause when the speculative RFH is created.
arthursonzogni6aff2922019-02-06 12:59:5613248// 3) Deletion of B.
Fergal Daly2e7e1e12020-06-24 09:18:2813249IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
arthursonzogni6aff2922019-02-06 12:59:5613250 NavigationCommitInIframePendingDeletionABC) {
13251 GURL url_a(embedded_test_server()->GetURL(
13252 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
Jiacheng Guo7d3174d2025-02-17 10:10:4113253 GURL url_d(embedded_test_server()->GetURL("d.com", "/title1.html"));
arthursonzogni6aff2922019-02-06 12:59:5613254
13255 // 1) Initial state: A(B(C)).
13256 EXPECT_TRUE(NavigateToURL(shell(), url_a));
Dave Tapuska327c06c92022-06-13 20:31:5113257 RenderFrameHostImpl* rfh_a = web_contents()->GetPrimaryMainFrame();
arthursonzogni6aff2922019-02-06 12:59:5613258 RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
13259 RenderFrameHostImpl* rfh_c = rfh_b->child_at(0)->current_frame_host();
13260
Sreeja Kamishettydce0fc62020-05-18 11:29:0613261 // Leave rfh_c in pending deletion state.
13262 LeaveInPendingDeletionState(rfh_c);
arthursonzogni6aff2922019-02-06 12:59:5613263
Jiacheng Guo7d3174d2025-02-17 10:10:4113264 // 2) Navigation from C to D. The navigation will be paused
13265 // when the speculative RFH is created.
arthursonzogni6aff2922019-02-06 12:59:5613266 TestNavigationManager navigation_observer(web_contents(), url_d);
13267 EXPECT_TRUE(ExecJs(rfh_c, JsReplace("location.href=$1;", url_d)));
Jiacheng Guo4bdd0be2024-06-11 23:35:2113268 navigation_observer.WaitForSpeculativeRenderFrameHostCreation();
arthursonzogni6aff2922019-02-06 12:59:5613269 RenderFrameHostImpl* rfh_d =
13270 rfh_c->frame_tree_node()->render_manager()->speculative_frame_host();
13271
Sreeja Kamishetty299329ad2021-03-25 14:06:0113272 EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
Sreeja Kamishetty19c72912020-04-30 15:49:3613273 rfh_a->lifecycle_state());
Sreeja Kamishetty299329ad2021-03-25 14:06:0113274 EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
Sreeja Kamishetty19c72912020-04-30 15:49:3613275 rfh_b->lifecycle_state());
Sreeja Kamishetty299329ad2021-03-25 14:06:0113276 EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
Sreeja Kamishetty19c72912020-04-30 15:49:3613277 rfh_c->lifecycle_state());
Sreeja Kamishetty299329ad2021-03-25 14:06:0113278 EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kSpeculative,
Sreeja Kamishetty19c72912020-04-30 15:49:3613279 rfh_d->lifecycle_state());
arthursonzogni6aff2922019-02-06 12:59:5613280
13281 // 3) Deletion of D. The unload handler takes times to execute.
13282 RenderFrameDeletedObserver delete_b(rfh_b), delete_c(rfh_c), delete_d(rfh_d);
13283 EXPECT_TRUE(
13284 ExecJs(rfh_a, JsReplace("document.querySelector('iframe').remove();")));
13285 EXPECT_FALSE(delete_b.deleted());
13286 EXPECT_FALSE(delete_c.deleted());
13287 EXPECT_TRUE(delete_d.deleted()); // The speculative RFH is deleted.
Sreeja Kamishetty299329ad2021-03-25 14:06:0113288 EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kActive,
Sreeja Kamishetty19c72912020-04-30 15:49:3613289 rfh_a->lifecycle_state());
Sreeja Kamishetty299329ad2021-03-25 14:06:0113290 EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kReadyToBeDeleted,
Sreeja Kamishetty19c72912020-04-30 15:49:3613291 rfh_b->lifecycle_state());
Sreeja Kamishetty600f7842021-04-06 14:27:3113292 EXPECT_EQ(RenderFrameHost::LifecycleState::kPendingDeletion,
13293 rfh_b->GetLifecycleState());
Sreeja Kamishetty299329ad2021-03-25 14:06:0113294 EXPECT_EQ(RenderFrameHostImpl::LifecycleStateImpl::kRunningUnloadHandlers,
Sreeja Kamishetty19c72912020-04-30 15:49:3613295 rfh_c->lifecycle_state());
Sreeja Kamishetty600f7842021-04-06 14:27:3113296 EXPECT_EQ(RenderFrameHost::LifecycleState::kPendingDeletion,
13297 rfh_c->GetLifecycleState());
arthursonzogni6aff2922019-02-06 12:59:5613298
13299 // The navigation has been canceled.
Fergal Daly83bc3cd2023-01-18 00:22:5413300 ASSERT_TRUE(navigation_observer.WaitForNavigationFinished());
arthursonzogni6aff2922019-02-06 12:59:5613301 EXPECT_FALSE(navigation_observer.was_successful());
13302
13303 // |rfh_b| and |rfh_c| will complete their deletion at some point:
13304 EXPECT_FALSE(delete_b.deleted());
13305 EXPECT_FALSE(delete_c.deleted());
Miyoung Shin00b48252020-07-27 04:03:2013306 rfh_c->DetachForTesting();
arthursonzogni6aff2922019-02-06 12:59:5613307 EXPECT_TRUE(delete_b.deleted());
13308 EXPECT_TRUE(delete_c.deleted());
13309}
13310
arthursonzognid9831792019-02-11 10:24:1713311// A same document commit from the renderer process is received while the
13312// RenderFrameHost is pending deletion.
Fergal Daly2e7e1e12020-06-24 09:18:2813313IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
arthursonzognid9831792019-02-11 10:24:1713314 SameDocumentCommitWhilePendingDeletion) {
13315 GURL url(embedded_test_server()->GetURL(
13316 "a.com", "/cross_site_iframe_factory.html?a(b)"));
13317 EXPECT_TRUE(NavigateToURL(shell(), url));
Dave Tapuska327c06c92022-06-13 20:31:5113318 RenderFrameHostImpl* rfh_a = web_contents()->GetPrimaryMainFrame();
arthursonzognid9831792019-02-11 10:24:1713319 RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
13320
13321 // Frame B has a unload handler. The browser process needs to wait before
13322 // deleting it.
13323 EXPECT_TRUE(ExecJs(rfh_b, "onunload=function(){}"));
13324
13325 RenderFrameDeletedObserver deleted_observer(rfh_b);
13326 DidStartNavigationObserver did_start_navigation_observer(web_contents());
13327
13328 // Start a same-document navigation on B.
13329 ExecuteScriptAsync(rfh_b, "location.href='#fragment'");
13330
13331 // Simulate A deleting B.
13332 // It starts before receiving the same-document navigation. The detach ACK is
13333 // received after.
13334 rfh_b->DetachFromProxy();
13335 deleted_observer.WaitUntilDeleted();
13336
13337 // The navigation was ignored.
13338 EXPECT_FALSE(did_start_navigation_observer.observed());
13339}
13340
arthursonzogni03f76152019-02-12 10:35:2013341// An history navigation from the renderer process is received while the
13342// RenderFrameHost is pending deletion.
Fergal Daly2e7e1e12020-06-24 09:18:2813343IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
arthursonzogni03f76152019-02-12 10:35:2013344 HistoryNavigationWhilePendingDeletion) {
13345 GURL url_ab(embedded_test_server()->GetURL(
13346 "a.com", "/cross_site_iframe_factory.html?a(b)"));
13347 GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html"));
13348
13349 EXPECT_TRUE(NavigateToURL(shell(), url_ab));
Dave Tapuska327c06c92022-06-13 20:31:5113350 RenderFrameHostImpl* rfh_a = web_contents()->GetPrimaryMainFrame();
arthursonzogni03f76152019-02-12 10:35:2013351 RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2013352 EXPECT_TRUE(NavigateToURLFromRenderer(rfh_b->frame_tree_node(), url_c));
arthursonzogni03f76152019-02-12 10:35:2013353 RenderFrameHostImpl* rfh_c = rfh_a->child_at(0)->current_frame_host();
13354
Alex Moshchuk3a4e77a2020-05-29 21:32:5713355 // Set a value in rfh_a that we'll check later to ensure we didn't
13356 // incorrectly reload it.
13357 EXPECT_TRUE(ExecJs(rfh_a, "window.foo='bar';"));
13358
arthursonzogni03f76152019-02-12 10:35:2013359 // Frame C has a unload handler. The browser process needs to wait before
13360 // deleting it.
13361 EXPECT_TRUE(ExecJs(rfh_c, "onunload=function(){}"));
13362
13363 RenderFrameDeletedObserver deleted_observer(rfh_c);
arthursonzogni03f76152019-02-12 10:35:2013364
13365 // History navigation on C.
13366 ExecuteScriptAsync(rfh_c, "history.back();");
13367
13368 // Simulate A deleting C.
13369 // It starts before receiving the history navigation. The detach ACK is
13370 // received after.
13371 rfh_c->DetachFromProxy();
13372 deleted_observer.WaitUntilDeleted();
13373
13374 // The NavigationController won't be able to find the subframe to navigate
Alex Moshchuk3a4e77a2020-05-29 21:32:5713375 // since it was just detached, so it should cancel the history navigation and
13376 // not reload the main page. Verify this by waiting for any pending
13377 // navigation (there shouldn't be any) and checking that JavaScript state in
13378 // rfh_a hasn't changed. Note that because we've waited for rfh_c to be
13379 // deleted, we know that the browser process has already received an ack for
13380 // completion of its unload handler, and thus it has also processed the
13381 // preceding history.back() IPC.
13382 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
13383 EXPECT_EQ("bar", EvalJs(rfh_a, "window.foo"));
arthursonzogni03f76152019-02-12 10:35:2013384}
13385
arthursonzognib3a21182019-02-18 16:35:5113386// One frame navigates using window.open while it is pending deletion. The two
13387// frames lives in different processes.
13388// See https://crbug.com/932087.
Fergal Daly2e7e1e12020-06-24 09:18:2813389IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
arthursonzognib3a21182019-02-18 16:35:5113390 OpenUrlToRemoteFramePendingDeletion) {
13391 GURL url_ab(embedded_test_server()->GetURL(
13392 "a.com", "/cross_site_iframe_factory.html?a(b)"));
13393 GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html"));
13394
13395 EXPECT_TRUE(NavigateToURL(shell(), url_ab));
Dave Tapuska327c06c92022-06-13 20:31:5113396 RenderFrameHostImpl* rfh_a = web_contents()->GetPrimaryMainFrame();
arthursonzognib3a21182019-02-18 16:35:5113397 RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
13398
13399 // Frame B has a unload handler. The browser process needs to wait before
13400 // deleting it.
13401 EXPECT_TRUE(ExecJs(rfh_b, "onunload=function(){}"));
13402 RenderFrameDeletedObserver deleted_observer(rfh_b);
13403
13404 // window.open from A in B to url_c.
13405 DidStartNavigationObserver did_start_navigation_observer(web_contents());
Avi Drissmanc91bd8e2021-04-19 23:58:4413406 EXPECT_TRUE(ExecJs(rfh_b, "window.name = 'name';"));
arthursonzognib3a21182019-02-18 16:35:5113407 ExecuteScriptAsync(rfh_a, JsReplace("window.open($1, 'name');", url_c));
13408
13409 // Simulate A deleting C.
13410 // It starts before receiving the navigation. The detach ACK is
13411 // received after.
13412 rfh_b->DetachFromProxy();
13413 deleted_observer.WaitUntilDeleted();
13414
13415 EXPECT_FALSE(did_start_navigation_observer.observed());
13416}
13417
Alex Moshchuk77beeff92019-05-29 22:14:4713418// Check that if a frame starts a navigation, and the frame's current process
13419// dies before the response for the navigation comes back, the response will
13420// not trigger a process kill and will be allowed to commit in a new process.
13421// See https://crbug.com/968259.
Arthur Hemerydf1cf0bd2019-08-02 09:49:2413422// Note: This test needs to do a browser-initiated navigation because doing
13423// a renderer-initiated navigation would lead to the navigation being canceled.
Nasko Oskovd22f9bf872020-12-23 00:31:3513424// This behavior change has been introduced when navigation moved to use Mojo
13425// IPCs and is documented here https://crbug.com/988368.
Fergal Daly2e7e1e12020-06-24 09:18:2813426IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Alex Moshchuk77beeff92019-05-29 22:14:4713427 ProcessDiesBeforeCrossSiteNavigationCompletes) {
13428 GURL first_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
13429 EXPECT_TRUE(NavigateToURL(shell(), first_url));
13430 scoped_refptr<SiteInstanceImpl> first_site_instance(
Dave Tapuska327c06c92022-06-13 20:31:5113431 web_contents()->GetPrimaryMainFrame()->GetSiteInstance());
Alex Moshchuk77beeff92019-05-29 22:14:4713432
13433 // Start a cross-site navigation and proceed only up to the request start.
13434 GURL second_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
13435 TestNavigationManager delayer(web_contents(), second_url);
Arthur Hemerydf1cf0bd2019-08-02 09:49:2413436 web_contents()->GetController().LoadURL(
13437 second_url, Referrer(), ui::PageTransition::PAGE_TRANSITION_TYPED,
13438 std::string());
Alex Moshchuk77beeff92019-05-29 22:14:4713439 EXPECT_TRUE(delayer.WaitForRequestStart());
13440
13441 // Terminate the current a.com process.
13442 RenderProcessHost* first_process =
Dave Tapuska327c06c92022-06-13 20:31:5113443 web_contents()->GetPrimaryMainFrame()->GetProcess();
Alex Moshchuk77beeff92019-05-29 22:14:4713444 RenderProcessHostWatcher crash_observer(
13445 first_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
13446 EXPECT_TRUE(first_process->Shutdown(0));
13447 crash_observer.Wait();
Dave Tapuska327c06c92022-06-13 20:31:5113448 EXPECT_FALSE(web_contents()->GetPrimaryMainFrame()->IsRenderFrameLive());
Alex Moshchuk77beeff92019-05-29 22:14:4713449
13450 // Resume the cross-site navigation and ensure it commits in a new
13451 // SiteInstance and process.
Fergal Daly83bc3cd2023-01-18 00:22:5413452 ASSERT_TRUE(delayer.WaitForNavigationFinished());
Dave Tapuska327c06c92022-06-13 20:31:5113453 EXPECT_TRUE(web_contents()->GetPrimaryMainFrame()->IsRenderFrameLive());
13454 EXPECT_NE(web_contents()->GetPrimaryMainFrame()->GetProcess(), first_process);
13455 EXPECT_NE(web_contents()->GetPrimaryMainFrame()->GetSiteInstance(),
Alex Moshchuk77beeff92019-05-29 22:14:4713456 first_site_instance);
Dave Tapuska327c06c92022-06-13 20:31:5113457 EXPECT_EQ(second_url,
13458 web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL());
Alex Moshchuk77beeff92019-05-29 22:14:4713459}
13460
Ehsan Karamad44fc72112019-02-26 18:15:4713461enum class InnerWebContentsAttachChildFrameOriginType {
13462 kSameOriginAboutBlank,
13463 kSameOriginOther,
13464 kCrossOrigin
13465};
13466
13467class InnerWebContentsAttachTest
Fergal Daly2e7e1e12020-06-24 09:18:2813468 : public SitePerProcessBrowserTestBase,
Ehsan Karamad44fc72112019-02-26 18:15:4713469 public testing::WithParamInterface<
13470 std::tuple<InnerWebContentsAttachChildFrameOriginType,
13471 bool /* original frame has beforeunload handlers */,
13472 bool /* user proceeds with attaching */>> {
13473 public:
13474 InnerWebContentsAttachTest() {}
Peter Boström828b9022021-09-21 02:28:4313475
13476 InnerWebContentsAttachTest(const InnerWebContentsAttachTest&) = delete;
13477 InnerWebContentsAttachTest& operator=(const InnerWebContentsAttachTest&) =
13478 delete;
13479
Ehsan Karamad44fc72112019-02-26 18:15:4713480 ~InnerWebContentsAttachTest() override {}
13481
13482 protected:
13483 // Helper class to initiate and conclude a frame preparation process for
13484 // attaching an inner WebContents.
13485 class PrepareFrameJob {
13486 public:
13487 PrepareFrameJob(RenderFrameHostImpl* original_render_frame_host,
13488 bool proceed_through_beforeunload) {
13489 auto* web_contents =
13490 WebContents::FromRenderFrameHost(original_render_frame_host);
13491 // Need user gesture for 'beforeunload' to fire.
13492 PrepContentsForBeforeUnloadTest(web_contents);
13493 // Simulate user choosing to stay on the page after beforeunload fired.
David Benjaminf62c6662019-03-21 20:25:0413494 SetShouldProceedOnBeforeUnload(Shell::FromWebContents(web_contents),
13495 true /* always_proceed */,
13496 proceed_through_beforeunload);
Ehsan Karamad44fc72112019-02-26 18:15:4713497 RenderFrameHost::PrepareForInnerWebContentsAttachCallback callback =
13498 base::BindOnce(&PrepareFrameJob::OnPrepare, base::Unretained(this));
13499 original_render_frame_host->PrepareForInnerWebContentsAttach(
13500 std::move(callback));
13501 }
Peter Boström828b9022021-09-21 02:28:4313502
13503 PrepareFrameJob(const PrepareFrameJob&) = delete;
13504 PrepareFrameJob& operator=(const PrepareFrameJob&) = delete;
13505
Ehsan Karamad44fc72112019-02-26 18:15:4713506 virtual ~PrepareFrameJob() {}
13507
13508 void WaitForPreparedFrame() {
13509 if (did_call_prepare_)
13510 return;
13511 run_loop_.Run();
13512 }
13513
13514 RenderFrameHostImpl* prepared_frame() const {
13515 return new_render_frame_host_;
13516 }
13517
13518 private:
13519 void OnPrepare(RenderFrameHost* render_frame_host) {
13520 did_call_prepare_ = true;
13521 new_render_frame_host_ =
13522 static_cast<RenderFrameHostImpl*>(render_frame_host);
13523 if (run_loop_.running())
13524 run_loop_.Quit();
13525 }
13526
13527 bool did_call_prepare_ = false;
Keishi Hattori0e45c022021-11-27 09:25:5213528 raw_ptr<RenderFrameHostImpl> new_render_frame_host_ = nullptr;
Ehsan Karamad44fc72112019-02-26 18:15:4713529 base::RunLoop run_loop_;
Ehsan Karamad44fc72112019-02-26 18:15:4713530 };
Ehsan Karamad44fc72112019-02-26 18:15:4713531};
13532
13533// This is a test for the FrameTreeNode preparation process for various types
13534// of outer WebContents RenderFrameHosts; essentially when connecting two
13535// WebContents through a frame in a WebPage it is possible that the frame itself
13536// has a nontrivial document (other than about:blank) with a beforeunload
13537// handler, or even it is a cross-process frame. For such cases the frame first
13538// needs to be sanitized to be later consumed by the WebContents attaching API.
13539IN_PROC_BROWSER_TEST_P(InnerWebContentsAttachTest, PrepareFrame) {
13540 ASSERT_TRUE(
13541 NavigateToURL(shell(), embedded_test_server()->GetURL(
13542 "a.com", "/page_with_object_fallback.html")));
13543 InnerWebContentsAttachChildFrameOriginType child_frame_origin_type =
13544 std::get<0>(GetParam());
13545 bool test_beforeunload = std::get<1>(GetParam());
13546 bool proceed_through_beforeunload = std::get<2>(GetParam());
13547 GURL child_frame_url =
13548 child_frame_origin_type ==
13549 InnerWebContentsAttachChildFrameOriginType::kSameOriginAboutBlank
13550 ? GURL(url::kAboutBlankURL)
13551 : child_frame_origin_type ==
13552 InnerWebContentsAttachChildFrameOriginType::kSameOriginOther
13553 ? embedded_test_server()->GetURL("a.com", "/title1.html")
13554 : embedded_test_server()->GetURL("b.com", "/title1.html");
13555 SCOPED_TRACE(testing::Message()
13556 << " Child frame URL:" << child_frame_url.spec()
13557 << " 'beforeunload' modal shown: " << test_beforeunload
13558 << " proceed through'beforeunload': "
13559 << proceed_through_beforeunload);
Carlos Caballero15caeeb2021-10-27 09:57:5513560 auto* child_node = web_contents()->GetPrimaryFrameTree().root()->child_at(0);
Lukasz Anforowicz69c25dfd2020-11-12 21:50:2013561 EXPECT_TRUE(NavigateToURLFromRenderer(child_node, child_frame_url));
Ehsan Karamad44fc72112019-02-26 18:15:4713562 if (test_beforeunload) {
Di Zhang9bf1d652024-10-14 22:25:0213563 EXPECT_TRUE(ExecJs(child_node,
13564 "window.addEventListener('beforeunload', (e) => {"
13565 "e.preventDefault(); return e; });"));
Ehsan Karamad44fc72112019-02-26 18:15:4713566 }
13567 auto* original_child_frame = child_node->current_frame_host();
13568 RenderFrameDeletedObserver original_child_frame_observer(
13569 original_child_frame);
Marko Ivanovich7e403fc2020-10-09 05:56:4713570 AppModalDialogWaiter dialog_waiter(shell());
Ehsan Karamad44fc72112019-02-26 18:15:4713571 PrepareFrameJob prepare_job(original_child_frame,
13572 proceed_through_beforeunload);
13573 if (test_beforeunload)
Marko Ivanovich7e403fc2020-10-09 05:56:4713574 dialog_waiter.Wait();
Ehsan Karamad44fc72112019-02-26 18:15:4713575 prepare_job.WaitForPreparedFrame();
13576 auto* new_render_frame_host = prepare_job.prepared_frame();
13577 bool did_prepare_frame = new_render_frame_host;
13578 bool same_frame_used = (new_render_frame_host == original_child_frame);
13579 // If a frame was not prepared, then it has to be due to beforeunload being
13580 // dismissed.
13581 ASSERT_TRUE(did_prepare_frame ||
13582 (test_beforeunload && !proceed_through_beforeunload));
13583 // If the original frame is in the same SiteInstance as its parent, then it
13584 // can be reused; otherwise a new frame is expected here.
13585 bool is_same_origin =
13586 child_frame_origin_type !=
13587 InnerWebContentsAttachChildFrameOriginType::kCrossOrigin;
13588 if (!is_same_origin && did_prepare_frame) {
13589 // For the cross-origin case we expect the original RenderFrameHost to go
13590 // away during preparation.
13591 original_child_frame_observer.WaitUntilDeleted();
13592 }
13593 ASSERT_TRUE(!did_prepare_frame || (is_same_origin == same_frame_used));
13594 ASSERT_TRUE(!did_prepare_frame ||
13595 (original_child_frame_observer.deleted() != is_same_origin));
13596 // Finally, try the WebContents attach API and make sure we are doing OK.
13597 if (new_render_frame_host)
13598 CreateAndAttachInnerContents(new_render_frame_host);
13599}
13600
Staphany Park7dcbebc2019-02-28 06:59:4413601INSTANTIATE_TEST_SUITE_P(
Ehsan Karamad44fc72112019-02-26 18:15:4713602 SitePerProcess,
13603 InnerWebContentsAttachTest,
13604 testing::Combine(
13605 testing::ValuesIn(
13606 {InnerWebContentsAttachChildFrameOriginType::kSameOriginAboutBlank,
13607 InnerWebContentsAttachChildFrameOriginType::kSameOriginOther,
13608 InnerWebContentsAttachChildFrameOriginType::kCrossOrigin}),
13609 testing::Bool(),
13610 testing::Bool()));
W. James MacLean2dc75ed42019-03-06 14:31:0913611
arthursonzogni509e8af2019-05-29 14:03:1613612// This checks what process is used when an iframe is navigated to about:blank.
13613// The new document should be loaded in the process of its initiator.
13614//
13615// Test case:
13616// 1. Navigate to A1(B2).
13617// 2. B2 navigates itself to B3 = about:blank. Process B is used.
13618// 3. A1 makes B3 to navigate to A4 = about:blank. Process A is used.
Fergal Daly2e7e1e12020-06-24 09:18:2813619IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
arthursonzogni509e8af2019-05-29 14:03:1613620 SameAndCrossProcessIframeAboutBlankNavigation) {
13621 // 1. Navigate to A1(B2).
13622 GURL a1_url(embedded_test_server()->GetURL(
13623 "a.com", "/cross_site_iframe_factory.html?a(b)"));
13624 EXPECT_TRUE(NavigateToURL(shell(), a1_url));
Dave Tapuska327c06c92022-06-13 20:31:5113625 RenderFrameHostImpl* a1_rfh = web_contents()->GetPrimaryMainFrame();
arthursonzogni509e8af2019-05-29 14:03:1613626 RenderFrameHostImpl* b2_rfh = a1_rfh->child_at(0)->current_frame_host();
13627
13628 // 2. B2 navigates itself to B3 = about:blank. Process B is used.
13629 {
13630 scoped_refptr<SiteInstance> b2_site_instance = b2_rfh->GetSiteInstance();
13631 TestNavigationManager navigation_manager(web_contents(),
13632 GURL("about:blank"));
13633 EXPECT_TRUE(ExecJs(b2_rfh, "location.href = 'about:blank';"));
Fergal Daly83bc3cd2023-01-18 00:22:5413634 ASSERT_TRUE(navigation_manager.WaitForNavigationFinished());
arthursonzogni509e8af2019-05-29 14:03:1613635
13636 RenderFrameHostImpl* b3_rfh = a1_rfh->child_at(0)->current_frame_host();
13637 DCHECK_EQ(b3_rfh->GetSiteInstance(), b2_site_instance);
13638 DCHECK_NE(a1_rfh->GetProcess(), b3_rfh->GetProcess());
13639 }
13640
13641 // 3. A1 makes B3 to navigate to A4 = about:blank. Process A is used.
13642 {
13643 TestNavigationManager navigation_manager(web_contents(),
13644 GURL("about:blank"));
13645 EXPECT_TRUE(ExecJs(a1_rfh, R"(
13646 document.querySelector("iframe").src = "about:blank";
13647 )"));
Fergal Daly83bc3cd2023-01-18 00:22:5413648 ASSERT_TRUE(navigation_manager.WaitForNavigationFinished());
arthursonzogni509e8af2019-05-29 14:03:1613649
13650 RenderFrameHostImpl* b4_rfh = a1_rfh->child_at(0)->current_frame_host();
13651 DCHECK_EQ(a1_rfh->GetSiteInstance(), b4_rfh->GetSiteInstance());
13652 }
13653}
13654
Lei Zhang1d7a0d62025-06-24 18:48:1113655// TODO(crbug.com/425866013): Fix and re-enable flaky test.
13656#if BUILDFLAG(IS_FUCHSIA)
13657#define MAYBE_AccessWindowProxyOfCrashedFrameAfterNavigation \
13658 DISABLED_AccessWindowProxyOfCrashedFrameAfterNavigation
13659#else
13660#define MAYBE_AccessWindowProxyOfCrashedFrameAfterNavigation \
13661 AccessWindowProxyOfCrashedFrameAfterNavigation
13662#endif
Daniel Cheng12b3d642021-02-12 05:17:5713663IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
Lei Zhang1d7a0d62025-06-24 18:48:1113664 MAYBE_AccessWindowProxyOfCrashedFrameAfterNavigation) {
Daniel Cheng12b3d642021-02-12 05:17:5713665 EXPECT_TRUE(NavigateToURL(
13666 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
13667 const GURL cross_site_url =
13668 embedded_test_server()->GetURL("b.com", "/title1.html");
13669 TestNavigationObserver observer(cross_site_url);
13670 observer.StartWatchingNewWebContents();
13671 EXPECT_TRUE(ExecJs(
13672 shell(), JsReplace("openedWindow = window.open($1)", cross_site_url)));
13673 observer.WaitForNavigationFinished();
13674
13675 EXPECT_EQ(2u, Shell::windows().size());
13676 CrashTab(Shell::windows()[1]->web_contents());
13677
13678 // When starting a navigation in a crashed frame, the navigation code
13679 // immediately swaps in the speculative RFH.
13680 EXPECT_TRUE(
13681 ExecJs(shell(), "openedWindow.location = 'data:text/html,content'"));
13682 // The early-swapped frame should not be scriptable from another frame--nor
13683 // should trying to script it result in a crash.
13684 std::string result =
13685 EvalJs(shell(),
13686 "try { openedWindow.document } catch (e) { e.toString(); }")
13687 .ExtractString();
13688 EXPECT_THAT(
13689 result,
13690 ::testing::MatchesRegex(
Nate Chapin565f8ac2023-09-07 23:52:2813691 "SecurityError: Failed to read a named property 'document' from "
13692 "'Window': Blocked a frame with origin \"http://a.com:\\d+\" "
Daniel Cheng12b3d642021-02-12 05:17:5713693 "from accessing a cross-origin frame."));
13694}
13695
Sharon Yang36755952021-06-09 19:39:1013696// Make sure that a popup with a cross site subframe can be closed from the
13697// subframe.
13698IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, CloseNoopenerWindow) {
13699 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
13700 EXPECT_TRUE(NavigateToURL(shell(), main_url));
13701
13702 // Open a same site popup with a subframe using the noopener ref.
13703 GURL popup_url(
13704 embedded_test_server()->GetURL("a.com", "/page_with_blank_iframe.html"));
13705 ShellAddedObserver new_shell_observer;
13706 EXPECT_TRUE(ExecJs(
13707 shell(),
13708 JsReplace("popup = window.open($1,'_blank','noopener');", popup_url)));
13709 Shell* popup = new_shell_observer.GetShell();
13710 WebContentsImpl* popup_web_contents =
13711 static_cast<WebContentsImpl*>(popup->web_contents());
Carlos Caballero15caeeb2021-10-27 09:57:5513712 FrameTreeNode* popup_root = popup_web_contents->GetPrimaryFrameTree().root();
Sharon Yang36755952021-06-09 19:39:1013713 EXPECT_TRUE(WaitForLoadStop(popup_web_contents));
13714
13715 // Navigate the popup subframe cross site to b.com.
13716 FrameTreeNode* child = popup_root->child_at(0);
13717 GURL cross_origin_url(
13718 embedded_test_server()->GetURL("b.com", "/title1.html"));
13719 EXPECT_TRUE(NavigateToURLFromRenderer(child, cross_origin_url));
13720
13721 // Check that the popup successfully closes from the subframe.
13722 WebContentsDestroyedWatcher destroyed_watcher(popup->web_contents());
13723 EXPECT_TRUE(ExecJs(child, "window.parent.close()"));
13724 destroyed_watcher.Wait();
13725}
13726
Alex Moshchukdac03202020-04-28 05:30:5213727// Check that initial navigations to renderer debug URLs mark the renderer
13728// process as used, so that future navigations to sites that require a
13729// dedicated process do not reuse that process.
Fergal Daly2e7e1e12020-06-24 09:18:2813730IN_PROC_BROWSER_TEST_P(
Alex Moshchukdac03202020-04-28 05:30:5213731 SitePerProcessBrowserTest,
13732 ProcessNotReusedAfterInitialNavigationToRendererDebugURL) {
13733 // Load a javascript URL, which is a renderer debug URL. This navigation
13734 // won't commit, but the renderer process will synchronously process the
13735 // javascript URL and install an HTML document that contains "foo".
13736 GURL javascript_url("javascript:'foo'");
13737 shell()->LoadURL(javascript_url);
13738 EXPECT_EQ("foo", EvalJs(shell(), "document.body.innerText"));
13739
Dave Tapuska327c06c92022-06-13 20:31:5113740 RenderProcessHost* js_process =
13741 web_contents()->GetPrimaryMainFrame()->GetProcess();
Alex Moshchukdac03202020-04-28 05:30:5213742
13743 // Because the javascript URL can run arbitrary scripts in the renderer
13744 // process, it is unsafe to reuse the renderer process later for navigations
13745 // to sites that require a dedicated process. Ensure that this is the case.
13746 EXPECT_FALSE(js_process->IsUnused());
13747
13748 EXPECT_TRUE(NavigateToURL(
13749 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
Dave Tapuska327c06c92022-06-13 20:31:5113750 EXPECT_NE(js_process, web_contents()->GetPrimaryMainFrame()->GetProcess());
Alex Moshchukdac03202020-04-28 05:30:5213751}
13752
Mustaq Ahmede6459f82025-07-24 23:09:3113753namespace {
13754
13755void CheckStickyUserActivationState(RenderFrameHostImpl* host, bool expected) {
13756 EXPECT_EQ(expected, host->HasStickyUserActivation());
13757 EXPECT_EQ(expected, EvalJs(host, "navigator.userActivation.hasBeenActive",
13758 EXECUTE_SCRIPT_NO_USER_GESTURE));
13759}
13760
13761} // namespace
13762
13763// Test that a cross-site navigation in an iframe clears user activation.
13764IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
13765 UserActivationAfterCrossSiteNavInIframe) {
Liam Brady38b84562024-03-07 22:11:2613766 GURL main_url(embedded_test_server()->GetURL(
13767 "a.com", "/cross_site_iframe_factory.html?a(b)"));
13768 EXPECT_TRUE(NavigateToURL(shell(), main_url));
13769
13770 // It is safe to obtain the root frame tree node here, as it doesn't change.
13771 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
13772 FrameTreeNode* child = root->child_at(0);
13773
13774 // Sanity check that there is no sticky user activation at first.
Mustaq Ahmede6459f82025-07-24 23:09:3113775 CheckStickyUserActivationState(child->current_frame_host(), false);
Liam Brady38b84562024-03-07 22:11:2613776
13777 // Load cross-site page into iframe and verify there is still no sticky user
13778 // activation.
13779 GURL first_http_url(embedded_test_server()->GetURL("d.com", "/title1.html"));
13780 EXPECT_TRUE(
13781 NavigateToURLFromRendererWithoutUserGesture(child, first_http_url));
Mustaq Ahmede6459f82025-07-24 23:09:3113782 CheckStickyUserActivationState(child->current_frame_host(), false);
Liam Brady38b84562024-03-07 22:11:2613783
13784 // Give the child iframe user activation.
13785 EXPECT_TRUE(ExecJs(child, "// No-op script"));
Mustaq Ahmede6459f82025-07-24 23:09:3113786 CheckStickyUserActivationState(child->current_frame_host(), true);
Liam Brady38b84562024-03-07 22:11:2613787
13788 // Perform another cross-site navigation in the iframe.
13789 GURL http_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
13790 EXPECT_TRUE(NavigateToURLFromRendererWithoutUserGesture(child, http_url));
13791
13792 // The cross-site navigation should have cleared the user activation.
Mustaq Ahmede6459f82025-07-24 23:09:3113793 CheckStickyUserActivationState(child->current_frame_host(), false);
Liam Brady38b84562024-03-07 22:11:2613794
Mustaq Ahmed91a39092025-07-28 23:48:1213795 // Ensure that a top-level navigation from the iframe cannot happen.
Liam Brady38b84562024-03-07 22:11:2613796 EXPECT_TRUE(ExecJs(child->current_frame_host(),
13797 JsReplace("window.open($1, $2)", http_url, "_top"),
13798 EXECUTE_SCRIPT_NO_USER_GESTURE));
13799 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
13800 EXPECT_NE(http_url, shell()->web_contents()->GetLastCommittedURL());
13801}
13802
Mustaq Ahmede6459f82025-07-24 23:09:3113803// Test that a same-site cross-origin navigation in an iframe keeps user
13804// activation.
13805IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
13806 UserActivationAfterSameSiteNavInIframe) {
Liam Brady38b84562024-03-07 22:11:2613807 GURL main_url(embedded_test_server()->GetURL(
13808 "a.com", "/cross_site_iframe_factory.html?a(b)"));
13809 EXPECT_TRUE(NavigateToURL(shell(), main_url));
13810
13811 // It is safe to obtain the root frame tree node here, as it doesn't change.
13812 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
13813 FrameTreeNode* child = root->child_at(0);
13814
13815 // Sanity check that there is no sticky user activation at first.
Mustaq Ahmede6459f82025-07-24 23:09:3113816 CheckStickyUserActivationState(child->current_frame_host(), false);
Liam Brady38b84562024-03-07 22:11:2613817
13818 // Load cross-origin same-site page into iframe and verify there is still no
13819 // sticky user activation.
13820 GURL first_http_url(
13821 embedded_test_server()->GetURL("subdomain.b.com", "/title1.html"));
13822 EXPECT_TRUE(
13823 NavigateToURLFromRendererWithoutUserGesture(child, first_http_url));
Mustaq Ahmede6459f82025-07-24 23:09:3113824 CheckStickyUserActivationState(child->current_frame_host(), false);
Liam Brady38b84562024-03-07 22:11:2613825
13826 // Give the child iframe user activation.
13827 EXPECT_TRUE(ExecJs(child, "// No-op script"));
Mustaq Ahmede6459f82025-07-24 23:09:3113828 CheckStickyUserActivationState(child->current_frame_host(), true);
Liam Brady38b84562024-03-07 22:11:2613829
13830 // Perform another same-site navigation in the iframe.
13831 GURL http_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
13832 EXPECT_TRUE(NavigateToURLFromRendererWithoutUserGesture(child, http_url));
13833
13834 // The cross-origin same-site navigation should keep the sticky user
13835 // activation from the previous page.
Mustaq Ahmede6459f82025-07-24 23:09:3113836 CheckStickyUserActivationState(child->current_frame_host(), true);
Liam Brady38b84562024-03-07 22:11:2613837
Mustaq Ahmed91a39092025-07-28 23:48:1213838 // Ensure that a top-level navigation from the iframe can still happen.
Liam Brady38b84562024-03-07 22:11:2613839 EXPECT_TRUE(ExecJs(child->current_frame_host(),
13840 JsReplace("window.open($1, $2)", http_url, "_top"),
13841 EXECUTE_SCRIPT_NO_USER_GESTURE));
13842 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
13843 EXPECT_EQ(http_url, shell()->web_contents()->GetLastCommittedURL());
13844}
13845
Mustaq Ahmede6459f82025-07-24 23:09:3113846// Test that a same-origin navigation in an iframe keeps user activation.
13847IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
13848 UserActivationAfterSameOriginNavInIframe) {
Liam Brady38b84562024-03-07 22:11:2613849 GURL main_url(embedded_test_server()->GetURL(
13850 "a.com", "/cross_site_iframe_factory.html?a(b)"));
13851 EXPECT_TRUE(NavigateToURL(shell(), main_url));
13852
13853 // It is safe to obtain the root frame tree node here, as it doesn't change.
13854 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
13855 FrameTreeNode* child = root->child_at(0);
13856
13857 // Sanity check that there is no sticky user activation at first.
Mustaq Ahmede6459f82025-07-24 23:09:3113858 CheckStickyUserActivationState(child->current_frame_host(), false);
Liam Brady38b84562024-03-07 22:11:2613859
13860 // Load cross-site page into iframe and verify there is still no sticky user
13861 // activation.
13862 GURL first_http_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
13863 EXPECT_TRUE(NavigateIframeToURL(web_contents(), "child-0", first_http_url));
Mustaq Ahmede6459f82025-07-24 23:09:3113864 CheckStickyUserActivationState(child->current_frame_host(), false);
Liam Brady38b84562024-03-07 22:11:2613865
13866 // Give the child iframe user activation.
13867 EXPECT_TRUE(ExecJs(child, "// No-op script"));
Mustaq Ahmede6459f82025-07-24 23:09:3113868 CheckStickyUserActivationState(child->current_frame_host(), true);
Liam Brady38b84562024-03-07 22:11:2613869
13870 // Load same-origin page into iframe.
13871 GURL http_url(embedded_test_server()->GetURL("c.com", "/title2.html"));
13872 EXPECT_TRUE(NavigateIframeToURL(web_contents(), "child-0", http_url));
13873
13874 // The same-origin navigation should keep the sticky user activation from the
13875 // previous page.
Mustaq Ahmede6459f82025-07-24 23:09:3113876 CheckStickyUserActivationState(child->current_frame_host(), true);
Liam Brady38b84562024-03-07 22:11:2613877
Mustaq Ahmed91a39092025-07-28 23:48:1213878 // Ensure that a top-level navigation from the iframe can still happen.
Liam Brady38b84562024-03-07 22:11:2613879 EXPECT_TRUE(ExecJs(child->current_frame_host(),
13880 JsReplace("window.open($1, $2)", http_url, "_top"),
13881 EXECUTE_SCRIPT_NO_USER_GESTURE));
13882 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
13883 EXPECT_EQ(http_url, shell()->web_contents()->GetLastCommittedURL());
13884}
13885
Mustaq Ahmed91a39092025-07-28 23:48:1213886class StickyActivationAcrossSameOriginNavBrowserTest
13887 : public SitePerProcessBrowserTest {
13888 public:
13889 StickyActivationAcrossSameOriginNavBrowserTest() {
13890 scoped_feature_list_.InitAndEnableFeature(
13891 blink::features::kStickyUserActivationAcrossSameOriginNavigation);
13892 }
13893
13894 private:
13895 base::test::ScopedFeatureList scoped_feature_list_;
13896};
13897
13898// Test that a cross-site navigation in the top frame clears user activation.
13899IN_PROC_BROWSER_TEST_P(StickyActivationAcrossSameOriginNavBrowserTest,
13900 UserActivationAfterCrossSiteNavInTopFrame) {
13901 GURL starting_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
13902 EXPECT_TRUE(NavigateToURL(shell(), starting_url));
13903 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
13904
13905 // Sanity check that there is no sticky user activation at first.
13906 CheckStickyUserActivationState(root->current_frame_host(), false);
13907
13908 // Perform a cross-site navigation and verify there is still no sticky user
13909 // activation.
13910 GURL first_nav_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
13911 EXPECT_TRUE(
13912 NavigateToURLFromRendererWithoutUserGesture(shell(), first_nav_url));
13913 CheckStickyUserActivationState(root->current_frame_host(), false);
13914
13915 // Give the frame user activation.
13916 EXPECT_TRUE(ExecJs(root, "// No-op script"));
13917 CheckStickyUserActivationState(root->current_frame_host(), true);
13918
13919 // Perform another cross-site navigation.
13920 GURL second_nav_url(embedded_test_server()->GetURL(
13921 "c.com", "/cross_site_iframe_factory.html?c(c)"));
13922 EXPECT_TRUE(
13923 NavigateToURLFromRendererWithoutUserGesture(shell(), second_nav_url));
13924
13925 // The navigation should have cleared the user activation.
13926 CheckStickyUserActivationState(root->current_frame_host(), false);
13927 CheckStickyUserActivationState(root->child_at(0)->current_frame_host(),
13928 false);
13929}
13930
13931// Test that a same-site cross-origin navigation in the top frame clears user
13932// activation.
13933IN_PROC_BROWSER_TEST_P(StickyActivationAcrossSameOriginNavBrowserTest,
13934 UserActivationAfterSameSiteNavInTopFrame) {
13935 GURL starting_url(
13936 embedded_test_server()->GetURL("sub1.a.com", "/title1.html"));
13937 EXPECT_TRUE(NavigateToURL(shell(), starting_url));
13938 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
13939
13940 // Sanity check that there is no sticky user activation at first.
13941 CheckStickyUserActivationState(root->current_frame_host(), false);
13942
13943 // Perform a same-site cross-origin navigation and verify there is still no
13944 // sticky user activation.
13945 GURL first_nav_url(
13946 embedded_test_server()->GetURL("sub2.a.com", "/title1.html"));
13947 EXPECT_TRUE(
13948 NavigateToURLFromRendererWithoutUserGesture(shell(), first_nav_url));
13949 CheckStickyUserActivationState(root->current_frame_host(), false);
13950
13951 // Give the frame user activation.
13952 EXPECT_TRUE(ExecJs(root, "// No-op script"));
13953 CheckStickyUserActivationState(root->current_frame_host(), true);
13954
13955 // Perform another same-site cross-origin navigation in the iframe.
13956 GURL second_nav_url(embedded_test_server()->GetURL(
13957 "a.com", "/cross_site_iframe_factory.html?a(a)"));
13958 EXPECT_TRUE(
13959 NavigateToURLFromRendererWithoutUserGesture(shell(), second_nav_url));
13960
13961 // The navigation should have cleared the user activation.
13962 CheckStickyUserActivationState(root->current_frame_host(), false);
13963 CheckStickyUserActivationState(root->child_at(0)->current_frame_host(),
13964 false);
13965}
13966
13967// Test that a same-origin navigation in the top frame keeps user activation.
13968IN_PROC_BROWSER_TEST_P(StickyActivationAcrossSameOriginNavBrowserTest,
13969 UserActivationAfterSameOriginNavInTopFrame) {
13970 GURL starting_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
13971 EXPECT_TRUE(NavigateToURL(shell(), starting_url));
13972 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
13973
13974 // Sanity check that there is no sticky user activation at first.
13975 CheckStickyUserActivationState(root->current_frame_host(), false);
13976
13977 // Perform a same-origin navigation and verify there is still no sticky user
13978 // activation.
13979 GURL first_nav_url(embedded_test_server()->GetURL("a.com", "/title2.html"));
13980 EXPECT_TRUE(
13981 NavigateToURLFromRendererWithoutUserGesture(shell(), first_nav_url));
13982 CheckStickyUserActivationState(root->current_frame_host(), false);
13983
13984 // Give the frame user activation.
13985 EXPECT_TRUE(ExecJs(root, "// No-op script"));
13986 CheckStickyUserActivationState(root->current_frame_host(), true);
13987
13988 // Perform another same-origin navigation in the iframe.
13989 GURL second_nav_url(embedded_test_server()->GetURL(
13990 "a.com", "/cross_site_iframe_factory.html?a(a)"));
13991 EXPECT_TRUE(
13992 NavigateToURLFromRendererWithoutUserGesture(shell(), second_nav_url));
13993
13994 // The navigation should keep the user activation at the top frame only.
13995 CheckStickyUserActivationState(root->current_frame_host(), true);
13996 CheckStickyUserActivationState(root->child_at(0)->current_frame_host(),
13997 false);
13998}
13999
Nasko Oskovc084f562024-09-07 01:49:5414000// Test which captures behavior of navigation to about:blank in a newly created
14001// WebContents when an initial SiteInstance is supplied as part of the creation.
14002IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
14003 AboutBlankInNewWindowWithInitialSiteInstance) {
14004 // Start by navigating to a page on a normal web site.
14005 EXPECT_TRUE(
14006 NavigateToURL(shell(), embedded_test_server()->GetURL("/empty.html")));
14007
14008 // Now do a browser-initiated navigation to about:blank in a new tab created
Nasko Oskov58553e9e2024-09-30 21:55:0114009 // in the previous SiteInstance. The current behavior is for the navigation
14010 // to switch to a new SiteInstance, though there is no real requirement for
14011 // that. In the past the existing SiteInstance was used.
Nasko Oskovc084f562024-09-07 01:49:5414012 WebContents::CreateParams new_contents_params(
14013 web_contents()->GetBrowserContext(), web_contents()->GetSiteInstance());
14014 std::unique_ptr<WebContents> new_web_contents(
14015 WebContents::Create(new_contents_params));
14016
14017 EXPECT_TRUE(NavigateToURL(new_web_contents.get(), GURL(url::kAboutBlankURL)));
Nasko Oskov58553e9e2024-09-30 21:55:0114018 EXPECT_NE(web_contents()->GetPrimaryMainFrame()->GetProcess(),
Nasko Oskovc084f562024-09-07 01:49:5414019 new_web_contents->GetPrimaryMainFrame()->GetProcess());
14020}
14021
Sharon Yangf390ea92021-06-23 19:55:4014022// Tests that verify the feature disabling process reuse.
14023class DisableProcessReusePolicyTest : public SitePerProcessBrowserTest {
14024 public:
14025 DisableProcessReusePolicyTest() {
14026 scoped_feature_list_.InitAndEnableFeature(features::kDisableProcessReuse);
14027 }
14028 ~DisableProcessReusePolicyTest() override = default;
14029
14030 DisableProcessReusePolicyTest(const DisableProcessReusePolicyTest&) = delete;
14031 DisableProcessReusePolicyTest& operator=(
14032 const DisableProcessReusePolicyTest&) = delete;
14033
14034 private:
14035 base::test::ScopedFeatureList scoped_feature_list_;
14036};
14037
14038// In two tabs with the same site, open a cross site iframe in each (same site
14039// for the iframes). Make sure these do not have the same process ID.
14040IN_PROC_BROWSER_TEST_P(DisableProcessReusePolicyTest,
14041 DisableProcessReusePolicy) {
14042 GURL url(
14043 embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html"));
14044 EXPECT_TRUE(NavigateToURL(shell(), url));
Carlos Caballero15caeeb2021-10-27 09:57:5514045 FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
Sharon Yangf390ea92021-06-23 19:55:4014046 FrameTreeNode* child = root->child_at(0);
14047
14048 // Navigate the subframe cross site, and make sure it is an OOPIF.
14049 GURL cross_site_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
14050 TestNavigationObserver observer(shell()->web_contents());
14051 EXPECT_TRUE(NavigateToURLFromRenderer(child, cross_site_url));
14052 EXPECT_TRUE(child->current_frame_host()->IsCrossProcessSubframe());
14053
14054 // Open an new tab in a separate BrowsingInstance with the same url as the
14055 // first tab and open a subframe, also to |cross_site_url|.
14056 Shell* second_shell = CreateBrowser();
14057 EXPECT_TRUE(NavigateToURL(second_shell, url));
14058 FrameTreeNode* second_root =
14059 static_cast<WebContentsImpl*>(second_shell->web_contents())
Carlos Caballero15caeeb2021-10-27 09:57:5514060 ->GetPrimaryFrameTree()
14061 .root();
Sharon Yangf390ea92021-06-23 19:55:4014062 FrameTreeNode* second_child = second_root->child_at(0);
14063 EXPECT_TRUE(NavigateToURLFromRenderer(second_child, cross_site_url));
14064 EXPECT_TRUE(second_child->current_frame_host()->IsCrossProcessSubframe());
14065
14066 scoped_refptr<SiteInstanceImpl> second_shell_instance =
14067 second_child->current_frame_host()->GetSiteInstance();
Charlie Reis9ce0ed222024-09-05 22:05:2614068 EXPECT_NE(ProcessReusePolicy::REUSE_PENDING_OR_COMMITTED_SITE_WORKER,
W. James MacLeanb2e5e992024-08-27 16:51:3014069 second_shell_instance->process_reuse_policy());
Charlie Reis9ce0ed222024-09-05 22:05:2614070 EXPECT_NE(ProcessReusePolicy::REUSE_PENDING_OR_COMMITTED_SITE_SUBFRAME,
W. James MacLeanb2e5e992024-08-27 16:51:3014071 second_shell_instance->process_reuse_policy());
Sharon Yangf390ea92021-06-23 19:55:4014072
14073 EXPECT_NE(child->current_frame_host()->GetProcess(),
14074 second_child->current_frame_host()->GetProcess());
14075}
14076
Kenichi Ishibashi8e087a62023-06-06 06:17:4114077class SitePerProcessWithMainFrameThresholdTestBase
14078 : public SitePerProcessBrowserTestBase {
Kenichi Ishibashi6ac9e982023-04-27 00:10:5514079 public:
Dave Tapuskae15164c2024-08-02 23:53:3514080 static constexpr size_t kDefaultThreshold = 3;
Kenichi Ishibashi6ac9e982023-04-27 00:10:5514081
Dave Tapuskae15164c2024-08-02 23:53:3514082 explicit SitePerProcessWithMainFrameThresholdTestBase(
14083 size_t frame_threshold = kDefaultThreshold,
14084 size_t total_memory_threshold = 0) {
14085 base::FieldTrialParams params = {
14086 {"ProcessPerSiteMainFrameThreshold",
14087 base::StringPrintf("%zu", frame_threshold)}};
14088 if (total_memory_threshold != 0) {
14089 params["ProcessPerSiteMainFrameTotalMemoryLimit"] =
14090 base::StringPrintf("%zu", total_memory_threshold);
14091 }
Kenichi Ishibashi6ac9e982023-04-27 00:10:5514092 scoped_feature_list_.InitAndEnableFeatureWithParameters(
Dave Tapuskae15164c2024-08-02 23:53:3514093 features::kProcessPerSiteUpToMainFrameThreshold, params);
Kenichi Ishibashi6ac9e982023-04-27 00:10:5514094 }
Kenichi Ishibashi8e087a62023-06-06 06:17:4114095 ~SitePerProcessWithMainFrameThresholdTestBase() override = default;
14096
14097 Shell* CreateShellAndNavigateToURL(const GURL& url) {
14098 const GURL kOtherUrl =
14099 embedded_test_server()->GetURL("bar.test", "/title1.html");
14100
14101 Shell* shell = CreateBrowser();
14102 // Navigate to a different site first so that the new shell has a non empty
14103 // site info before navigating to the target site.
Alison Gale770f3fc2024-04-27 00:39:5814104 // TODO(crbug.com/40264958): Remove this workaround once we figure
Kenichi Ishibashi8e087a62023-06-06 06:17:4114105 // out how to handle navigation from an empty site to a new site.
14106 CHECK(NavigateToURL(shell, kOtherUrl));
14107 CHECK(NavigateToURL(shell, url));
14108 return shell;
14109 }
Kenichi Ishibashi6ac9e982023-04-27 00:10:5514110
14111 private:
14112 base::test::ScopedFeatureList scoped_feature_list_;
14113};
14114
Kenichi Ishibashi8e087a62023-06-06 06:17:4114115class SitePerProcessWithMainFrameThresholdTest
14116 : public SitePerProcessWithMainFrameThresholdTestBase,
14117 public ::testing::WithParamInterface<std::string> {
14118 public:
14119 SitePerProcessWithMainFrameThresholdTest() = default;
14120 ~SitePerProcessWithMainFrameThresholdTest() override = default;
14121};
14122
Kenichi Ishibashi6ac9e982023-04-27 00:10:5514123// Tests that a RenderProcessHost is reused up to a certain threshold against
14124// number of main frames, if the corresponding SiteInstance requires a dedicated
14125// process. Subframes are irrelevant to the threshold. Once the number of main
14126// frame reaches to the threshold, a new RenderProcessHost should be created and
14127// the existing RenderProcessHost should not be reused.
14128IN_PROC_BROWSER_TEST_P(SitePerProcessWithMainFrameThresholdTest,
14129 ReuseProcessUpToThreshold) {
14130 const GURL kUrl =
14131 embedded_test_server()->GetURL("foo.test", "/page_with_iframe.html");
14132 const GURL kOtherUrl =
14133 embedded_test_server()->GetURL("bar.test", "/title1.html");
14134
14135 ASSERT_TRUE(NavigateToURL(shell(), kUrl));
14136 RenderFrameHostImpl* main_frame_in_main_shell =
14137 static_cast<WebContentsImpl*>(shell()->web_contents())
14138 ->GetPrimaryMainFrame();
14139 RenderFrameHostImpl* subframe_in_main_shell =
14140 main_frame_in_main_shell->child_at(0)->current_frame_host();
14141 ASSERT_EQ(main_frame_in_main_shell->GetProcess(),
14142 subframe_in_main_shell->GetProcess());
14143
14144 std::vector<Shell*> shells;
Dave Tapuskae15164c2024-08-02 23:53:3514145 for (size_t i = 0; i < kDefaultThreshold - 1; ++i) {
Kenichi Ishibashi8e087a62023-06-06 06:17:4114146 Shell* new_shell = CreateShellAndNavigateToURL(kUrl);
Kenichi Ishibashi6ac9e982023-04-27 00:10:5514147 RenderFrameHostImpl* new_frame =
14148 static_cast<WebContentsImpl*>(new_shell->web_contents())
14149 ->GetPrimaryMainFrame();
14150 // Currently the reuse policy is only applied for sites that require a
14151 // dedicated process, and if this not the case, the two main frames won't
14152 // share a process due to being under the process limit.
14153 if (main_frame_in_main_shell->GetSiteInstance()
14154 ->RequiresDedicatedProcess()) {
14155 ASSERT_EQ(main_frame_in_main_shell->GetProcess(),
14156 new_frame->GetProcess());
14157 } else {
14158 ASSERT_NE(main_frame_in_main_shell->GetProcess(),
14159 new_frame->GetProcess());
14160 }
14161 shells.emplace_back(new_shell);
14162 }
14163
14164 Shell* non_shared_shell = CreateBrowser();
Alison Gale770f3fc2024-04-27 00:39:5814165 // TODO(crbug.com/40264958): Remove this workaround once we figure
Kenichi Ishibashi6ac9e982023-04-27 00:10:5514166 // out how to handle navigation from an empty site to a new site.
14167 ASSERT_TRUE(NavigateToURL(non_shared_shell, kOtherUrl));
14168 ASSERT_TRUE(NavigateToURL(non_shared_shell, kUrl));
14169 RenderFrameHostImpl* main_frame_in_non_shared_frame =
14170 static_cast<WebContentsImpl*>(non_shared_shell->web_contents())
14171 ->GetPrimaryMainFrame();
14172 ASSERT_NE(main_frame_in_main_shell->GetProcess(),
14173 main_frame_in_non_shared_frame->GetProcess());
14174 shells.emplace_back(non_shared_shell);
14175
14176 for (auto*& shell : shells) {
14177 shell->Close();
14178 }
14179}
14180
Viktoriya Bryhider777dfba2025-04-02 00:15:5014181// Tests that renderer process is not reused when it hangs:
14182// 1. For OOP iframe in a different tab;
14183// 2. For main frame in a different tab.
14184IN_PROC_BROWSER_TEST_P(SitePerProcessWithMainFrameThresholdTest,
14185 DoNotReuseRenderProcessAfterHung) {
14186 const GURL kUrl_a_b(embedded_test_server()->GetURL(
14187 "a.com", "/cross_site_iframe_factory.html?a(b)"));
14188
14189 const GURL kUrl_c_b(embedded_test_server()->GetURL(
14190 "c.com", "/cross_site_iframe_factory.html?c(b)"));
14191
14192 const GURL kUrl_b(embedded_test_server()->GetURL("b.com", "/title1.html"));
14193
14194 // Ensure the reuse of processes for same-site URLs, to test
14195 // that the process is not reused when it becomes unresponsive.
14196 RenderProcessHost::SetMaxRendererProcessCount(1);
14197
14198 ASSERT_TRUE(NavigateToURL(shell(), kUrl_a_b));
14199 RenderFrameHostImpl* main_frame =
14200 static_cast<WebContentsImpl*>(shell()->web_contents())
14201 ->GetPrimaryMainFrame();
14202 RenderFrameHostImpl* subframe = main_frame->child_at(0)->current_frame_host();
14203 RenderProcessHost* main_frame_process = main_frame->GetProcess();
14204 RenderProcessHost* b_subframe_process = subframe->GetProcess();
14205 ASSERT_NE(main_frame_process, b_subframe_process);
14206
14207 // Hang b.com process with OOP iframe.
14208 {
14209 UnresponsiveRendererObserver unresponsive_renderer_observer(
14210 shell()->web_contents());
14211
14212 // This is to simulate renderer hung event. Class
14213 // SimulateUnresponsiveRenderer does not work here, because it hits only
14214 // WebContents, while we need widget to know that it is unresponsive.
14215 static_cast<RenderWidgetHostImpl*>(subframe->GetRenderWidgetHost())
Aman Vermad88d4de2025-05-06 12:00:3314216 ->OnInputEventAckTimeout(base::TimeTicks::Now() +
14217 input::kHungRendererDelay);
Viktoriya Bryhider777dfba2025-04-02 00:15:5014218
14219 RenderProcessHost* hung_process = unresponsive_renderer_observer.Wait();
14220 EXPECT_EQ(hung_process, b_subframe_process);
14221 }
14222
14223 // 1. Navigate to url with b.com iframe, for which process is unresponsive.
14224 Shell* cb_shell = CreateShellAndNavigateToURL(kUrl_c_b);
14225 RenderFrameHostImpl* cb_main_frame =
14226 static_cast<WebContentsImpl*>(cb_shell->web_contents())
14227 ->GetPrimaryMainFrame();
14228
14229 RenderFrameHostImpl* cb_subframe =
14230 cb_main_frame->child_at(0)->current_frame_host();
14231
14232 // Check that b.com iframe is not reusing existing unresponsive process with
14233 // b.com.
14234 ASSERT_NE(b_subframe_process, cb_subframe->GetProcess());
14235
14236 // 2. Navigate main frame to b.com, for which process is unresponsive.
14237 Shell* b_shell = CreateShellAndNavigateToURL(kUrl_b);
14238 RenderFrameHostImpl* b_main_frame =
14239 static_cast<WebContentsImpl*>(b_shell->web_contents())
14240 ->GetPrimaryMainFrame();
14241
14242 // Check that b.com main frame is not reusing existing unresponsive process
14243 // with b.com.
14244 ASSERT_NE(b_subframe_process, b_main_frame->GetProcess());
14245}
14246
Alex Attarb82599d2025-05-13 14:30:0614247// Test fixture that enables kProcessPerSiteUpToMainFrameThreshold and sets up
14248// a SitePerProcessWithMainFrameThresholdAndSiteRestrictionBrowserClient to
14249// restrict the sites for which ProcessPerSite is used.
14250class SitePerProcessWithMainFrameThresholdAndSiteRestrictionTest
14251 : public SitePerProcessWithMainFrameThresholdTest {
14252 public:
14253 SitePerProcessWithMainFrameThresholdAndSiteRestrictionTest() {
14254 // Initialize both features in a single call
14255 scoped_feature_list_.InitAndEnableFeature(
14256 features::kProcessPerSiteUpToMainFrameThreshold);
14257 }
14258
14259 void SetUpOnMainThread() override {
14260 SitePerProcessWithMainFrameThresholdTest::SetUpOnMainThread();
14261 test_client_ = std::make_unique<
14262 SitePerProcessWithMainFrameThresholdAndSiteRestrictionBrowserClient>();
14263 }
14264
14265 private:
14266 base::test::ScopedFeatureList scoped_feature_list_;
14267 std::unique_ptr<
14268 SitePerProcessWithMainFrameThresholdAndSiteRestrictionBrowserClient>
14269 test_client_;
14270};
14271
14272// Verify that ShouldReuseExistingProcessForNewMainFrameSiteInstance is honored
14273// when deciding whether to reuse a process for a main frame navigation under
14274// the threshold, provided the controlling feature flag is enabled.
14275IN_PROC_BROWSER_TEST_P(
14276 SitePerProcessWithMainFrameThresholdAndSiteRestrictionTest,
14277 RestrictedToURLWithContentClient) {
14278 GURL foo_url = embedded_test_server()->GetURL("foo.com", "/title1.html");
14279 GURL bar_url = embedded_test_server()->GetURL("bar.com", "/title2.html");
14280
14281 auto* shell_foo1 = CreateShellAndNavigateToURL(foo_url);
14282 RenderProcessHost* rph_foo1 =
14283 shell_foo1->web_contents()->GetPrimaryMainFrame()->GetProcess();
14284
14285 auto* shell_foo2 = CreateShellAndNavigateToURL(foo_url);
14286 RenderProcessHost* rph_foo2 =
14287 shell_foo2->web_contents()->GetPrimaryMainFrame()->GetProcess();
14288
14289 // Verify foo.com reuse processes.
14290 EXPECT_EQ(rph_foo1, rph_foo2);
14291
14292 auto* shell_bar1 = CreateShellAndNavigateToURL(bar_url);
14293 RenderProcessHost* rph_bar1 =
14294 shell_bar1->web_contents()->GetPrimaryMainFrame()->GetProcess();
14295
14296 auto* shell_bar2 = CreateShellAndNavigateToURL(bar_url);
14297 RenderProcessHost* rph_bar2 =
14298 shell_bar2->web_contents()->GetPrimaryMainFrame()->GetProcess();
14299
14300 // Verify bar.com did not reuse processes.
14301 EXPECT_NE(rph_bar1, rph_bar2);
14302
14303 // Verify foo.com and bar.com are in different processes.
14304 EXPECT_NE(rph_foo1, rph_bar1);
14305 EXPECT_NE(rph_foo1, rph_bar2);
14306}
14307
14308// Verify that ShouldReuseExistingProcessForNewMainFrameSiteInstance's
14309// path-specific logic, using the original_url, correctly assigns different
14310// processes to main frame navigations on the same domain but with different
14311// paths, under the kProcessPerSiteUpToMainFrameThreshold policy.
14312IN_PROC_BROWSER_TEST_P(
14313 SitePerProcessWithMainFrameThresholdAndSiteRestrictionTest,
14314 PathSpecificOriginalUrlReuse) {
14315 GURL foo_url_path = embedded_test_server()->GetURL("foo.com", "/title1.html");
14316 GURL foo_url_path_noreuse =
14317 embedded_test_server()->GetURL("foo.com", "/title2.html");
14318 // Navigate to foo.com/title1.html (matches client rule for reuse) however
14319 // foo.com/title2.html should not be reused since they have different paths.
14320 auto* shell_foo_url_path = CreateShellAndNavigateToURL(foo_url_path);
14321 RenderProcessHost* rph_foo_url_path =
14322 shell_foo_url_path->web_contents()->GetPrimaryMainFrame()->GetProcess();
14323
14324 auto* shell_foo_url_path_noreuse =
14325 CreateShellAndNavigateToURL(foo_url_path_noreuse);
14326 RenderProcessHost* rph_foo_url_path_noreuse =
14327 shell_foo_url_path_noreuse->web_contents()
14328 ->GetPrimaryMainFrame()
14329 ->GetProcess();
14330
14331 EXPECT_NE(rph_foo_url_path, rph_foo_url_path_noreuse);
14332}
14333
Dave Tapuskae15164c2024-08-02 23:53:3514334// A test fixture that provides an upper limit of 4 bytes, so should fail the
14335// assignment of another outermost main frame into the process.
14336class SitePerProcessWithMainFrameThresholdWithTotalLimitTest
14337 : public SitePerProcessWithMainFrameThresholdTestBase,
14338 public ::testing::WithParamInterface<std::string> {
14339 public:
14340 SitePerProcessWithMainFrameThresholdWithTotalLimitTest()
14341 : SitePerProcessWithMainFrameThresholdTestBase(
14342 /*frame_threshold=*/10,
14343 /*total_memory_threshold=*/9) {}
14344 ~SitePerProcessWithMainFrameThresholdWithTotalLimitTest() override = default;
14345};
14346
14347class RendererHostInterceptor
14348 : public mojom::RendererHostInterceptorForTesting {
14349 public:
14350 explicit RendererHostInterceptor(RenderProcessHostImpl* process_host)
14351 : swapped_impl_(process_host->renderer_host_receiver_for_testing(),
14352 this) {}
14353 mojom::RendererHost* GetForwardingInterface() override {
14354 return swapped_impl_.old_impl();
14355 }
14356
14357#if BUILDFLAG(IS_ANDROID)
14358 void SetPrivateMemoryFootprint(
14359 uint64_t private_memory_footprint_bytes) override {
14360 // Drop this message from the renderer.
14361 }
14362#endif
14363
14364 private:
14365 mojo::test::ScopedSwapImplForTesting<mojom::RendererHost> swapped_impl_;
14366};
14367
14368// Tests that a RenderProcessHost is not reused when the private memory
14369// footprint of the process exceeds a certain amount.
14370IN_PROC_BROWSER_TEST_P(SitePerProcessWithMainFrameThresholdWithTotalLimitTest,
14371 ExcessiveAllocation) {
14372 const GURL kUrl =
14373 embedded_test_server()->GetURL("foo.test", "/page_with_iframe.html");
14374
14375 base::HistogramTester histograms;
14376 ASSERT_TRUE(NavigateToURL(shell(), kUrl));
14377 RenderFrameHostImpl* main_frame_in_main_shell =
14378 static_cast<WebContentsImpl*>(shell()->web_contents())
14379 ->GetPrimaryMainFrame();
14380 RenderFrameHostImpl* subframe_in_main_shell =
14381 main_frame_in_main_shell->child_at(0)->current_frame_host();
14382 ASSERT_EQ(main_frame_in_main_shell->GetProcess(),
14383 subframe_in_main_shell->GetProcess());
14384
14385 Shell* new_shell = CreateShellAndNavigateToURL(kUrl);
14386 RenderFrameHostImpl* new_frame =
14387 static_cast<WebContentsImpl*>(new_shell->web_contents())
14388 ->GetPrimaryMainFrame();
14389 ASSERT_NE(main_frame_in_main_shell->GetProcess(), new_frame->GetProcess());
14390 new_shell->Close();
14391
14392 // Verify that we hit a limit histogram.
14393 histograms.ExpectTotalCount(
14394 "BrowserRenderProcessHost.ProcessPerSiteMainFrameLimit", 1);
14395 histograms.ExpectBucketCount(
14396 "BrowserRenderProcessHost.ProcessPerSiteMainFrameLimit", 1, 1);
14397}
14398
14399// Tests that opening a fourth tab will put it over the limit and will allocate
14400// a new process. We allocate 3 main frames that are 2 bytes each. Placing
14401// a fourth would exceeded the limit of 9.
14402IN_PROC_BROWSER_TEST_P(SitePerProcessWithMainFrameThresholdWithTotalLimitTest,
14403 AllowedAllocation) {
14404 const GURL kUrl =
14405 embedded_test_server()->GetURL("foo.test", "/page_with_iframe.html");
14406
14407 base::HistogramTester histograms;
14408 ASSERT_TRUE(NavigateToURL(shell(), kUrl));
14409 RenderFrameHostImpl* main_frame_in_main_shell =
14410 static_cast<WebContentsImpl*>(shell()->web_contents())
14411 ->GetPrimaryMainFrame();
14412 RenderFrameHostImpl* subframe_in_main_shell =
14413 main_frame_in_main_shell->child_at(0)->current_frame_host();
14414 ASSERT_EQ(main_frame_in_main_shell->GetProcess(),
14415 subframe_in_main_shell->GetProcess());
14416
14417 auto* process_host = static_cast<RenderProcessHostImpl*>(
14418 main_frame_in_main_shell->GetProcess());
14419 RendererHostInterceptor interceptor(process_host);
14420 process_host->SetPrivateMemoryFootprintForTesting(2);
14421
14422 std::vector<Shell*> shells;
14423 for (size_t i = 0; i < 2; ++i) {
14424 Shell* new_shell = CreateShellAndNavigateToURL(kUrl);
14425 RenderFrameHostImpl* new_frame =
14426 static_cast<WebContentsImpl*>(new_shell->web_contents())
14427 ->GetPrimaryMainFrame();
14428 // Currently the reuse policy is only applied for sites that require a
14429 // dedicated process, and if this not the case, the two main frames won't
14430 // share a process due to being under the process limit.
14431 if (main_frame_in_main_shell->GetSiteInstance()
14432 ->RequiresDedicatedProcess()) {
14433 ASSERT_EQ(main_frame_in_main_shell->GetProcess(),
14434 new_frame->GetProcess());
14435 } else {
14436 ASSERT_NE(main_frame_in_main_shell->GetProcess(),
14437 new_frame->GetProcess());
14438 }
14439 process_host->SetPrivateMemoryFootprintForTesting(2 * (i + 2));
14440 shells.emplace_back(new_shell);
14441 }
14442 EXPECT_EQ(
14443 6u, main_frame_in_main_shell->GetProcess()->GetPrivateMemoryFootprint());
14444
14445 // The 4th outermostmain frame will not fit.
14446 // The expected size of a frame will be 2, with a scale factor of 1.5
14447 // 6 + (2 * 1.5) > 9 so the check should fail.
14448 Shell* fourth_shell = CreateShellAndNavigateToURL(kUrl);
14449 RenderFrameHostImpl* fourth_frame =
14450 static_cast<WebContentsImpl*>(fourth_shell->web_contents())
14451 ->GetPrimaryMainFrame();
14452 ASSERT_NE(main_frame_in_main_shell->GetProcess(), fourth_frame->GetProcess());
14453 shells.emplace_back(fourth_shell);
14454 for (auto*& shell : shells) {
14455 shell->Close();
14456 }
14457 // Verify that we hit a limit histogram.
14458 histograms.ExpectTotalCount(
14459 "BrowserRenderProcessHost.ProcessPerSiteMainFrameLimit", 1);
14460 histograms.ExpectBucketCount(
14461 "BrowserRenderProcessHost.ProcessPerSiteMainFrameLimit", 3, 1);
14462}
14463
Kenichi Ishibashi6ac9e982023-04-27 00:10:5514464// Tests that opening a new tab from an existing page via ctrl-click reuses a
14465// process when both pages are the same-site.
14466IN_PROC_BROWSER_TEST_P(SitePerProcessWithMainFrameThresholdTest,
14467 ReuseProcessOpenTabByCtrlClickLink) {
14468 const GURL kUrl = embedded_test_server()->GetURL(
14469 "foo.test", "/ctrl-click-subframe-link.html");
14470 ASSERT_TRUE(NavigateToURL(shell(), kUrl));
14471 RenderFrameHostImpl* main_frame =
14472 static_cast<WebContentsImpl*>(shell()->web_contents())
14473 ->GetPrimaryMainFrame();
14474 ShellAddedObserver new_shell_observer;
14475 ASSERT_TRUE(ExecJs(main_frame,
14476 "window.domAutomationController.send(ctrlClickLink());"));
14477 Shell* popup = new_shell_observer.GetShell();
14478 ASSERT_EQ(main_frame->GetProcess(),
14479 static_cast<WebContentsImpl*>(popup->web_contents())
14480 ->GetPrimaryMainFrame()
14481 ->GetProcess());
14482}
14483
14484// Tests that opening a new tab from an existing page via window.open reuses a
14485// process when both pages are the same-site.
Alison Gale770f3fc2024-04-27 00:39:5814486// TODO(crbug.com/40264958): Change this test to use 'noopener' once we
Kenichi Ishibashi6ac9e982023-04-27 00:10:5514487// figure out how to handle navigation from an empty site to a new site.
14488IN_PROC_BROWSER_TEST_P(SitePerProcessWithMainFrameThresholdTest,
14489 ReuseProcessWithOpener) {
14490 const GURL kUrl = embedded_test_server()->GetURL("foo.test", "/title1.html");
14491 ASSERT_TRUE(NavigateToURL(shell(), kUrl));
14492 RenderFrameHostImpl* main_frame =
14493 static_cast<WebContentsImpl*>(shell()->web_contents())
14494 ->GetPrimaryMainFrame();
14495 ShellAddedObserver new_shell_observer;
14496 ASSERT_TRUE(
14497 ExecJs(main_frame, "popup = window.open('/title1.html', '_blank');"));
14498 Shell* popup = new_shell_observer.GetShell();
14499 ASSERT_EQ(main_frame->GetProcess(),
14500 static_cast<WebContentsImpl*>(popup->web_contents())
14501 ->GetPrimaryMainFrame()
14502 ->GetProcess());
14503}
14504
Kenichi Ishibashi8e087a62023-06-06 06:17:4114505class SitePerProcessWithMainFrameThresholdLocalhostTest
14506 : public SitePerProcessWithMainFrameThresholdTestBase,
14507 public ::testing::WithParamInterface<bool> {
14508 public:
14509 SitePerProcessWithMainFrameThresholdLocalhostTest() {
14510 scoped_feature_list_.InitAndEnableFeatureWithParameters(
14511 features::kProcessPerSiteUpToMainFrameThreshold,
14512 {{"ProcessPerSiteMainFrameThreshold",
Dave Tapuskae15164c2024-08-02 23:53:3514513 base::StringPrintf("%zu", kDefaultThreshold)},
Kenichi Ishibashi8e087a62023-06-06 06:17:4114514 {"ProcessPerSiteMainFrameAllowIPAndLocalhost",
Devon Loehrc0138d8d2025-03-04 01:11:4214515 base::ToString(IsLocalhostAllowed())}});
Kenichi Ishibashi8e087a62023-06-06 06:17:4114516 }
14517 ~SitePerProcessWithMainFrameThresholdLocalhostTest() override = default;
14518
14519 bool IsLocalhostAllowed() { return GetParam(); }
14520
14521 private:
14522 base::test::ScopedFeatureList scoped_feature_list_;
14523};
14524
14525// Tests that process reuse is allowed or disallowed for localhost based on a
14526// feature parameter.
14527IN_PROC_BROWSER_TEST_P(SitePerProcessWithMainFrameThresholdLocalhostTest,
14528 AllowReuseLocalHost) {
14529 const GURL kUrl = embedded_test_server()->GetURL("localhost", "/title1.html");
14530 ASSERT_TRUE(net::IsLocalHostname(kUrl.host()));
14531
14532 ASSERT_TRUE(NavigateToURL(shell(), kUrl));
14533 Shell* second_shell = CreateShellAndNavigateToURL(kUrl);
14534
14535 RenderFrameHostImpl* main_frame =
14536 static_cast<WebContentsImpl*>(shell()->web_contents())
14537 ->GetPrimaryMainFrame();
14538 RenderFrameHostImpl* second_frame =
14539 static_cast<WebContentsImpl*>(second_shell->web_contents())
14540 ->GetPrimaryMainFrame();
14541 if (IsLocalhostAllowed()) {
14542 ASSERT_EQ(main_frame->GetProcess(), second_frame->GetProcess());
14543 } else {
14544 ASSERT_NE(main_frame->GetProcess(), second_frame->GetProcess());
14545 }
14546}
14547
14548class SitePerProcessWithMainFrameThresholdDevToolsTest
14549 : public SitePerProcessWithMainFrameThresholdTestBase,
14550 public TestDevToolsProtocolClient {
14551 public:
14552 SitePerProcessWithMainFrameThresholdDevToolsTest() = default;
14553 ~SitePerProcessWithMainFrameThresholdDevToolsTest() override = default;
14554
14555 void TearDown() override {
14556 DetachProtocolClient();
14557 SitePerProcessWithMainFrameThresholdTestBase::TearDown();
14558 }
14559};
14560
14561// Tests that process reuse is diallowed when DevTools is attached to the
14562// renderer process.
14563IN_PROC_BROWSER_TEST_F(SitePerProcessWithMainFrameThresholdDevToolsTest,
14564 DevToolsAttached) {
14565 const GURL kUrl = embedded_test_server()->GetURL("foo.test", "/title1.html");
14566
14567 ASSERT_TRUE(NavigateToURL(shell(), kUrl));
14568
14569 AttachToWebContents(shell()->web_contents());
14570 set_agent_host_can_close();
14571
14572 Shell* second_shell = CreateShellAndNavigateToURL(kUrl);
14573 RenderFrameHostImpl* main_frame =
14574 static_cast<WebContentsImpl*>(shell()->web_contents())
14575 ->GetPrimaryMainFrame();
14576 RenderFrameHostImpl* second_frame =
14577 static_cast<WebContentsImpl*>(second_shell->web_contents())
14578 ->GetPrimaryMainFrame();
14579 ASSERT_NE(main_frame->GetProcess(), second_frame->GetProcess());
14580}
14581
Alex Moshchuke3bc9fe2024-09-11 19:11:0914582// Helper class to enable subframe process reuse thresholds and set the total
14583// allowed memory limit to 8 bytes.
14584class SitePerProcessWithSubframeProcessReuseThresholdsTest
14585 : public SitePerProcessBrowserTestBase,
14586 public ::testing::WithParamInterface<std::string> {
14587 public:
14588 SitePerProcessWithSubframeProcessReuseThresholdsTest() {
14589 size_t total_memory_limit = 8;
14590 base::FieldTrialParams params = {
14591 {"SubframeProcessReuseMemoryThreshold",
14592 base::StringPrintf("%zu", total_memory_limit)}};
14593 scoped_feature_list_.InitAndEnableFeatureWithParameters(
14594 features::kSubframeProcessReuseThresholds, params);
14595 }
14596 ~SitePerProcessWithSubframeProcessReuseThresholdsTest() override = default;
14597
14598 private:
14599 base::test::ScopedFeatureList scoped_feature_list_;
14600};
14601
14602// Verify that a subframe will only reuse an existing process if adding
14603// another subframe to that process won't exceed the memory threshold.
14604IN_PROC_BROWSER_TEST_P(SitePerProcessWithSubframeProcessReuseThresholdsTest,
14605 SubframeReuseRespectsMemoryThreshold) {
14606 base::HistogramTester histograms;
14607
14608 // Start with a simple a(b) page.
14609 GURL main_url(embedded_test_server()->GetURL(
14610 "a.com", "/cross_site_iframe_factory.html?a(b)"));
14611 EXPECT_TRUE(NavigateToURL(shell(), main_url));
14612 RenderFrameHostImpl* main_frame1 =
14613 static_cast<WebContentsImpl*>(shell()->web_contents())
14614 ->GetPrimaryMainFrame();
14615 RenderFrameHostImpl* subframe1 =
14616 main_frame1->child_at(0)->current_frame_host();
14617 auto* subframe_process =
14618 static_cast<RenderProcessHostImpl*>(subframe1->GetProcess());
14619 ASSERT_NE(main_frame1->GetProcess(), subframe_process);
14620
14621 // Ignore private memory footprint updates from the renderer, and pretend
14622 // that the subframe process's PMF is currently 5 bytes.
14623 RendererHostInterceptor interceptor(subframe_process);
14624 subframe_process->SetPrivateMemoryFootprintForTesting(5);
14625
14626 // Create an unrelated tab and navigate it to a(b).
14627 Shell* shell2 = CreateBrowser();
14628 EXPECT_TRUE(NavigateToURL(shell2, main_url));
14629 RenderFrameHostImpl* main_frame2 =
14630 static_cast<WebContentsImpl*>(shell2->web_contents())
14631 ->GetPrimaryMainFrame();
14632 RenderFrameHostImpl* subframe2 =
14633 main_frame2->child_at(0)->current_frame_host();
14634 ASSERT_NE(main_frame2->GetProcess(), subframe2->GetProcess());
14635
14636 // The new b.com subframe should reuse the available b.com process from the
14637 // first tab. This is because the process uses 5 bytes of memory, which is
14638 // below the reuse threshold of 8 bytes.
14639 EXPECT_EQ(subframe2->GetProcess(), subframe_process);
14640
14641 // Update the subframe process's PMF to 10, pretending that the second
14642 // subframe also takes up 5 bytes.
14643 subframe_process->SetPrivateMemoryFootprintForTesting(10);
14644
14645 // Create a third tab and navigate it to a(b).
14646 Shell* shell3 = CreateBrowser();
14647 EXPECT_TRUE(NavigateToURL(shell3, main_url));
14648 RenderFrameHostImpl* main_frame3 =
14649 static_cast<WebContentsImpl*>(shell3->web_contents())
14650 ->GetPrimaryMainFrame();
14651 RenderFrameHostImpl* subframe3 =
14652 main_frame3->child_at(0)->current_frame_host();
14653 ASSERT_NE(main_frame3->GetProcess(), subframe3->GetProcess());
14654
14655 // This time, the new b.com subframe should not reuse the available b.com
14656 // process from the first two tabs. This is because the process is consuming
14657 // 10 bytes of memory, which is above the reuse threshold of 8 bytes.
14658 EXPECT_NE(subframe3->GetProcess(), subframe_process);
14659
14660 // Check that the histogram was recorded when the memory threshold was
14661 // exceeded for `subframe_process`. At that time, the process should've had
14662 // two total frames.
14663 histograms.ExpectTotalCount(
14664 "BrowserRenderProcessHost.SubframeProcessReuseThreshold.TotalFrames", 1);
14665 histograms.ExpectBucketCount(
14666 "BrowserRenderProcessHost.SubframeProcessReuseThreshold.TotalFrames", 2,
14667 1);
14668}
14669
Fergal Daly2e7e1e12020-06-24 09:18:2814670INSTANTIATE_TEST_SUITE_P(All,
14671 RequestDelayingSitePerProcessBrowserTest,
14672 testing::ValuesIn(RenderDocumentFeatureLevelValues()));
Xiaohan Wang1ecfd002022-01-19 22:33:1014673#if BUILDFLAG(IS_ANDROID)
Fergal Daly2e7e1e12020-06-24 09:18:2814674INSTANTIATE_TEST_SUITE_P(All,
14675 SitePerProcessAndroidImeTest,
14676 testing::ValuesIn(RenderDocumentFeatureLevelValues()));
Aman Verma2e897242025-01-14 12:08:2614677INSTANTIATE_TEST_SUITE_P(All,
14678 AndroidInputBrowserTest,
14679 testing::ValuesIn(RenderDocumentFeatureLevelValues()));
Shin Kawamura7c2f4692025-07-30 00:14:3314680INSTANTIATE_TEST_SUITE_P(All,
14681 SitePerProcessBrowserTestWithSubframePriority,
14682 testing::ValuesIn(RenderDocumentFeatureLevelValues()));
Xiaohan Wang1ecfd002022-01-19 22:33:1014683#endif // BUILDFLAG(IS_ANDROID)
Fergal Daly2e7e1e12020-06-24 09:18:2814684INSTANTIATE_TEST_SUITE_P(All,
14685 SitePerProcessAndProcessPerSiteBrowserTest,
14686 testing::ValuesIn(RenderDocumentFeatureLevelValues()));
14687INSTANTIATE_TEST_SUITE_P(All,
14688 SitePerProcessAutoplayBrowserTest,
14689 testing::ValuesIn(RenderDocumentFeatureLevelValues()));
14690INSTANTIATE_TEST_SUITE_P(All,
14691 SitePerProcessBrowserTest,
14692 testing::ValuesIn(RenderDocumentFeatureLevelValues()));
14693INSTANTIATE_TEST_SUITE_P(All,
Dave Tapuskabca0c282024-09-20 21:29:3514694 SitePerProcessNoSharingBrowserTest,
14695 testing::ValuesIn(RenderDocumentFeatureLevelValues()));
14696INSTANTIATE_TEST_SUITE_P(All,
Jiacheng Guo782c9ac2024-08-01 08:14:2414697 SitePerProcessBrowserTestWithoutSpeculativeRFHDelay,
14698 testing::ValuesIn(RenderDocumentFeatureLevelValues()));
14699INSTANTIATE_TEST_SUITE_P(All,
Fergal Daly2e7e1e12020-06-24 09:18:2814700 SitePerProcessBrowserTouchActionTest,
14701 testing::ValuesIn(RenderDocumentFeatureLevelValues()));
14702INSTANTIATE_TEST_SUITE_P(All,
Fergal Daly2e7e1e12020-06-24 09:18:2814703 SitePerProcessIgnoreCertErrorsBrowserTest,
14704 testing::ValuesIn(RenderDocumentFeatureLevelValues()));
Sharon Yangf390ea92021-06-23 19:55:4014705INSTANTIATE_TEST_SUITE_P(All,
14706 DisableProcessReusePolicyTest,
14707 testing::ValuesIn(RenderDocumentFeatureLevelValues()));
Kenichi Ishibashi6ac9e982023-04-27 00:10:5514708INSTANTIATE_TEST_SUITE_P(All,
14709 SitePerProcessWithMainFrameThresholdTest,
14710 testing::ValuesIn(RenderDocumentFeatureLevelValues()));
Dave Tapuskae15164c2024-08-02 23:53:3514711INSTANTIATE_TEST_SUITE_P(All,
14712 SitePerProcessWithMainFrameThresholdWithTotalLimitTest,
14713 testing::ValuesIn(RenderDocumentFeatureLevelValues()));
Xiaohan Wang1ecfd002022-01-19 22:33:1014714#if BUILDFLAG(IS_ANDROID)
Fergal Daly2e7e1e12020-06-24 09:18:2814715INSTANTIATE_TEST_SUITE_P(All,
14716 TouchSelectionControllerClientAndroidSiteIsolationTest,
14717 testing::ValuesIn(RenderDocumentFeatureLevelValues()));
Xiaohan Wang1ecfd002022-01-19 22:33:1014718#endif // BUILDFLAG(IS_ANDROID)
Daniel Cheng463b5fd2020-12-12 02:59:5514719INSTANTIATE_TEST_SUITE_P(All,
14720 SitePerProcessBrowserTestWithLeakDetector,
14721 testing::ValuesIn(RenderDocumentFeatureLevelValues()));
14722
Kenichi Ishibashi8e087a62023-06-06 06:17:4114723INSTANTIATE_TEST_SUITE_P(All,
14724 SitePerProcessWithMainFrameThresholdLocalhostTest,
14725 testing::Bool());
14726
Alex Moshchuke3bc9fe2024-09-11 19:11:0914727INSTANTIATE_TEST_SUITE_P(All,
14728 SitePerProcessWithSubframeProcessReuseThresholdsTest,
14729 testing::ValuesIn(RenderDocumentFeatureLevelValues()));
14730
Alex Attarb82599d2025-05-13 14:30:0614731INSTANTIATE_TEST_SUITE_P(
14732 All,
14733 SitePerProcessWithMainFrameThresholdAndSiteRestrictionTest,
14734 testing::ValuesIn(RenderDocumentFeatureLevelValues()));
Mustaq Ahmed91a39092025-07-28 23:48:1214735INSTANTIATE_TEST_SUITE_P(All,
14736 StickyActivationAcrossSameOriginNavBrowserTest,
14737 testing::ValuesIn(RenderDocumentFeatureLevelValues()));
Alex Attarb82599d2025-05-13 14:30:0614738
[email protected]9b159a52013-10-03 17:24:5514739} // namespace content