blob: b453846ed6681ac120af01cbda7a27193e87e027 [file] [log] [blame]
[email protected]bbdd1b20b2012-12-11 21:24:131// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// 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>
9
nick59dcb162015-04-09 20:29:0110#include <algorithm>
Lukasz Anforowicz9e0ce4e2017-09-28 19:09:1511#include <list>
csharrisond86c35bc2017-02-02 17:41:2612#include <map>
Lukasz Anforowicz9e0ce4e2017-09-28 19:09:1513#include <memory>
Daniel Cheng999698bd2017-03-22 04:56:3714#include <set>
Ehsan Karamad192a8da2018-10-21 03:48:0815#include <string>
16#include <tuple>
Lukasz Anforowicz9e0ce4e2017-09-28 19:09:1517#include <utility>
nick59dcb162015-04-09 20:29:0118#include <vector>
19
csharrisond86c35bc2017-02-02 17:41:2620#include "base/bind.h"
21#include "base/callback.h"
[email protected]bbdd1b20b2012-12-11 21:24:1322#include "base/command_line.h"
chaopengd3ca34feb2017-04-20 17:11:2223#include "base/feature_list.h"
W. James MacLean99c4ba42017-12-01 14:44:4324#include "base/json/json_reader.h"
skyostil95082a62015-06-05 19:53:0725#include "base/location.h"
csharrisond86c35bc2017-02-02 17:41:2626#include "base/memory/ptr_util.h"
nasko39e3eb72016-06-24 23:15:4427#include "base/path_service.h"
Gabriel Charette078e3662017-08-28 22:59:0428#include "base/run_loop.h"
Avi Drissman808f72a22018-04-16 21:35:0929#include "base/scoped_observer.h"
csharrisonf2466b802016-11-19 23:57:1230#include "base/sequenced_task_runner.h"
skyostil95082a62015-06-05 19:53:0731#include "base/single_thread_task_runner.h"
Avi Drissman9d3ded92018-12-25 20:50:2132#include "base/stl_util.h"
brettwd97eede2015-07-06 22:09:0033#include "base/strings/pattern.h"
Ehsan Karamadf152db882017-10-23 17:41:2534#include "base/strings/string_number_conversions.h"
35#include "base/strings/string_split.h"
Arthur Sonzognif8840b92018-11-07 14:10:3536#include "base/strings/string_util.h"
[email protected]348fbaac2013-06-11 06:31:5137#include "base/strings/stringprintf.h"
lfg9ef7d2d2014-12-15 22:32:3038#include "base/strings/utf_string_conversions.h"
Eric Seckler8652dcd52018-09-20 10:42:2839#include "base/task/post_task.h"
Balazs Engedy1cb5c0e2018-01-30 14:34:2040#include "base/test/bind_test_util.h"
Devlin Cronin513398f2018-06-05 15:33:4941#include "base/test/metrics/histogram_tester.h"
kenrb162e42c2015-11-24 21:10:0042#include "base/test/test_timeouts.h"
csharrisonf2466b802016-11-19 23:57:1243#include "base/threading/sequenced_task_runner_handle.h"
gab30f26df2016-05-11 19:37:5544#include "base/threading/thread_task_runner_handle.h"
Arthur Sonzognif8840b92018-11-07 14:10:3545#include "base/time/time.h"
Lukasz Anforowicz4f0593d2018-09-21 18:33:5446#include "base/timer/timer.h"
avib7348942015-12-25 20:57:1047#include "build/build_config.h"
xidachenfa0199e72017-05-11 11:34:2648#include "cc/input/touch_action.h"
mmenke68a50212017-06-12 20:20:3149#include "components/network_session_configurator/common/network_switches.h"
W. James MacLeand973a55b2018-11-29 21:39:1350#include "components/viz/common/features.h"
clamyaf4bf2d92018-02-06 10:54:3651#include "content/browser/child_process_security_policy_impl.h"
[email protected]9a1abe72014-06-19 23:49:0252#include "content/browser/frame_host/cross_process_frame_connector.h"
Daniel Cheng999698bd2017-03-22 04:56:3753#include "content/browser/frame_host/frame_navigation_entry.h"
[email protected]d4a8ca482013-10-30 21:06:4054#include "content/browser/frame_host/frame_tree.h"
csharrison264bca642016-05-06 19:27:0955#include "content/browser/frame_host/interstitial_page_impl.h"
Daniel Cheng999698bd2017-03-22 04:56:3756#include "content/browser/frame_host/navigation_controller_impl.h"
57#include "content/browser/frame_host/navigation_entry_impl.h"
naskoe6edde32014-10-17 15:36:4858#include "content/browser/frame_host/navigator.h"
Lukasz Anforowicz9e0ce4e2017-09-28 19:09:1559#include "content/browser/frame_host/render_frame_host_impl.h"
[email protected]9a1abe72014-06-19 23:49:0260#include "content/browser/frame_host/render_frame_proxy_host.h"
kenrb2a565f82015-09-02 20:24:5961#include "content/browser/gpu/compositor_util.h"
estark693d1282016-03-15 22:41:2962#include "content/browser/loader/resource_dispatcher_host_impl.h"
Dave Tapuskac268151b82017-06-12 13:59:2163#include "content/browser/renderer_host/input/input_router.h"
W. James MacLeand973a55b2018-11-29 21:39:1364#include "content/browser/renderer_host/input/synthetic_gesture.h"
W. James MacLean53a6e842018-10-17 16:19:0165#include "content/browser/renderer_host/input/synthetic_gesture_target.h"
David Bokan469e7e42018-11-05 14:50:2966#include "content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h"
W. James MacLeanad0bd35f2018-12-12 22:10:0567#include "content/browser/renderer_host/input/synthetic_tap_gesture.h"
W. James MacLean53a6e842018-10-17 16:19:0168#include "content/browser/renderer_host/input/synthetic_touchscreen_pinch_gesture.h"
[email protected]9b159a52013-10-03 17:24:5569#include "content/browser/renderer_host/render_view_host_impl.h"
kenrb2a565f82015-09-02 20:24:5970#include "content/browser/renderer_host/render_widget_host_input_event_router.h"
Ken Buchanandaef006b2017-08-17 18:32:1571#include "content/browser/renderer_host/render_widget_host_view_child_frame.h"
jamc1905862017-05-16 14:45:3072#include "content/browser/storage_partition_impl.h"
73#include "content/browser/url_loader_factory_getter.h"
estarkcd2e30c2016-08-12 06:51:1574#include "content/browser/web_contents/web_contents_impl.h"
creis0f6edddc2015-04-08 00:20:5275#include "content/common/frame_messages.h"
W. James MacLeand973a55b2018-11-29 21:39:1376#include "content/common/input/actions_parser.h"
77#include "content/common/input/synthetic_pinch_gesture_params.h"
alexmos9aa61232016-04-26 21:54:0278#include "content/common/input_messages.h"
W. James MacLean62198672019-06-04 16:46:1079#include "content/common/page_messages.h"
rockot53be7caf2016-10-04 20:17:0880#include "content/common/renderer.mojom.h"
kenrb77db5072015-10-15 19:21:2581#include "content/common/view_messages.h"
jamc1905862017-05-16 14:45:3082#include "content/public/browser/browser_context.h"
Eric Seckler8652dcd52018-09-20 10:42:2883#include "content/public/browser/browser_task_traits.h"
csharrisond86c35bc2017-02-02 17:41:2684#include "content/public/browser/browser_thread.h"
csharrison264bca642016-05-06 19:27:0985#include "content/public/browser/interstitial_page_delegate.h"
Avi Drissmanc3736ed62018-10-31 22:07:0586#include "content/public/browser/javascript_dialog_manager.h"
jam419c0f12016-10-13 02:09:1687#include "content/public/browser/navigation_handle.h"
[email protected]bbdd1b20b2012-12-11 21:24:1388#include "content/public/browser/notification_observer.h"
89#include "content/public/browser/notification_service.h"
90#include "content/public/browser/notification_types.h"
Avi Drissman808f72a22018-04-16 21:35:0991#include "content/public/browser/render_widget_host_observer.h"
jama86c1ba2017-07-05 17:12:3892#include "content/public/common/content_features.h"
[email protected]bbdd1b20b2012-12-11 21:24:1393#include "content/public/common/content_switches.h"
Arthur Hemeryc65cae02019-02-28 10:26:1194#include "content/public/common/navigation_policy.h"
alexmos5b50b6742016-03-17 20:38:0595#include "content/public/common/url_constants.h"
Ken Buchanan75dd4f62018-04-26 18:37:4996#include "content/public/common/use_zoom_for_dsf_policy.h"
[email protected]bbdd1b20b2012-12-11 21:24:1397#include "content/public/test/browser_test_utils.h"
[email protected]6e9def12014-03-27 20:23:2898#include "content/public/test/content_browser_test_utils.h"
jonross09d21de2018-06-13 12:31:3699#include "content/public/test/hit_test_region_observer.h"
jamcb4ae152017-05-19 01:35:51100#include "content/public/test/navigation_handle_observer.h"
lukasza67fdcaf2016-11-16 00:27:52101#include "content/public/test/test_frame_navigation_observer.h"
nasko3e8c20e2014-12-18 06:54:56102#include "content/public/test/test_navigation_observer.h"
[email protected]bbdd1b20b2012-12-11 21:24:13103#include "content/public/test/test_utils.h"
John Abd-El-Maleka1e25a3a2017-12-04 23:21:08104#include "content/public/test/url_loader_interceptor.h"
csharrisonf2466b802016-11-19 23:57:12105#include "content/shell/browser/shell.h"
W. James MacLean99c4ba42017-12-01 14:44:43106#include "content/shell/common/shell_switches.h"
[email protected]893558b2014-04-25 23:01:06107#include "content/test/content_browser_test_utils_internal.h"
Arthur Hemery2a0a28be2019-03-06 17:51:36108#include "content/test/did_commit_navigation_interceptor.h"
Ken Rockotfd907632017-09-14 04:23:41109#include "ipc/constants.mojom.h"
creis0f6edddc2015-04-08 00:20:52110#include "ipc/ipc_security_test_util.h"
Becca Hughes60af7d42017-12-12 10:53:15111#include "media/base/media_switches.h"
jambcc67882017-04-28 18:20:09112#include "mojo/public/cpp/bindings/strong_binding.h"
[email protected]9b159a52013-10-03 17:24:55113#include "net/dns/mock_host_resolver.h"
naskocbce0e62014-10-07 14:04:26114#include "net/test/embedded_test_server/embedded_test_server.h"
csharrisond86c35bc2017-02-02 17:41:26115#include "net/test/embedded_test_server/http_request.h"
116#include "net/test/embedded_test_server/http_response.h"
rhalavati4cda417b2017-06-12 11:00:24117#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
Yutaka Hiranod8789f92018-01-30 09:59:51118#include "services/network/public/cpp/features.h"
lunalufec23402017-02-22 16:49:34119#include "testing/gmock/include/gmock/gmock.h"
lukasza8e1c02e42016-05-17 20:05:10120#include "testing/gtest/include/gtest/gtest.h"
Blink Reformata30d4232018-04-07 15:31:06121#include "third_party/blink/public/common/feature_policy/feature_policy.h"
Luna Lu0c2cd5a2019-02-12 16:24:59122#include "third_party/blink/public/common/feature_policy/policy_value.h"
Blink Reformata30d4232018-04-07 15:31:06123#include "third_party/blink/public/common/frame/sandbox_flags.h"
124#include "third_party/blink/public/platform/web_input_event.h"
125#include "third_party/blink/public/platform/web_insecure_request_policy.h"
oshima06b39602016-05-11 02:40:10126#include "ui/display/display_switches.h"
wjmaclean8a795f32016-08-11 23:49:58127#include "ui/display/screen.h"
dtapuska899ac222017-01-03 18:09:16128#include "ui/events/base_event_utils.h"
Xida Chena964d8a92019-03-15 20:09:19129#include "ui/events/blink/blink_features.h"
wjmacleanfab616a2016-03-02 18:40:39130#include "ui/events/event.h"
131#include "ui/events/event_utils.h"
sahel8505f882017-07-04 15:46:16132#include "ui/events/gesture_detection/gesture_configuration.h"
Kevin McNeee21d23b2018-06-29 15:25:04133#include "ui/events/keycodes/dom/dom_code.h"
134#include "ui/events/keycodes/dom/dom_key.h"
135#include "ui/events/keycodes/dom/keycode_converter.h"
136#include "ui/events/keycodes/keyboard_codes.h"
wjmacleanfab616a2016-03-02 18:40:39137#include "ui/gfx/geometry/point.h"
EhsanKbd2cea992017-11-23 18:49:08138#include "ui/gfx/geometry/rect.h"
mfomitchev3ba450ad2017-04-03 18:20:40139#include "ui/latency/latency_info.h"
chaopengd3ca34feb2017-04-20 17:11:22140#include "ui/native_theme/native_theme_features.h"
[email protected]bbdd1b20b2012-12-11 21:24:13141
wjmacleanfab616a2016-03-02 18:40:39142#if defined(USE_AURA)
Scott Violet1098538e2017-10-05 19:23:33143#include "content/browser/renderer_host/render_widget_host_view_aura.h"
Scott Violet3c03cfef2019-04-03 22:22:06144#include "ui/aura/window.h"
145#include "ui/aura/window_tree_host.h"
wjmacleanfab616a2016-03-02 18:40:39146#endif
147
ahest1d019b72016-02-02 07:24:42148#if defined(OS_MACOSX)
W. James MacLean2a90bff2018-11-05 20:52:47149#include "content/browser/renderer_host/input/synthetic_touchpad_pinch_gesture.h"
spqchan83534ab02016-02-23 19:28:27150#include "ui/base/test/scoped_preferred_scroller_style_mac.h"
ahest1d019b72016-02-02 07:24:42151#endif
152
ekaramada06dc6c2017-02-22 20:07:43153#if defined(OS_ANDROID)
154#include "base/android/jni_android.h"
155#include "base/android/jni_string.h"
156#include "base/android/scoped_java_ref.h"
jinsukkim48f82db72017-04-05 04:10:26157#include "content/browser/android/ime_adapter_android.h"
W. James MacLean12ba7972017-07-08 01:58:26158#include "content/browser/renderer_host/input/touch_selection_controller_client_manager_android.h"
ekaramada06dc6c2017-02-22 20:07:43159#include "content/browser/renderer_host/render_widget_host_view_android.h"
Kevin McNee18430052018-08-10 16:50:56160#include "content/browser/web_contents/web_contents_view_android.h"
Bo Liu168c8642017-08-28 18:26:02161#include "content/public/browser/android/child_process_importance.h"
Kevin McNee80e0a452017-08-09 14:42:18162#include "content/test/mock_overscroll_refresh_handler_android.h"
Kevin McNee18430052018-08-10 16:50:56163#include "ui/android/view_android.h"
164#include "ui/android/window_android.h"
165#include "ui/events/android/event_handler_android.h"
Jinsuk Kim2b859472017-08-30 02:47:19166#include "ui/events/android/motion_event_android.h"
W. James MacLean12ba7972017-07-08 01:58:26167#include "ui/gfx/geometry/point_f.h"
ekaramada06dc6c2017-02-22 20:07:43168#endif
169
Mitsuru Oshima3c07b332018-02-06 04:56:50170#if defined(OS_CHROMEOS)
Mitsuru Oshima3c07b332018-02-06 04:56:50171#include "ui/aura/test/test_screen.h"
172#endif
173
Daniel Cheng999698bd2017-03-22 04:56:37174using ::testing::SizeIs;
Arthur Sonzognif8840b92018-11-07 14:10:35175using ::testing::WhenSorted;
176using ::testing::ElementsAre;
Daniel Cheng999698bd2017-03-22 04:56:37177
[email protected]bbdd1b20b2012-12-11 21:24:13178namespace content {
179
alexmos9f8705a2015-05-06 19:58:59180namespace {
181
182// Helper function to send a postMessage and wait for a reply message. The
183// |post_message_script| is executed on the |sender_ftn| frame, and the sender
184// frame is expected to post |reply_status| from the DOMAutomationController
185// when it receives a reply.
186void PostMessageAndWaitForReply(FrameTreeNode* sender_ftn,
187 const std::string& post_message_script,
188 const std::string& reply_status) {
alexmos17e57532015-08-15 02:54:36189 // Subtle: msg_queue needs to be declared before the ExecuteScript below, or
190 // else it might miss the message of interest. See https://crbug.com/518729.
191 DOMMessageQueue msg_queue;
192
Nick Carterb7e71312018-08-03 23:36:13193 EXPECT_EQ(true, EvalJs(sender_ftn, "(" + post_message_script + ");"));
alexmos9f8705a2015-05-06 19:58:59194
alexmos9f8705a2015-05-06 19:58:59195 std::string status;
196 while (msg_queue.WaitForMessage(&status)) {
197 if (status == reply_status)
198 break;
199 }
200}
201
alexmos58729042015-06-18 23:20:00202// Helper function to extract and return "window.receivedMessages" from the
203// |sender_ftn| frame. This variable is used in post_message.html to count the
204// number of messages received via postMessage by the current window.
205int GetReceivedMessages(FrameTreeNode* ftn) {
Nick Carterb7e71312018-08-03 23:36:13206 return EvalJs(ftn, "window.receivedMessages;").ExtractInt();
alexmos58729042015-06-18 23:20:00207}
208
alexmos646fec02015-07-25 00:11:49209// Helper function to perform a window.open from the |caller_frame| targeting a
210// frame with the specified name.
211void NavigateNamedFrame(const ToRenderFrameHost& caller_frame,
212 const GURL& url,
213 const std::string& name) {
Nick Carterb7e71312018-08-03 23:36:13214 EXPECT_EQ(true, EvalJs(caller_frame,
215 JsReplace("!!window.open($1, $2)", url, name)));
alexmos646fec02015-07-25 00:11:49216}
217
alexmosb1dc2162015-11-05 00:59:20218// Helper function to generate a click on the given RenderWidgetHost. The
219// mouse event is forwarded directly to the RenderWidgetHost without any
220// hit-testing.
221void SimulateMouseClick(RenderWidgetHost* rwh, int x, int y) {
Daniel Cheng93c80a92018-02-14 19:02:43222 blink::WebMouseEvent mouse_event(
223 blink::WebInputEvent::kMouseDown, blink::WebInputEvent::kNoModifiers,
224 blink::WebInputEvent::GetStaticTimeStampForTests());
Blink Reformat1c4d759e2017-04-09 16:34:54225 mouse_event.button = blink::WebPointerProperties::Button::kLeft;
226 mouse_event.SetPositionInWidget(x, y);
alexmosb1dc2162015-11-05 00:59:20227 rwh->ForwardMouseEvent(mouse_event);
228}
229
Philip Jägenstedt67302a22018-09-14 09:58:05230// Retrieve self.origin for the frame |ftn|.
231EvalJsResult GetOriginFromRenderer(FrameTreeNode* ftn) {
232 return EvalJs(ftn, "self.origin;");
alexmos6e940102016-01-19 22:47:25233}
234
kenrb2b5d08b2017-03-08 22:31:29235double GetFrameDeviceScaleFactor(const ToRenderFrameHost& adapter) {
Nick Carterb7e71312018-08-03 23:36:13236 return EvalJs(adapter, "window.devicePixelRatio;").ExtractDouble();
kenrb2b5d08b2017-03-08 22:31:29237}
238
[email protected]bbdd1b20b2012-12-11 21:24:13239class RedirectNotificationObserver : public NotificationObserver {
240 public:
241 // Register to listen for notifications of the given type from either a
242 // specific source, or from all sources if |source| is
243 // NotificationService::AllSources().
244 RedirectNotificationObserver(int notification_type,
245 const NotificationSource& source);
dchengc2282aa2014-10-21 12:07:58246 ~RedirectNotificationObserver() override;
[email protected]bbdd1b20b2012-12-11 21:24:13247
248 // Wait until the specified notification occurs. If the notification was
249 // emitted between the construction of this object and this call then it
250 // returns immediately.
251 void Wait();
252
253 // Returns NotificationService::AllSources() if we haven't observed a
254 // notification yet.
255 const NotificationSource& source() const {
256 return source_;
257 }
258
259 const NotificationDetails& details() const {
260 return details_;
261 }
262
263 // NotificationObserver:
dchengc2282aa2014-10-21 12:07:58264 void Observe(int type,
265 const NotificationSource& source,
266 const NotificationDetails& details) override;
[email protected]bbdd1b20b2012-12-11 21:24:13267
268 private:
269 bool seen_;
270 bool seen_twice_;
271 bool running_;
272 NotificationRegistrar registrar_;
273
274 NotificationSource source_;
275 NotificationDetails details_;
Lukasz Anforowicz5a92f052018-08-15 20:38:47276 base::RunLoop run_loop_;
[email protected]bbdd1b20b2012-12-11 21:24:13277
278 DISALLOW_COPY_AND_ASSIGN(RedirectNotificationObserver);
279};
280
281RedirectNotificationObserver::RedirectNotificationObserver(
282 int notification_type,
283 const NotificationSource& source)
284 : seen_(false),
285 running_(false),
286 source_(NotificationService::AllSources()) {
287 registrar_.Add(this, notification_type, source);
288}
289
290RedirectNotificationObserver::~RedirectNotificationObserver() {}
291
292void RedirectNotificationObserver::Wait() {
293 if (seen_ && seen_twice_)
294 return;
295
296 running_ = true;
Lukasz Anforowicz5a92f052018-08-15 20:38:47297 run_loop_.Run();
[email protected]bbdd1b20b2012-12-11 21:24:13298 EXPECT_TRUE(seen_);
299}
300
301void RedirectNotificationObserver::Observe(
302 int type,
303 const NotificationSource& source,
304 const NotificationDetails& details) {
305 source_ = source;
306 details_ = details;
307 seen_twice_ = seen_;
308 seen_ = true;
309 if (!running_)
310 return;
311
Lukasz Anforowicz5a92f052018-08-15 20:38:47312 run_loop_.Quit();
[email protected]bbdd1b20b2012-12-11 21:24:13313 running_ = false;
314}
315
alexmos35d7b932014-12-05 03:55:23316// This observer keeps track of the number of created RenderFrameHosts. Tests
317// can use this to ensure that a certain number of child frames has been
318// created after navigating.
319class RenderFrameHostCreatedObserver : public WebContentsObserver {
320 public:
321 RenderFrameHostCreatedObserver(WebContents* web_contents,
322 int expected_frame_count)
323 : WebContentsObserver(web_contents),
324 expected_frame_count_(expected_frame_count),
Lukasz Anforowicz5a92f052018-08-15 20:38:47325 frames_created_(0) {}
alexmos35d7b932014-12-05 03:55:23326
327 ~RenderFrameHostCreatedObserver() override;
328
gab2998ee72017-05-05 16:23:50329 // Runs a nested run loop and blocks until the expected number of
alexmos35d7b932014-12-05 03:55:23330 // RenderFrameHosts is created.
331 void Wait();
332
333 private:
334 // WebContentsObserver
335 void RenderFrameCreated(RenderFrameHost* render_frame_host) override;
336
337 // The number of RenderFrameHosts to wait for.
338 int expected_frame_count_;
339
340 // The number of RenderFrameHosts that have been created.
341 int frames_created_;
342
Lukasz Anforowicz5a92f052018-08-15 20:38:47343 // The RunLoop used to spin the message loop.
344 base::RunLoop run_loop_;
alexmos35d7b932014-12-05 03:55:23345
346 DISALLOW_COPY_AND_ASSIGN(RenderFrameHostCreatedObserver);
347};
348
349RenderFrameHostCreatedObserver::~RenderFrameHostCreatedObserver() {
350}
351
352void RenderFrameHostCreatedObserver::Wait() {
Lukasz Anforowicz5a92f052018-08-15 20:38:47353 run_loop_.Run();
alexmos35d7b932014-12-05 03:55:23354}
355
356void RenderFrameHostCreatedObserver::RenderFrameCreated(
357 RenderFrameHost* render_frame_host) {
358 frames_created_++;
359 if (frames_created_ == expected_frame_count_) {
Lukasz Anforowicz5a92f052018-08-15 20:38:47360 run_loop_.Quit();
alexmos35d7b932014-12-05 03:55:23361 }
362}
363
kenrb19221852016-04-29 17:21:40364// This observer detects when WebContents receives notification of a user
365// gesture having occurred, following a user input event targeted to
366// a RenderWidgetHost under that WebContents.
367class UserInteractionObserver : public WebContentsObserver {
368 public:
369 explicit UserInteractionObserver(WebContents* web_contents)
370 : WebContentsObserver(web_contents), user_interaction_received_(false) {}
371
372 ~UserInteractionObserver() override {}
373
374 // Retrieve the flag. There is no need to wait on a loop since
375 // DidGetUserInteraction() should be called synchronously with the input
376 // event processing in the browser process.
377 bool WasUserInteractionReceived() { return user_interaction_received_; }
378
379 void Reset() { user_interaction_received_ = false; }
380
381 private:
382 // WebContentsObserver
383 void DidGetUserInteraction(const blink::WebInputEvent::Type type) override {
384 user_interaction_received_ = true;
385 }
386
387 bool user_interaction_received_;
388
389 DISALLOW_COPY_AND_ASSIGN(UserInteractionObserver);
390};
391
alexmos21acae52015-11-07 01:04:43392// Helper function to focus a frame by sending it a mouse click and then
393// waiting for it to become focused.
394void FocusFrame(FrameTreeNode* frame) {
avallee0206f782016-07-28 18:55:33395 FrameFocusedObserver focus_observer(frame->current_frame_host());
alexmos21acae52015-11-07 01:04:43396 SimulateMouseClick(frame->current_frame_host()->GetRenderWidgetHost(), 1, 1);
397 focus_observer.Wait();
398}
399
Avi Drissman808f72a22018-04-16 21:35:09400class RenderWidgetHostVisibilityObserver : public RenderWidgetHostObserver {
ekaramadbabb9bf2016-01-12 15:17:02401 public:
402 explicit RenderWidgetHostVisibilityObserver(RenderWidgetHostImpl* rwhi,
403 bool expected_visibility_state)
404 : expected_visibility_state_(expected_visibility_state),
Avi Drissman808f72a22018-04-16 21:35:09405 observer_(this),
ekaramadbabb9bf2016-01-12 15:17:02406 was_observed_(false),
407 did_fail_(false),
Avi Drissman808f72a22018-04-16 21:35:09408 render_widget_(rwhi) {
409 observer_.Add(render_widget_);
ekaramadbabb9bf2016-01-12 15:17:02410 message_loop_runner_ = new MessageLoopRunner;
411 }
412
413 bool WaitUntilSatisfied() {
414 if (!was_observed_)
415 message_loop_runner_->Run();
Avi Drissman808f72a22018-04-16 21:35:09416 if (observer_.IsObserving(render_widget_))
417 observer_.Remove(render_widget_);
ekaramadbabb9bf2016-01-12 15:17:02418 return !did_fail_;
419 }
420
421 private:
Avi Drissman808f72a22018-04-16 21:35:09422 void RenderWidgetHostVisibilityChanged(RenderWidgetHost* widget_host,
423 bool became_visible) override {
ekaramadbabb9bf2016-01-12 15:17:02424 was_observed_ = true;
Avi Drissman808f72a22018-04-16 21:35:09425 did_fail_ = expected_visibility_state_ != became_visible;
ekaramadbabb9bf2016-01-12 15:17:02426 if (message_loop_runner_->loop_running())
427 message_loop_runner_->Quit();
428 }
429
Avi Drissman808f72a22018-04-16 21:35:09430 void RenderWidgetHostDestroyed(RenderWidgetHost* widget_host) override {
431 observer_.Remove(widget_host);
432 }
433
ekaramadbabb9bf2016-01-12 15:17:02434 bool expected_visibility_state_;
435 scoped_refptr<MessageLoopRunner> message_loop_runner_;
Avi Drissman808f72a22018-04-16 21:35:09436 ScopedObserver<RenderWidgetHost, RenderWidgetHostObserver> observer_;
ekaramadbabb9bf2016-01-12 15:17:02437 bool was_observed_;
438 bool did_fail_;
Avi Drissman808f72a22018-04-16 21:35:09439 RenderWidgetHost* render_widget_;
ekaramadbabb9bf2016-01-12 15:17:02440
441 DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostVisibilityObserver);
442};
443
csharrison264bca642016-05-06 19:27:09444class TestInterstitialDelegate : public InterstitialPageDelegate {
445 private:
446 // InterstitialPageDelegate:
447 std::string GetHTMLContents() override { return "<p>Interstitial</p>"; }
448};
449
W. James MacLean99c4ba42017-12-01 14:44:43450bool ConvertJSONToPoint(const std::string& str, gfx::PointF* point) {
Avi Drissman57e5d7df2019-03-21 19:12:53451 base::Optional<base::Value> value = base::JSONReader::Read(str);
452 if (!value.has_value())
W. James MacLean99c4ba42017-12-01 14:44:43453 return false;
Avi Drissman57e5d7df2019-03-21 19:12:53454 if (!value->is_dict())
W. James MacLean99c4ba42017-12-01 14:44:43455 return false;
Avi Drissman57e5d7df2019-03-21 19:12:53456 base::Optional<double> x = value->FindDoubleKey("x");
457 base::Optional<double> y = value->FindDoubleKey("y");
458 if (!x.has_value())
W. James MacLean99c4ba42017-12-01 14:44:43459 return false;
Avi Drissman57e5d7df2019-03-21 19:12:53460 if (!y.has_value())
W. James MacLean99c4ba42017-12-01 14:44:43461 return false;
Avi Drissman57e5d7df2019-03-21 19:12:53462 point->set_x(x.value());
463 point->set_y(y.value());
W. James MacLean99c4ba42017-12-01 14:44:43464 return true;
465}
W. James MacLean99c4ba42017-12-01 14:44:43466
Mounir Lamourif7246b92018-01-30 18:16:33467void OpenURLBlockUntilNavigationComplete(Shell* shell, const GURL& url) {
468 WaitForLoadStop(shell->web_contents());
469 TestNavigationObserver same_tab_observer(shell->web_contents(), 1);
470
471 OpenURLParams params(
472 url,
473 content::Referrer(shell->web_contents()->GetLastCommittedURL(),
Richard Li49fe04d2018-10-21 09:07:19474 network::mojom::ReferrerPolicy::kAlways),
Mounir Lamourif7246b92018-01-30 18:16:33475 WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_LINK,
476 true /* is_renderer_initiated */);
Nasko Oskov93e7c55c2018-12-19 01:59:29477 params.initiator_origin = url::Origin::Create(url);
Mounir Lamourif7246b92018-01-30 18:16:33478 shell->OpenURLFromTab(shell->web_contents(), params);
479
480 same_tab_observer.Wait();
481}
482
Ian Clelland91cff7ae2018-02-15 16:51:37483// Helper function to generate a feature policy for a single feature and a list
484// of origins. (Equivalent to the declared policy "feature origin1 origin2...".)
Luna Lu58c77b02019-03-05 20:13:46485void SetParsedFeaturePolicyDeclaration(
486 blink::ParsedFeaturePolicyDeclaration* declaration,
Ian Clelland91cff7ae2018-02-15 16:51:37487 blink::mojom::FeaturePolicyFeature feature,
488 const std::vector<GURL>& origins) {
Luna Lu58c77b02019-03-05 20:13:46489 declaration->feature = feature;
490 blink::mojom::PolicyValueType feature_type =
491 blink::FeaturePolicy::GetDefaultFeatureList().at(feature).second;
492 declaration->fallback_value =
493 blink::PolicyValue::CreateMinPolicyValue(feature_type);
494 declaration->opaque_value = declaration->fallback_value;
495 if (feature == blink::mojom::FeaturePolicyFeature::kOversizedImages) {
496 declaration->fallback_value.SetDoubleValue(2.0);
497 declaration->opaque_value.SetDoubleValue(2.0);
498 }
Ian Clelland91cff7ae2018-02-15 16:51:37499 DCHECK(!origins.empty());
Luna Lu58c77b02019-03-05 20:13:46500 for (const auto origin : origins)
501 declaration->values.insert(std::pair<url::Origin, blink::PolicyValue>(
502 url::Origin::Create(origin),
503 blink::PolicyValue::CreateMaxPolicyValue(feature_type)));
504}
505
506blink::ParsedFeaturePolicy CreateFPHeader(
507 blink::mojom::FeaturePolicyFeature feature1,
508 blink::mojom::FeaturePolicyFeature feature2,
509 const std::vector<GURL>& origins) {
510 blink::ParsedFeaturePolicy result(2);
511 SetParsedFeaturePolicyDeclaration(&(result[0]), feature1, origins);
512 SetParsedFeaturePolicyDeclaration(&(result[1]), feature2, origins);
Ian Clelland91cff7ae2018-02-15 16:51:37513 return result;
514}
515
516// Helper function to generate a feature policy for a single feature which
Luna Lu58c77b02019-03-05 20:13:46517// matches every origin. (Equivalent to the declared policy "feature1 *;
518// feature2 *".)
Ian Clelland91cff7ae2018-02-15 16:51:37519blink::ParsedFeaturePolicy CreateFPHeaderMatchesAll(
Luna Lu58c77b02019-03-05 20:13:46520 blink::mojom::FeaturePolicyFeature feature1,
521 blink::mojom::FeaturePolicyFeature feature2) {
522 blink::ParsedFeaturePolicy result(2);
523 blink::mojom::PolicyValueType feature_type1 =
524 blink::FeaturePolicy::GetDefaultFeatureList().at(feature1).second;
525 blink::mojom::PolicyValueType feature_type2 =
526 blink::FeaturePolicy::GetDefaultFeatureList().at(feature2).second;
527 blink::PolicyValue max_value1 =
528 blink::PolicyValue::CreateMaxPolicyValue(feature_type1);
529 blink::PolicyValue max_value2 =
530 blink::PolicyValue::CreateMaxPolicyValue(feature_type2);
531 result[0].feature = feature1;
532 result[0].fallback_value = max_value1;
533 result[0].opaque_value = max_value1;
534 result[1].feature = feature2;
535 result[1].fallback_value = max_value2;
536 result[1].opaque_value = max_value2;
Ian Clelland91cff7ae2018-02-15 16:51:37537 return result;
538}
539
Bo Liua13e7c02018-03-28 22:24:02540// Check frame depth on node, widget, and process all match expected depth.
541void CheckFrameDepth(unsigned int expected_depth, FrameTreeNode* node) {
542 EXPECT_EQ(expected_depth, node->depth());
543 RenderProcessHost::Priority priority =
544 node->current_frame_host()->GetRenderWidgetHost()->GetPriority();
545 EXPECT_EQ(expected_depth, priority.frame_depth);
Bo Liu2f75db32018-05-03 00:56:21546 EXPECT_EQ(expected_depth,
547 node->current_frame_host()->GetProcess()->GetFrameDepth());
Bo Liua13e7c02018-03-28 22:24:02548}
549
Bo Liuf3a5a642018-07-30 17:28:44550// Check |intersects_viewport| on widget and process.
551bool CheckIntersectsViewport(bool expected, FrameTreeNode* node) {
552 RenderProcessHost::Priority priority =
553 node->current_frame_host()->GetRenderWidgetHost()->GetPriority();
554 return priority.intersects_viewport == expected &&
555 node->current_frame_host()->GetProcess()->GetIntersectsViewport() ==
556 expected;
557}
558
559// Layout child frames in cross_site_iframe_factory.html so that they are the
560// same width as the viewport, and 75% of the height of the window. This is for
561// testing viewport intersection. Note this does not recurse into child frames
562// and re-layout in the same way since children might be in a different origin.
563void LayoutNonRecursiveForTestingViewportIntersection(
564 WebContents* web_contents) {
Lukasz Anforowicz3cfc1efd2019-02-22 02:29:29565 static const char* script = R"(
566 function relayoutNonRecursiveForTestingViewportIntersection() {
567 var width = window.innerWidth;
568 var height = window.innerHeight * 0.75;
569 for (var i = 0; i < window.frames.length; i++) {
570 child = document.getElementById("child-" + i);
571 child.width = width;
572 child.height = height;
573 }
574 }
575 relayoutNonRecursiveForTestingViewportIntersection();
576 )";
Bo Liuf3a5a642018-07-30 17:28:44577 EXPECT_TRUE(ExecuteScript(web_contents, script));
578}
579
Xida Chen2eec314f2018-06-07 11:42:42580void GenerateTapDownGesture(RenderWidgetHost* rwh) {
581 blink::WebGestureEvent gesture_tap_down(
582 blink::WebGestureEvent::kGestureTapDown,
583 blink::WebInputEvent::kNoModifiers,
584 blink::WebInputEvent::GetStaticTimeStampForTests(),
Daniel Cheng7f9ec902019-04-18 05:07:00585 blink::WebGestureDevice::kTouchscreen);
Xida Chen2eec314f2018-06-07 11:42:42586 gesture_tap_down.is_source_touch_event_set_non_blocking = true;
587 rwh->ForwardGestureEvent(gesture_tap_down);
588}
589
Bo Liuf3a5a642018-07-30 17:28:44590// Class to monitor incoming FrameHostMsg_UpdateViewportIntersection messages.
591class UpdateViewportIntersectionMessageFilter
592 : public content::BrowserMessageFilter {
593 public:
594 UpdateViewportIntersectionMessageFilter()
595 : content::BrowserMessageFilter(FrameMsgStart), msg_received_(false) {}
596
597 bool OnMessageReceived(const IPC::Message& message) override {
598 IPC_BEGIN_MESSAGE_MAP(UpdateViewportIntersectionMessageFilter, message)
599 IPC_MESSAGE_HANDLER(FrameHostMsg_UpdateViewportIntersection,
600 OnUpdateViewportIntersection)
601 IPC_END_MESSAGE_MAP()
602 return false;
603 }
604
605 gfx::Rect GetCompositingRect() const { return compositing_rect_; }
606 gfx::Rect GetViewportIntersection() const { return viewport_intersection_; }
Daniel Chengaa37a482019-03-21 00:24:20607 blink::FrameOcclusionState GetOcclusionState() const {
608 return occlusion_state_;
609 }
Bo Liuf3a5a642018-07-30 17:28:44610
611 void Wait() {
612 DCHECK(!run_loop_);
613 if (msg_received_) {
614 msg_received_ = false;
615 return;
616 }
617 std::unique_ptr<base::RunLoop> run_loop(new base::RunLoop);
618 run_loop_ = run_loop.get();
619 run_loop_->Run();
620 run_loop_ = nullptr;
621 msg_received_ = false;
622 }
623
624 void set_run_loop(base::RunLoop* run_loop) { run_loop_ = run_loop; }
625
626 private:
627 ~UpdateViewportIntersectionMessageFilter() override {}
628
Daniel Chengaa37a482019-03-21 00:24:20629 void OnUpdateViewportIntersection(
630 const gfx::Rect& viewport_intersection,
631 const gfx::Rect& compositing_rect,
632 blink::FrameOcclusionState occlusion_state) {
Bo Liuf3a5a642018-07-30 17:28:44633 // The message is going to be posted to UI thread after
634 // OnUpdateViewportIntersection returns. This additional post on the IO
635 // thread guarantees that by the time OnUpdateViewportIntersectionOnUI runs,
636 // the message has been handled on the UI thread.
Eric Seckler8652dcd52018-09-20 10:42:28637 base::PostTaskWithTraits(
638 FROM_HERE, {content::BrowserThread::IO},
Bo Liuf3a5a642018-07-30 17:28:44639 base::BindOnce(&UpdateViewportIntersectionMessageFilter::
640 OnUpdateViewportIntersectionPostOnIO,
Stefan Zager54e25832018-08-14 22:15:31641 this, viewport_intersection, compositing_rect,
Daniel Chengaa37a482019-03-21 00:24:20642 occlusion_state));
Bo Liuf3a5a642018-07-30 17:28:44643 }
644 void OnUpdateViewportIntersectionPostOnIO(
645 const gfx::Rect& viewport_intersection,
Stefan Zager54e25832018-08-14 22:15:31646 const gfx::Rect& compositing_rect,
Daniel Chengaa37a482019-03-21 00:24:20647 blink::FrameOcclusionState occlusion_state) {
Eric Seckler8652dcd52018-09-20 10:42:28648 base::PostTaskWithTraits(
649 FROM_HERE, {content::BrowserThread::UI},
Bo Liuf3a5a642018-07-30 17:28:44650 base::BindOnce(&UpdateViewportIntersectionMessageFilter::
651 OnUpdateViewportIntersectionOnUI,
Stefan Zager54e25832018-08-14 22:15:31652 this, viewport_intersection, compositing_rect,
Daniel Chengaa37a482019-03-21 00:24:20653 occlusion_state));
Bo Liuf3a5a642018-07-30 17:28:44654 }
Daniel Chengaa37a482019-03-21 00:24:20655 void OnUpdateViewportIntersectionOnUI(
656 const gfx::Rect& viewport_intersection,
657 const gfx::Rect& compositing_rect,
658 blink::FrameOcclusionState occlusion_state) {
Bo Liuf3a5a642018-07-30 17:28:44659 viewport_intersection_ = viewport_intersection;
660 compositing_rect_ = compositing_rect;
Daniel Chengaa37a482019-03-21 00:24:20661 occlusion_state_ = occlusion_state;
Bo Liuf3a5a642018-07-30 17:28:44662 msg_received_ = true;
663 if (run_loop_)
664 run_loop_->Quit();
665 }
666 base::RunLoop* run_loop_ = nullptr;
667 bool msg_received_;
668 gfx::Rect compositing_rect_;
669 gfx::Rect viewport_intersection_;
Daniel Chengaa37a482019-03-21 00:24:20670 blink::FrameOcclusionState occlusion_state_ =
671 blink::FrameOcclusionState::kUnknown;
Bo Liuf3a5a642018-07-30 17:28:44672 DISALLOW_COPY_AND_ASSIGN(UpdateViewportIntersectionMessageFilter);
673};
674
nick5f3d76072015-06-09 00:24:00675} // namespace
676
dmazzoni0b5d2482014-09-10 19:45:57677//
678// SitePerProcessBrowserTest
679//
[email protected]c96e9702014-02-15 08:29:50680
Lukasz Anforowicz9e0ce4e2017-09-28 19:09:15681SitePerProcessBrowserTest::SitePerProcessBrowserTest() {}
dmazzoni0b5d2482014-09-10 19:45:57682
nick44bacf32015-04-14 02:06:39683std::string SitePerProcessBrowserTest::DepictFrameTree(FrameTreeNode* node) {
684 return visualizer_.DepictFrameTree(node);
nick59dcb162015-04-09 20:29:01685}
686
avi83883c82014-12-23 00:08:49687void SitePerProcessBrowserTest::SetUpCommandLine(
688 base::CommandLine* command_line) {
nickd30fd962015-07-27 21:51:08689 IsolateAllSitesForTesting(command_line);
Kevin McNeee8b9e972019-02-12 20:44:53690
691 command_line->AppendSwitch(switches::kValidateInputEventStream);
692
bokanc3cd4452016-11-09 04:11:15693#if !defined(OS_ANDROID)
694 // TODO(bokan): Needed for scrollability check in
695 // FrameOwnerPropertiesPropagationScrolling. crbug.com/662196.
chaopengd3ca34feb2017-04-20 17:11:22696 feature_list_.InitAndDisableFeature(features::kOverlayScrollbar);
bokanc3cd4452016-11-09 04:11:15697#endif
chaopengd3ca34feb2017-04-20 17:11:22698}
[email protected]bbdd1b20b2012-12-11 21:24:13699
aboxhall5a560fe2017-04-20 03:55:05700void SitePerProcessBrowserTest::SetUpOnMainThread() {
naskocbce0e62014-10-07 14:04:26701 host_resolver()->AddRule("*", "127.0.0.1");
naskocbce0e62014-10-07 14:04:26702 SetupCrossSiteRedirector(embedded_test_server());
martijna4dce162016-11-16 17:25:04703 ASSERT_TRUE(embedded_test_server()->Start());
naskocbce0e62014-10-07 14:04:26704}
705
lfg52bac9072015-11-06 17:27:52706//
707// SitePerProcessHighDPIBrowserTest
708//
709
lfg52bac9072015-11-06 17:27:52710class SitePerProcessHighDPIBrowserTest : public SitePerProcessBrowserTest {
711 public:
wjmaclean8a795f32016-08-11 23:49:58712 const double kDeviceScaleFactor = 2.0;
713
lfg52bac9072015-11-06 17:27:52714 SitePerProcessHighDPIBrowserTest() {}
715
716 protected:
717 void SetUpCommandLine(base::CommandLine* command_line) override {
718 SitePerProcessBrowserTest::SetUpCommandLine(command_line);
wjmaclean8a795f32016-08-11 23:49:58719 command_line->AppendSwitchASCII(
720 switches::kForceDeviceScaleFactor,
721 base::StringPrintf("%f", kDeviceScaleFactor));
lfg52bac9072015-11-06 17:27:52722 }
723};
724
estark56dc8e22016-01-26 17:58:29725// SitePerProcessIgnoreCertErrorsBrowserTest
726
727class SitePerProcessIgnoreCertErrorsBrowserTest
728 : public SitePerProcessBrowserTest {
729 public:
730 SitePerProcessIgnoreCertErrorsBrowserTest() {}
731
732 protected:
733 void SetUpCommandLine(base::CommandLine* command_line) override {
734 SitePerProcessBrowserTest::SetUpCommandLine(command_line);
735 command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
736 }
737};
738
Luna Lu58c77b02019-03-05 20:13:46739// SitePerProcessFeaturePolicyBrowserTest
740
741class SitePerProcessFeaturePolicyBrowserTest
742 : public SitePerProcessBrowserTest {
743 public:
744 SitePerProcessFeaturePolicyBrowserTest() = default;
745
746 // Enable tests for parameterized features.
747 void SetUpCommandLine(base::CommandLine* command_line) override {
748 SitePerProcessBrowserTest::SetUpCommandLine(command_line);
749 command_line->AppendSwitchASCII("enable-blink-features",
750 "ExperimentalProductivityFeatures");
751 }
752};
753
Ian Clellandedb8c5dd2018-03-01 17:01:37754// SitePerProcessFeaturePolicyJavaScriptBrowserTest
755
756class SitePerProcessFeaturePolicyJavaScriptBrowserTest
757 : public SitePerProcessBrowserTest {
758 public:
759 SitePerProcessFeaturePolicyJavaScriptBrowserTest() = default;
760
761 // Enable the feature policy JavaScript interface
762 void SetUpCommandLine(base::CommandLine* command_line) override {
763 SitePerProcessBrowserTest::SetUpCommandLine(command_line);
Luna Lu58c77b02019-03-05 20:13:46764 command_line->AppendSwitchASCII(
765 "enable-blink-features",
766 "FeaturePolicyJavaScriptInterface,ExperimentalProductivityFeatures");
Ian Clellandedb8c5dd2018-03-01 17:01:37767 }
768};
769
Becca Hughes60af7d42017-12-12 10:53:15770// SitePerProcessAutoplayBrowserTest
771
772class SitePerProcessAutoplayBrowserTest : public SitePerProcessBrowserTest {
773 public:
774 SitePerProcessAutoplayBrowserTest() = default;
775
776 void SetUpCommandLine(base::CommandLine* command_line) override {
777 SitePerProcessBrowserTest::SetUpCommandLine(command_line);
778 command_line->AppendSwitchASCII(
779 switches::kAutoplayPolicy,
780 switches::autoplay::kDocumentUserActivationRequiredPolicy);
781 command_line->AppendSwitchASCII("enable-blink-features",
782 "FeaturePolicyAutoplayFeature");
783 }
784
785 bool AutoplayAllowed(const ToRenderFrameHost& adapter,
786 bool with_user_gesture) {
787 RenderFrameHost* rfh = adapter.render_frame_host();
788 const char* test_script = "attemptPlay();";
789 bool worked = false;
790 if (with_user_gesture) {
791 EXPECT_TRUE(ExecuteScriptAndExtractBool(rfh, test_script, &worked));
792 } else {
793 EXPECT_TRUE(ExecuteScriptWithoutUserGestureAndExtractBool(
794 rfh, test_script, &worked));
795 }
796 return worked;
797 }
798
799 void NavigateFrameAndWait(FrameTreeNode* node, const GURL& url) {
800 NavigateFrameToURL(node, url);
801 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
802 EXPECT_EQ(url, node->current_url());
803 }
804};
805
Patrick Noland4d4df3452018-02-26 19:25:56806class SitePerProcesScrollAnchorTest : public SitePerProcessBrowserTest {
807 public:
808 SitePerProcesScrollAnchorTest() = default;
809
810 void SetUpCommandLine(base::CommandLine* command_line) override {
811 SitePerProcessBrowserTest::SetUpCommandLine(command_line);
812 command_line->AppendSwitchASCII("enable-blink-features",
813 "ScrollAnchorSerialization");
814 }
815};
816
amalikae4100ce2016-10-06 07:27:58817// SitePerProcessEmbedderCSPEnforcementBrowserTest
818
819class SitePerProcessEmbedderCSPEnforcementBrowserTest
820 : public SitePerProcessBrowserTest {
821 public:
822 SitePerProcessEmbedderCSPEnforcementBrowserTest() {}
823
824 protected:
825 void SetUpCommandLine(base::CommandLine* command_line) override {
826 SitePerProcessBrowserTest::SetUpCommandLine(command_line);
827 // TODO(amalika): Remove this switch when the EmbedderCSPEnforcement becomes
828 // stable
829 command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
830 "EmbedderCSPEnforcement");
831 }
832};
833
Ehsan Karamadf2b6bb9a12018-04-06 17:13:14834// SitePerProcessProgrammaticScrollTest.
835
836class SitePerProcessProgrammaticScrollTest : public SitePerProcessBrowserTest {
837 public:
838 SitePerProcessProgrammaticScrollTest()
839 : kInfinity(1000000U), kPositiveXYPlane(0, 0, kInfinity, kInfinity) {}
840
841 protected:
842 const size_t kInfinity;
843 const std::string kIframeOutOfViewHTML = "/iframe_out_of_view.html";
David Bokan6f46897c2018-04-09 22:32:47844 const std::string kIframeClippedHTML = "/iframe_clipped.html";
845 const std::string kInputBoxHTML = "/input_box.html";
Ehsan Karamadf2b6bb9a12018-04-06 17:13:14846 const std::string kIframeSelector = "iframe";
847 const std::string kInputSelector = "input";
848 const gfx::Rect kPositiveXYPlane;
849
850 // Waits until the |load| handle is called inside the frame.
851 void WaitForOnLoad(FrameTreeNode* node) {
852 RunCommandAndWaitForResponse(node, "notifyWhenLoaded();", "LOADED");
853 }
854
855 void WaitForElementVisible(FrameTreeNode* node, const std::string& sel) {
856 RunCommandAndWaitForResponse(
857 node,
858 base::StringPrintf("notifyWhenVisible(document.querySelector('%s'));",
859 sel.c_str()),
860 "VISIBLE");
861 }
862
David Bokan6f46897c2018-04-09 22:32:47863 void WaitForViewportToStabilize(FrameTreeNode* node) {
864 RunCommandAndWaitForResponse(node, "notifyWhenViewportStable(0);",
865 "VIEWPORT_STABLE");
866 }
867
Ehsan Karamadf2b6bb9a12018-04-06 17:13:14868 void AddFocusedInputField(FrameTreeNode* node) {
869 ASSERT_TRUE(ExecuteScript(node, "addFocusedInputField();"));
870 }
871
872 void SetWindowScroll(FrameTreeNode* node, int x, int y) {
873 ASSERT_TRUE(ExecuteScript(
874 node, base::StringPrintf("window.scrollTo(%d, %d);", x, y)));
875 }
876
877 // Helper function to retrieve the bounding client rect of the element
878 // identified by |sel| inside |rfh|.
879 gfx::Rect GetBoundingClientRect(FrameTreeNode* node, const std::string& sel) {
880 std::string result;
881 EXPECT_TRUE(ExecuteScriptAndExtractString(
882 node,
883 base::StringPrintf(
884 "window.domAutomationController.send(rectAsString("
885 " document.querySelector('%s').getBoundingClientRect()));",
886 sel.c_str()),
887 &result));
888 return GetRectFromString(result);
889 }
890
891 // Returns a rect representing the current |visualViewport| in the main frame
892 // of |contents|.
893 gfx::Rect GetVisualViewport(FrameTreeNode* node) {
894 std::string result;
895 EXPECT_TRUE(ExecuteScriptAndExtractString(
896 node,
897 "window.domAutomationController.send("
898 " rectAsString(visualViewportAsRect()));",
899 &result));
900 return GetRectFromString(result);
901 }
902
903 float GetVisualViewportScale(FrameTreeNode* node) {
904 double scale;
905 EXPECT_TRUE(ExecuteScriptAndExtractDouble(
906 node, "window.domAutomationController.send(visualViewport.scale);",
907 &scale));
908 return static_cast<float>(scale);
909 }
910
911 private:
912 void RunCommandAndWaitForResponse(FrameTreeNode* node,
913 const std::string& command,
914 const std::string& response) {
915 std::string msg_from_renderer;
916 ASSERT_TRUE(
917 ExecuteScriptAndExtractString(node, command, &msg_from_renderer));
918 ASSERT_EQ(response, msg_from_renderer);
919 }
920
921 gfx::Rect GetRectFromString(const std::string& str) {
922 std::vector<std::string> tokens = base::SplitString(
923 str, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
924 EXPECT_EQ(4U, tokens.size());
925 double x = 0.0, y = 0.0, width = 0.0, height = 0.0;
926 EXPECT_TRUE(base::StringToDouble(tokens[0], &x));
927 EXPECT_TRUE(base::StringToDouble(tokens[1], &y));
928 EXPECT_TRUE(base::StringToDouble(tokens[2], &width));
929 EXPECT_TRUE(base::StringToDouble(tokens[3], &height));
930 return {static_cast<int>(x), static_cast<int>(y), static_cast<int>(width),
931 static_cast<int>(height)};
932 }
933
934 DISALLOW_COPY_AND_ASSIGN(SitePerProcessProgrammaticScrollTest);
935};
936
wjmaclean8a795f32016-08-11 23:49:58937IN_PROC_BROWSER_TEST_F(SitePerProcessHighDPIBrowserTest,
938 SubframeLoadsWithCorrectDeviceScaleFactor) {
939 GURL main_url(embedded_test_server()->GetURL(
940 "a.com", "/cross_site_iframe_factory.html?a(b)"));
davidsac6e6c35e42016-11-21 19:45:57941 EXPECT_TRUE(NavigateToURL(shell(), main_url));
wjmaclean8a795f32016-08-11 23:49:58942
timav9483b392016-11-08 06:16:15943 // On Android forcing device scale factor does not work for tests, therefore
944 // we ensure that make frame and iframe have the same DIP scale there, but
945 // not necessarily kDeviceScaleFactor.
946 const double expected_dip_scale =
947#if defined(OS_ANDROID)
948 GetFrameDeviceScaleFactor(web_contents());
949#else
950 SitePerProcessHighDPIBrowserTest::kDeviceScaleFactor;
951#endif
952
953 EXPECT_EQ(expected_dip_scale, GetFrameDeviceScaleFactor(web_contents()));
wjmaclean8a795f32016-08-11 23:49:58954
955 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
timav9483b392016-11-08 06:16:15956 ASSERT_EQ(1U, root->child_count());
957
wjmaclean8a795f32016-08-11 23:49:58958 FrameTreeNode* child = root->child_at(0);
timav9483b392016-11-08 06:16:15959 EXPECT_EQ(expected_dip_scale, GetFrameDeviceScaleFactor(child));
wjmaclean8a795f32016-08-11 23:49:58960}
961
Mitsuru Oshima3c07b332018-02-06 04:56:50962#if defined(OS_CHROMEOS)
963IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
964 SubframeUpdateToCorrectDeviceScaleFactor) {
Mitsuru Oshima3c07b332018-02-06 04:56:50965 GURL main_url(embedded_test_server()->GetURL(
966 "a.com", "/cross_site_iframe_factory.html?a(b)"));
967 EXPECT_TRUE(NavigateToURL(shell(), main_url));
968
969 EXPECT_EQ(1.0, GetFrameDeviceScaleFactor(web_contents()));
970
971 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
972 ASSERT_EQ(1U, root->child_count());
973
974 FrameTreeNode* child = root->child_at(0);
975 EXPECT_EQ(1.0, GetFrameDeviceScaleFactor(child));
976
977 double expected_dip_scale = 2.0;
978
979 // TODO(oshima): allow DeviceScaleFactor change on other platforms
980 // (win, linux, mac, android and mus).
981 aura::TestScreen* test_screen =
982 static_cast<aura::TestScreen*>(display::Screen::GetScreen());
983 test_screen->CreateHostForPrimaryDisplay();
984 test_screen->SetDeviceScaleFactor(expected_dip_scale);
985
Scott Violet3c03cfef2019-04-03 22:22:06986 // This forces |expected_dip_scale| to be applied to the aura::WindowTreeHost
987 // and aura::Window.
988 aura::WindowTreeHost* window_tree_host = shell()->window()->GetHost();
989 window_tree_host->SetBoundsInPixels(window_tree_host->GetBoundsInPixels());
990
Mitsuru Oshima3c07b332018-02-06 04:56:50991 double device_scale_factor = 0;
992 // Wait until dppx becomes 2 if the frame's dpr hasn't beeen updated
993 // to 2 yet.
994 const char kScript[] =
995 "function sendDpr() "
996 "{window.domAutomationController.send(window.devicePixelRatio);}; "
997 "if (window.devicePixelRatio == 2) sendDpr();"
998 "window.matchMedia('screen and "
999 "(min-resolution: 2dppx)').addListener(function(e) { if (e.matches) { "
1000 "sendDpr();}})";
1001 // Make sure that both main frame and iframe are updated to 2x.
1002 EXPECT_TRUE(
1003 ExecuteScriptAndExtractDouble(child, kScript, &device_scale_factor));
1004 EXPECT_EQ(expected_dip_scale, device_scale_factor);
1005
1006 device_scale_factor = 0;
1007 EXPECT_TRUE(ExecuteScriptAndExtractDouble(web_contents(), kScript,
1008 &device_scale_factor));
1009 EXPECT_EQ(expected_dip_scale, device_scale_factor);
1010}
1011
1012#endif
1013
W. James MacLean62198672019-06-04 16:46:101014class TextAutosizerPageInfoObserver : public WebContentsObserver {
1015 public:
1016 explicit TextAutosizerPageInfoObserver(WebContents* web_contents)
1017 : remote_page_info_seen_(false), remote_page_info_({0, 0, 1.f}) {
1018 Observe(web_contents);
1019 }
1020 ~TextAutosizerPageInfoObserver() override { Observe(nullptr); }
1021
1022 bool OnMessageReceived(const IPC::Message& message) override {
1023 IPC_BEGIN_MESSAGE_MAP(TextAutosizerPageInfoObserver, message)
1024 IPC_MESSAGE_HANDLER(
1025 ViewHostMsg_NotifyTextAutosizerPageInfoChangedInLocalMainFrame,
1026 OnUpdateRemotePageInfo)
1027 IPC_END_MESSAGE_MAP()
1028 return false;
1029 }
1030
1031 void WaitForPageInfo(base::Optional<int> target_main_frame_width,
1032 base::Optional<float> target_device_scale_adjustment) {
1033 target_main_frame_width_ = target_main_frame_width;
1034 target_device_scale_adjustment_ = target_device_scale_adjustment;
1035 remote_page_info_seen_ = false;
1036 run_loop_ = std::make_unique<base::RunLoop>();
1037 run_loop_->Run();
1038 run_loop_.reset();
1039 }
1040
1041 const blink::WebTextAutosizerPageInfo& GetTextAutosizerPageInfo() {
1042 return remote_page_info_;
1043 }
1044
1045 private:
1046 void OnUpdateRemotePageInfo(
1047 const blink::WebTextAutosizerPageInfo& remote_page_info) {
1048 remote_page_info_ = remote_page_info;
1049 if ((!target_main_frame_width_ ||
1050 remote_page_info.main_frame_width != target_main_frame_width_) &&
1051 (!target_device_scale_adjustment_ ||
1052 remote_page_info.device_scale_adjustment !=
1053 target_device_scale_adjustment_)) {
1054 return;
1055 }
1056 remote_page_info_seen_ = true;
1057 if (run_loop_)
1058 run_loop_->Quit();
1059 }
1060
1061 bool remote_page_info_seen_;
1062 blink::WebTextAutosizerPageInfo remote_page_info_;
1063 std::unique_ptr<base::RunLoop> run_loop_;
1064 base::Optional<int> target_main_frame_width_;
1065 base::Optional<float> target_device_scale_adjustment_;
1066};
1067
1068class OutgoingTextAutosizerPageInfoIPCWatcher {
1069 public:
1070 OutgoingTextAutosizerPageInfoIPCWatcher(
1071 RenderProcessHostImpl* rph,
1072 base::Optional<int> target_width,
1073 base::Optional<float> target_device_scale_adjustment)
1074 : rph_(rph),
1075 outgoing_message_seen_(false),
1076 target_width_(target_width),
1077 target_device_scale_adjustment_(target_device_scale_adjustment) {
1078 rph_->SetIpcSendWatcherForTesting(
1079 base::BindRepeating(&OutgoingTextAutosizerPageInfoIPCWatcher::OnMessage,
1080 base::Unretained(this)));
1081 }
1082 ~OutgoingTextAutosizerPageInfoIPCWatcher() {
1083 rph_->SetIpcSendWatcherForTesting(
1084 base::RepeatingCallback<void(const IPC::Message& msg)>());
1085 }
1086
1087 void WaitForIPC() {
1088 if (outgoing_message_seen_)
1089 return;
1090 run_loop_ = std::make_unique<base::RunLoop>();
1091 run_loop_->Run();
1092 run_loop_.reset();
1093 }
1094
1095 const blink::WebTextAutosizerPageInfo& GetTextAutosizerPageInfo() {
1096 return remote_page_info_;
1097 }
1098
1099 private:
1100 void OnMessage(const IPC::Message& message) {
1101 IPC_BEGIN_MESSAGE_MAP(OutgoingTextAutosizerPageInfoIPCWatcher, message)
1102 IPC_MESSAGE_HANDLER(
1103 PageMsg_UpdateTextAutosizerPageInfoForRemoteMainFrames,
1104 ProcessMessage)
1105 IPC_END_MESSAGE_MAP()
1106 }
1107
1108 void ProcessMessage(const blink::WebTextAutosizerPageInfo& remote_page_info) {
1109 if ((target_width_ && remote_page_info.main_frame_width != target_width_) ||
1110 (target_device_scale_adjustment_ &&
1111 remote_page_info.device_scale_adjustment !=
1112 target_device_scale_adjustment_)) {
1113 return;
1114 }
1115 outgoing_message_seen_ = true;
1116 remote_page_info_ = remote_page_info;
1117 if (run_loop_)
1118 run_loop_->Quit();
1119 }
1120
1121 RenderProcessHostImpl* rph_;
1122 bool outgoing_message_seen_;
1123 base::Optional<int> target_width_;
1124 base::Optional<float> target_device_scale_adjustment_;
1125 std::unique_ptr<base::RunLoop> run_loop_;
1126 blink::WebTextAutosizerPageInfo remote_page_info_;
1127};
1128
1129// Make sure that when a relevant feature of the main frame changes, e.g. the
1130// frame width, that the browser is notified.
1131IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, TextAutosizerPageInfo) {
1132 WebPreferences prefs =
1133 web_contents()->GetRenderViewHost()->GetWebkitPreferences();
1134 prefs.text_autosizing_enabled = true;
1135
1136 GURL main_url(embedded_test_server()->GetURL(
1137 "a.com", "/cross_site_iframe_factory.html?a(b)"));
1138 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1139 web_contents()->GetRenderViewHost()->UpdateWebkitPreferences(prefs);
1140
1141 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
1142 ASSERT_EQ(1U, root->child_count());
1143 FrameTreeNode* b_child = root->child_at(0);
1144 auto* child_rph = static_cast<RenderProcessHostImpl*>(
1145 b_child->current_frame_host()->GetProcess());
1146
1147 blink::WebTextAutosizerPageInfo received_page_info;
1148 auto observer =
1149 std::make_unique<TextAutosizerPageInfoObserver>(web_contents());
1150#if defined(OS_ANDROID)
1151 prefs.device_scale_adjustment += 0.05f;
1152 OutgoingTextAutosizerPageInfoIPCWatcher ipc_watcher(
1153 child_rph, base::Optional<int>(), prefs.device_scale_adjustment);
1154 // Change the device scale adjustment to trigger a RemotePageInfo update.
1155 web_contents()->GetRenderViewHost()->UpdateWebkitPreferences(prefs);
1156 // Make sure we receive a ViewHostMsg from the main frame's renderer.
1157 observer->WaitForPageInfo(base::Optional<int>(),
1158 prefs.device_scale_adjustment);
1159 // Make sure the correct page message is sent to the child.
1160 ipc_watcher.WaitForIPC();
1161 received_page_info = observer->GetTextAutosizerPageInfo();
1162 EXPECT_EQ(prefs.device_scale_adjustment,
1163 received_page_info.device_scale_adjustment);
1164#else
1165 // Resize the main frame, then wait to observe that the RemotePageInfo message
1166 // arrives.
1167 auto* view = web_contents()->GetRenderWidgetHostView();
1168 gfx::Rect old_bounds = view->GetViewBounds();
1169 gfx::Rect new_bounds(
1170 old_bounds.origin(),
1171 gfx::Size(old_bounds.width() - 20, old_bounds.height() - 20));
1172 OutgoingTextAutosizerPageInfoIPCWatcher ipc_watcher(
1173 child_rph, new_bounds.width(), base::Optional<float>());
1174 view->SetBounds(new_bounds);
1175 // Make sure we receive a ViewHostMsg from the main frame's renderer.
1176 observer->WaitForPageInfo(new_bounds.width(), base::Optional<float>());
1177 // Make sure the correct page message is sent to the child.
1178 ipc_watcher.WaitForIPC();
1179 received_page_info = observer->GetTextAutosizerPageInfo();
1180 EXPECT_EQ(new_bounds.width(), received_page_info.main_frame_width);
1181#endif // defined(OS_ANDROID)
1182
1183 // Dynamically create a new, cross-process frame to test sending the cached
1184 // TextAutosizerPageInfo.
1185
1186 GURL c_url = embedded_test_server()->GetURL("c.com", "/title1.html");
1187 // The following is a hack so we can get an IPC watcher connected to the
1188 // RenderProcessHost for C before the RenderView is created for it, and the
1189 // TextAutosizerPageInfo IPC is sent to it.
1190 scoped_refptr<SiteInstance> c_site =
1191 web_contents()->GetSiteInstance()->GetRelatedSiteInstance(c_url);
1192 // Force creation of a render process for c's SiteInstance, this will get
1193 // used when we dynamically create the new frame.
1194 auto* c_rph = static_cast<RenderProcessHostImpl*>(c_site->GetProcess());
1195 ASSERT_TRUE(c_rph);
1196 ASSERT_NE(c_rph, root->current_frame_host()->GetProcess());
1197 ASSERT_NE(c_rph, b_child->current_frame_host()->GetProcess());
1198 OutgoingTextAutosizerPageInfoIPCWatcher c_ipc_watcher(
1199 c_rph, base::Optional<int>(), base::Optional<float>());
1200
1201 // Create the subframe now.
1202 std::string create_frame_script = base::StringPrintf(
1203 "var new_iframe = document.createElement('iframe');"
1204 "new_iframe.src = '%s';"
1205 "document.body.appendChild(new_iframe);",
1206 c_url.spec().c_str());
1207 EXPECT_TRUE(ExecuteScript(root, create_frame_script));
1208 ASSERT_EQ(2U, root->child_count());
1209
1210 // Ensure IPC is sent.
1211 c_ipc_watcher.WaitForIPC();
1212 EXPECT_EQ(received_page_info, c_ipc_watcher.GetTextAutosizerPageInfo());
1213}
1214
nasko983ea9c2014-10-25 00:27:531215// Ensure that navigating subframes in --site-per-process mode works and the
1216// correct documents are committed.
Alex Moshchuk777a6372017-07-25 21:59:431217IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CrossSiteIframe) {
nick4e68f5252015-08-28 20:17:051218 GURL main_url(embedded_test_server()->GetURL(
1219 "a.com", "/cross_site_iframe_factory.html?a(a,a(a,a(a)))"));
davidsac6e6c35e42016-11-21 19:45:571220 EXPECT_TRUE(NavigateToURL(shell(), main_url));
[email protected]bbdd1b20b2012-12-11 21:24:131221
[email protected]893558b2014-04-25 23:01:061222 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:001223 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
[email protected]8d613aa2014-02-12 20:37:201224
clamyf1ccb4d2015-01-28 17:40:381225 TestNavigationObserver observer(shell()->web_contents());
[email protected]bbdd1b20b2012-12-11 21:24:131226
[email protected]a1b99262013-12-27 21:56:221227 // Load same-site page into iframe.
[email protected]9a1abe72014-06-19 23:49:021228 FrameTreeNode* child = root->child_at(0);
nick4e68f5252015-08-28 20:17:051229 GURL http_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
[email protected]9a1abe72014-06-19 23:49:021230 NavigateFrameToURL(child, http_url);
clamyf1ccb4d2015-01-28 17:40:381231 EXPECT_EQ(http_url, observer.last_navigation_url());
1232 EXPECT_TRUE(observer.last_navigation_succeeded());
[email protected]de3c5d82014-05-28 22:12:591233 {
1234 // There should be only one RenderWidgetHost when there are no
1235 // cross-process iframes.
[email protected]948481d2014-06-11 18:32:221236 std::set<RenderWidgetHostView*> views_set =
ekaramadfd1b5cfa2016-04-19 00:35:001237 web_contents()->GetRenderWidgetHostViewsInTree();
[email protected]948481d2014-06-11 18:32:221238 EXPECT_EQ(1U, views_set.size());
[email protected]de3c5d82014-05-28 22:12:591239 }
nick44bacf32015-04-14 02:06:391240
1241 EXPECT_EQ(
1242 " Site A\n"
1243 " |--Site A\n"
1244 " +--Site A\n"
1245 " |--Site A\n"
1246 " +--Site A\n"
1247 " +--Site A\n"
nick4e68f5252015-08-28 20:17:051248 "Where A = http://a.com/",
nick44bacf32015-04-14 02:06:391249 DepictFrameTree(root));
[email protected]a1b99262013-12-27 21:56:221250
1251 // Load cross-site page into iframe.
nasko30374c72014-10-30 19:18:371252 GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html");
creis29f85682016-11-08 01:52:421253 {
1254 RenderFrameDeletedObserver deleted_observer(child->current_frame_host());
1255 NavigateFrameToURL(root->child_at(0), url);
1256 deleted_observer.WaitUntilDeleted();
1257 }
naskocbce0e62014-10-07 14:04:261258 // Verify that the navigation succeeded and the expected URL was loaded.
clamyf1ccb4d2015-01-28 17:40:381259 EXPECT_TRUE(observer.last_navigation_succeeded());
1260 EXPECT_EQ(url, observer.last_navigation_url());
[email protected]a1b99262013-12-27 21:56:221261
1262 // Ensure that we have created a new process for the subframe.
naskoe6edde32014-10-17 15:36:481263 ASSERT_EQ(2U, root->child_count());
[email protected]893558b2014-04-25 23:01:061264 SiteInstance* site_instance = child->current_frame_host()->GetSiteInstance();
1265 RenderViewHost* rvh = child->current_frame_host()->render_view_host();
1266 RenderProcessHost* rph = child->current_frame_host()->GetProcess();
1267 EXPECT_NE(shell()->web_contents()->GetRenderViewHost(), rvh);
1268 EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site_instance);
Lukasz Anforowicz9e0ce4e2017-09-28 19:09:151269 EXPECT_NE(shell()->web_contents()->GetMainFrame()->GetProcess(), rph);
[email protected]de3c5d82014-05-28 22:12:591270 {
1271 // There should be now two RenderWidgetHosts, one for each process
1272 // rendering a frame.
[email protected]948481d2014-06-11 18:32:221273 std::set<RenderWidgetHostView*> views_set =
ekaramadfd1b5cfa2016-04-19 00:35:001274 web_contents()->GetRenderWidgetHostViewsInTree();
[email protected]948481d2014-06-11 18:32:221275 EXPECT_EQ(2U, views_set.size());
[email protected]de3c5d82014-05-28 22:12:591276 }
nick44bacf32015-04-14 02:06:391277 RenderFrameProxyHost* proxy_to_parent =
1278 child->render_manager()->GetProxyToParent();
[email protected]9a1abe72014-06-19 23:49:021279 EXPECT_TRUE(proxy_to_parent);
1280 EXPECT_TRUE(proxy_to_parent->cross_process_frame_connector());
kenrba7199832015-01-22 23:44:591281 // The out-of-process iframe should have its own RenderWidgetHost,
1282 // independent of any RenderViewHost.
1283 EXPECT_NE(
avif9ab5d942015-10-15 14:05:441284 rvh->GetWidget()->GetView(),
[email protected]9a1abe72014-06-19 23:49:021285 proxy_to_parent->cross_process_frame_connector()->get_view_for_testing());
kenrba7199832015-01-22 23:44:591286 EXPECT_TRUE(child->current_frame_host()->GetRenderWidgetHost());
[email protected]893558b2014-04-25 23:01:061287
nick44bacf32015-04-14 02:06:391288 EXPECT_EQ(
1289 " Site A ------------ proxies for B\n"
1290 " |--Site B ------- proxies for A\n"
1291 " +--Site A ------- proxies for B\n"
1292 " |--Site A -- proxies for B\n"
1293 " +--Site A -- proxies for B\n"
1294 " +--Site A -- proxies for B\n"
nick4e68f5252015-08-28 20:17:051295 "Where A = http://a.com/\n"
nick44bacf32015-04-14 02:06:391296 " B = http://foo.com/",
1297 DepictFrameTree(root));
1298
[email protected]893558b2014-04-25 23:01:061299 // Load another cross-site page into the same iframe.
nasko30374c72014-10-30 19:18:371300 url = embedded_test_server()->GetURL("bar.com", "/title3.html");
creis29f85682016-11-08 01:52:421301 {
1302 RenderFrameDeletedObserver deleted_observer(child->current_frame_host());
1303 NavigateFrameToURL(root->child_at(0), url);
1304 deleted_observer.WaitUntilDeleted();
1305 }
clamyf1ccb4d2015-01-28 17:40:381306 EXPECT_TRUE(observer.last_navigation_succeeded());
1307 EXPECT_EQ(url, observer.last_navigation_url());
[email protected]893558b2014-04-25 23:01:061308
1309 // Check again that a new process is created and is different from the
1310 // top level one and the previous one.
naskoe6edde32014-10-17 15:36:481311 ASSERT_EQ(2U, root->child_count());
[email protected]893558b2014-04-25 23:01:061312 child = root->child_at(0);
[email protected]a1b99262013-12-27 21:56:221313 EXPECT_NE(shell()->web_contents()->GetRenderViewHost(),
1314 child->current_frame_host()->render_view_host());
[email protected]893558b2014-04-25 23:01:061315 EXPECT_NE(rvh, child->current_frame_host()->render_view_host());
[email protected]a1b99262013-12-27 21:56:221316 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
[email protected]893558b2014-04-25 23:01:061317 child->current_frame_host()->GetSiteInstance());
1318 EXPECT_NE(site_instance,
1319 child->current_frame_host()->GetSiteInstance());
Lukasz Anforowicz9e0ce4e2017-09-28 19:09:151320 EXPECT_NE(shell()->web_contents()->GetMainFrame()->GetProcess(),
[email protected]a1b99262013-12-27 21:56:221321 child->current_frame_host()->GetProcess());
[email protected]893558b2014-04-25 23:01:061322 EXPECT_NE(rph, child->current_frame_host()->GetProcess());
[email protected]de3c5d82014-05-28 22:12:591323 {
[email protected]948481d2014-06-11 18:32:221324 std::set<RenderWidgetHostView*> views_set =
ekaramadfd1b5cfa2016-04-19 00:35:001325 web_contents()->GetRenderWidgetHostViewsInTree();
[email protected]948481d2014-06-11 18:32:221326 EXPECT_EQ(2U, views_set.size());
[email protected]de3c5d82014-05-28 22:12:591327 }
[email protected]9a1abe72014-06-19 23:49:021328 EXPECT_EQ(proxy_to_parent, child->render_manager()->GetProxyToParent());
1329 EXPECT_TRUE(proxy_to_parent->cross_process_frame_connector());
kenrba7199832015-01-22 23:44:591330 EXPECT_NE(
avi3627ecac2015-10-16 17:40:431331 child->current_frame_host()->render_view_host()->GetWidget()->GetView(),
[email protected]9a1abe72014-06-19 23:49:021332 proxy_to_parent->cross_process_frame_connector()->get_view_for_testing());
kenrba7199832015-01-22 23:44:591333 EXPECT_TRUE(child->current_frame_host()->GetRenderWidgetHost());
nick44bacf32015-04-14 02:06:391334
1335 EXPECT_EQ(
1336 " Site A ------------ proxies for C\n"
1337 " |--Site C ------- proxies for A\n"
1338 " +--Site A ------- proxies for C\n"
1339 " |--Site A -- proxies for C\n"
1340 " +--Site A -- proxies for C\n"
1341 " +--Site A -- proxies for C\n"
nick4e68f5252015-08-28 20:17:051342 "Where A = http://a.com/\n"
nick44bacf32015-04-14 02:06:391343 " C = http://bar.com/",
1344 DepictFrameTree(root));
[email protected]bbdd1b20b2012-12-11 21:24:131345}
1346
creis0bfe5282016-06-02 06:46:201347// Ensure that title updates affect the correct NavigationEntry after a new
1348// subframe navigation with an out-of-process iframe. https://crbug.com/616609.
1349IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, TitleAfterCrossSiteIframe) {
1350 // Start at an initial page.
1351 GURL initial_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
1352 EXPECT_TRUE(NavigateToURL(shell(), initial_url));
1353
1354 // Navigate to a same-site page with a same-site iframe.
1355 GURL main_url(embedded_test_server()->GetURL(
1356 "a.com", "/cross_site_iframe_factory.html?a(a)"));
1357 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1358
1359 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
1360
1361 // Make the main frame update its title after the subframe loads.
1362 EXPECT_TRUE(ExecuteScript(shell()->web_contents(),
1363 "document.querySelector('iframe').onload = "
1364 " function() { document.title = 'loaded'; };"));
1365 EXPECT_TRUE(
1366 ExecuteScript(shell()->web_contents(), "document.title = 'not loaded';"));
1367 base::string16 expected_title(base::UTF8ToUTF16("loaded"));
1368 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
1369
1370 // Navigate the iframe cross-site.
1371 TestNavigationObserver load_observer(shell()->web_contents());
1372 GURL frame_url = embedded_test_server()->GetURL("b.com", "/title2.html");
danakj824a7ff2019-02-07 20:34:021373 EXPECT_TRUE(ExecuteScript(root->child_at(0)->current_frame_host(),
1374 JsReplace("window.location.href = $1", frame_url)));
creis0bfe5282016-06-02 06:46:201375 load_observer.Wait();
1376
1377 // Wait for the title to update and ensure it affects the right NavEntry.
1378 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
1379 NavigationEntry* entry =
1380 shell()->web_contents()->GetController().GetLastCommittedEntry();
1381 EXPECT_EQ(expected_title, entry->GetTitle());
1382}
1383
Fady Samuel82fd52a2018-01-31 21:00:061384// Test that the physical backing size and view bounds for a scaled out-of-
1385// process iframe are set and updated correctly.
Christopher Cameronaad15a0d2018-03-09 03:47:331386IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
1387 CompositorViewportPixelSizeTest) {
Fady Samuel82fd52a2018-01-31 21:00:061388 GURL main_url(embedded_test_server()->GetURL(
1389 "a.com", "/frame_tree/page_with_scaled_frame.html"));
1390 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1391
1392 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
1393 ->GetFrameTree()
1394 ->root();
1395
1396 ASSERT_EQ(1U, root->child_count());
1397
1398 FrameTreeNode* parent_iframe_node = root->child_at(0);
1399
1400 EXPECT_EQ(
1401 " Site A ------------ proxies for B\n"
1402 " +--Site A ------- proxies for B\n"
1403 " +--Site B -- proxies for A\n"
1404 "Where A = http://a.com/\n"
1405 " B = http://baz.com/",
1406 DepictFrameTree(root));
1407
1408 FrameTreeNode* nested_iframe_node = parent_iframe_node->child_at(0);
1409 RenderFrameProxyHost* proxy_to_parent =
1410 nested_iframe_node->render_manager()->GetProxyToParent();
1411 CrossProcessFrameConnector* connector =
1412 proxy_to_parent->cross_process_frame_connector();
1413 RenderWidgetHostViewBase* rwhv_nested =
1414 static_cast<RenderWidgetHostViewBase*>(
1415 nested_iframe_node->current_frame_host()
1416 ->GetRenderWidgetHost()
1417 ->GetView());
1418
jonross068adbc2018-05-30 18:04:471419 RenderFrameSubmissionObserver frame_observer(nested_iframe_node);
1420 frame_observer.WaitForMetadataChange();
Fady Samuel82fd52a2018-01-31 21:00:061421
1422 // Verify that applying a CSS scale transform does not impact the size of the
1423 // content of the nested iframe.
Ella Ge5bce2dc2018-10-10 17:49:581424 // The screen_space_rect_in_dip may be off by 1 due to rounding. There is no
1425 // good way to avoid this due to various device-scale-factor. (e.g. when
1426 // dsf=3.375, ceil(round(50 * 3.375) / 3.375) = 51. Thus, we allow the screen
1427 // size in dip to be off by 1 here.
1428 EXPECT_NEAR(50, connector->screen_space_rect_in_dip().size().width(), 1);
1429 EXPECT_NEAR(50, connector->screen_space_rect_in_dip().size().height(), 1);
Fady Samuel82fd52a2018-01-31 21:00:061430 EXPECT_EQ(gfx::Size(100, 100), rwhv_nested->GetViewBounds().size());
1431 EXPECT_EQ(gfx::Size(100, 100), connector->local_frame_size_in_dip());
1432 EXPECT_EQ(connector->local_frame_size_in_pixels(),
Christopher Cameronaad15a0d2018-03-09 03:47:331433 rwhv_nested->GetCompositorViewportPixelSize());
Fady Samuel82fd52a2018-01-31 21:00:061434}
1435
Ken Buchanan3a0ea2d2018-05-18 00:15:221436// Verify an OOPIF resize handler doesn't fire immediately after load without
1437// the frame having been resized. See https://crbug.com/826457.
Ella Ge3d984242019-01-21 15:54:291438IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, NoResizeAfterIframeLoad) {
Ken Buchanan3a0ea2d2018-05-18 00:15:221439 GURL main_url(embedded_test_server()->GetURL(
1440 "a.com", "/cross_site_iframe_factory.html?a(a)"));
1441 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1442 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
1443 ->GetFrameTree()
1444 ->root();
1445
1446 FrameTreeNode* iframe = root->child_at(0);
1447 GURL site_url =
1448 embedded_test_server()->GetURL("b.com", "/page_with_resize_handler.html");
1449 NavigateFrameToURL(iframe, site_url);
Ella Ged0b8bbd22018-07-31 20:06:021450 base::RunLoop().RunUntilIdle();
Ken Buchanan3a0ea2d2018-05-18 00:15:221451
1452 int resizes = -1;
1453 EXPECT_TRUE(ExecuteScriptAndExtractInt(
1454 iframe->current_frame_host(),
1455 "window.domAutomationController.send(resize_count);", &resizes));
1456
1457 // Should be zero because the iframe only has its initial size from parent.
1458 EXPECT_EQ(resizes, 0);
1459}
1460
kenrb1b1ab8e2016-04-13 17:27:441461// Test that the view bounds for an out-of-process iframe are set and updated
1462// correctly, including accounting for local frame offsets in the parent and
1463// scroll positions.
kenrb40be6e42017-05-12 19:19:381464IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, ViewBoundsInNestedFrameTest) {
kenrb1b1ab8e2016-04-13 17:27:441465 GURL main_url(embedded_test_server()->GetURL(
1466 "a.com", "/cross_site_iframe_factory.html?a(a)"));
davidsac6e6c35e42016-11-21 19:45:571467 EXPECT_TRUE(NavigateToURL(shell(), main_url));
kenrb1b1ab8e2016-04-13 17:27:441468
1469 // It is safe to obtain the root frame tree node here, as it doesn't change.
1470 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
1471 ->GetFrameTree()
1472 ->root();
1473 RenderWidgetHostViewBase* rwhv_root = static_cast<RenderWidgetHostViewBase*>(
1474 root->current_frame_host()->GetRenderWidgetHost()->GetView());
1475 ASSERT_EQ(1U, root->child_count());
1476
1477 FrameTreeNode* parent_iframe_node = root->child_at(0);
1478 GURL site_url(embedded_test_server()->GetURL(
1479 "a.com", "/frame_tree/page_with_positioned_frame.html"));
1480 NavigateFrameToURL(parent_iframe_node, site_url);
jonross9e9d6802018-07-19 21:56:191481 RenderFrameSubmissionObserver frame_observer(shell()->web_contents());
kenrb1b1ab8e2016-04-13 17:27:441482
1483 EXPECT_EQ(
1484 " Site A ------------ proxies for B\n"
1485 " +--Site A ------- proxies for B\n"
1486 " +--Site B -- proxies for A\n"
1487 "Where A = http://a.com/\n"
1488 " B = http://baz.com/",
1489 DepictFrameTree(root));
1490
1491 FrameTreeNode* nested_iframe_node = parent_iframe_node->child_at(0);
1492 RenderWidgetHostViewBase* rwhv_nested =
1493 static_cast<RenderWidgetHostViewBase*>(
1494 nested_iframe_node->current_frame_host()
1495 ->GetRenderWidgetHost()
1496 ->GetView());
jonross09d21de2018-06-13 12:31:361497 WaitForHitTestDataOrChildSurfaceReady(
1498 nested_iframe_node->current_frame_host());
kenrb1b1ab8e2016-04-13 17:27:441499
jonross9e9d6802018-07-19 21:56:191500 float scale_factor =
1501 frame_observer.LastRenderFrameMetadata().page_scale_factor;
kenrb2b5d08b2017-03-08 22:31:291502
kenrb40be6e42017-05-12 19:19:381503 // Get the view bounds of the nested iframe, which should account for the
kenrb1b1ab8e2016-04-13 17:27:441504 // relative offset of its direct parent within the root frame.
1505 gfx::Rect bounds = rwhv_nested->GetViewBounds();
kenrb1b1ab8e2016-04-13 17:27:441506
W. James MacLeane2df7ef2019-04-09 11:49:571507 auto filter =
1508 base::MakeRefCounted<SynchronizeVisualPropertiesMessageFilter>();
kenrb1b1ab8e2016-04-13 17:27:441509 root->current_frame_host()->GetProcess()->AddFilter(filter.get());
1510
1511 // Scroll the parent frame downward to verify that the child rect gets updated
1512 // correctly.
dtapuska899ac222017-01-03 18:09:161513 blink::WebMouseWheelEvent scroll_event(
Blink Reformat1c4d759e2017-04-09 16:34:541514 blink::WebInputEvent::kMouseWheel, blink::WebInputEvent::kNoModifiers,
Daniel Cheng93c80a92018-02-14 19:02:431515 blink::WebInputEvent::GetStaticTimeStampForTests());
kenrb2b5d08b2017-03-08 22:31:291516
Blink Reformat1c4d759e2017-04-09 16:34:541517 scroll_event.SetPositionInWidget(
kenrb40be6e42017-05-12 19:19:381518 gfx::ToFlooredInt((bounds.x() - rwhv_root->GetViewBounds().x() - 5) *
mustaqc51f3aab2017-04-05 15:43:111519 scale_factor),
kenrb40be6e42017-05-12 19:19:381520 gfx::ToFlooredInt((bounds.y() - rwhv_root->GetViewBounds().y() - 5) *
mustaqc51f3aab2017-04-05 15:43:111521 scale_factor));
Blink Reformat1c4d759e2017-04-09 16:34:541522 scroll_event.delta_x = 0.0f;
1523 scroll_event.delta_y = -30.0f;
sahel45a2b442017-07-04 15:23:281524 scroll_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
saheld9c26df2016-06-07 15:09:561525 rwhv_root->ProcessMouseWheelEvent(scroll_event, ui::LatencyInfo());
Jonathan9e66f6a2017-11-15 20:08:531526 filter->WaitForRect();
kenrb1b1ab8e2016-04-13 17:27:441527
1528 // The precise amount of scroll for the first view position update is not
1529 // deterministic, so this simply verifies that the OOPIF moved from its
1530 // earlier position.
1531 gfx::Rect update_rect = filter->last_rect();
1532 EXPECT_LT(update_rect.y(), bounds.y() - rwhv_root->GetViewBounds().y());
1533}
1534
wjmacleancff98372017-04-28 18:56:551535// This test verifies that scroll bubbling from an OOPIF properly forwards
1536// GestureFlingStart events from the child frame to the parent frame. This
1537// test times out on failure.
1538IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
1539 GestureFlingStartEventsBubble) {
1540 GURL main_url(embedded_test_server()->GetURL(
1541 "a.com", "/cross_site_iframe_factory.html?a(b)"));
1542 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1543
1544 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
1545 ->GetFrameTree()
1546 ->root();
1547 ASSERT_EQ(1U, root->child_count());
1548
1549 FrameTreeNode* child_iframe_node = root->child_at(0);
1550
sahel45a2b442017-07-04 15:23:281551 RenderWidgetHost* child_rwh =
1552 child_iframe_node->current_frame_host()->GetRenderWidgetHost();
1553
Sahel Sharifye6d81f472018-07-11 20:40:261554 // The fling start won't bubble since its corresponding GSB hasn't bubbled.
Kevin McNeecbb8687a2017-11-14 19:10:591555 InputEventAckWaiter gesture_fling_start_ack_observer(
Sahel Sharifye6d81f472018-07-11 20:40:261556 child_rwh, blink::WebInputEvent::kGestureFlingStart);
wjmacleancff98372017-04-28 18:56:551557
jonross09d21de2018-06-13 12:31:361558 WaitForHitTestDataOrChildSurfaceReady(
1559 child_iframe_node->current_frame_host());
wjmacleancff98372017-04-28 18:56:551560
Kevin McNeecbb8687a2017-11-14 19:10:591561 gesture_fling_start_ack_observer.Reset();
Xida Chen2eec314f2018-06-07 11:42:421562
1563 GenerateTapDownGesture(child_rwh);
1564
wjmacleancff98372017-04-28 18:56:551565 // Send a GSB, GSU, GFS sequence and verify that the GFS bubbles.
1566 blink::WebGestureEvent gesture_scroll_begin(
1567 blink::WebGestureEvent::kGestureScrollBegin,
1568 blink::WebInputEvent::kNoModifiers,
Ella Ge1116059d2018-03-21 02:06:131569 blink::WebInputEvent::GetStaticTimeStampForTests(),
Daniel Cheng7f9ec902019-04-18 05:07:001570 blink::WebGestureDevice::kTouchscreen);
wjmacleancff98372017-04-28 18:56:551571 gesture_scroll_begin.data.scroll_begin.delta_hint_units =
Daniel Libbye15b80b2019-05-24 17:18:241572 ui::input_types::ScrollGranularity::kScrollByPrecisePixel;
wjmacleancff98372017-04-28 18:56:551573 gesture_scroll_begin.data.scroll_begin.delta_x_hint = 0.f;
1574 gesture_scroll_begin.data.scroll_begin.delta_y_hint = 5.f;
1575
1576 child_rwh->ForwardGestureEvent(gesture_scroll_begin);
1577
1578 blink::WebGestureEvent gesture_scroll_update(
1579 blink::WebGestureEvent::kGestureScrollUpdate,
1580 blink::WebInputEvent::kNoModifiers,
Ella Ge1116059d2018-03-21 02:06:131581 blink::WebInputEvent::GetStaticTimeStampForTests(),
Daniel Cheng7f9ec902019-04-18 05:07:001582 blink::WebGestureDevice::kTouchscreen);
wjmacleancff98372017-04-28 18:56:551583 gesture_scroll_update.data.scroll_update.delta_units =
Daniel Libbye15b80b2019-05-24 17:18:241584 ui::input_types::ScrollGranularity::kScrollByPrecisePixel;
wjmacleancff98372017-04-28 18:56:551585 gesture_scroll_update.data.scroll_update.delta_x = 0.f;
1586 gesture_scroll_update.data.scroll_update.delta_y = 5.f;
1587 gesture_scroll_update.data.scroll_update.velocity_y = 5.f;
1588
1589 child_rwh->ForwardGestureEvent(gesture_scroll_update);
1590
1591 blink::WebGestureEvent gesture_fling_start(
1592 blink::WebGestureEvent::kGestureFlingStart,
1593 blink::WebInputEvent::kNoModifiers,
Ella Ge1116059d2018-03-21 02:06:131594 blink::WebInputEvent::GetStaticTimeStampForTests(),
Daniel Cheng7f9ec902019-04-18 05:07:001595 blink::WebGestureDevice::kTouchscreen);
wjmacleancff98372017-04-28 18:56:551596 gesture_fling_start.data.fling_start.velocity_x = 0.f;
1597 gesture_fling_start.data.fling_start.velocity_y = 5.f;
1598
1599 child_rwh->ForwardGestureEvent(gesture_fling_start);
1600
1601 // We now wait for the fling start event to be acked by the parent
1602 // frame. If the test fails, then the test times out.
Kevin McNeecbb8687a2017-11-14 19:10:591603 gesture_fling_start_ack_observer.Wait();
wjmacleancff98372017-04-28 18:56:551604}
1605
kenrb42ba7e82016-05-16 18:54:181606// Test that scrolling a nested out-of-process iframe bubbles unused scroll
1607// delta to a parent frame.
Lukasz Anforowicz79f42962018-09-26 18:31:431608IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, ScrollBubblingFromOOPIFTest) {
sahel8505f882017-07-04 15:46:161609 ui::GestureConfiguration::GetInstance()->set_scroll_debounce_interval_in_ms(
1610 0);
kenrb42ba7e82016-05-16 18:54:181611 GURL main_url(embedded_test_server()->GetURL(
1612 "a.com", "/cross_site_iframe_factory.html?a(b)"));
davidsac6e6c35e42016-11-21 19:45:571613 EXPECT_TRUE(NavigateToURL(shell(), main_url));
kenrb42ba7e82016-05-16 18:54:181614
1615 // It is safe to obtain the root frame tree node here, as it doesn't change.
1616 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
1617 ->GetFrameTree()
1618 ->root();
1619 ASSERT_EQ(1U, root->child_count());
1620
1621 FrameTreeNode* parent_iframe_node = root->child_at(0);
1622
1623 // This test uses the position of the nested iframe within the parent iframe
Fady Samuel799e72192018-04-25 21:16:571624 // to infer the scroll position of the parent.
1625 // SynchronizeVisualPropertiesMessageFilter catches updates to the position in
1626 // order to avoid busy waiting. It gets created early to catch the initial
1627 // rects from the navigation.
W. James MacLeane2df7ef2019-04-09 11:49:571628 auto filter =
1629 base::MakeRefCounted<SynchronizeVisualPropertiesMessageFilter>();
kenrb42ba7e82016-05-16 18:54:181630 parent_iframe_node->current_frame_host()->GetProcess()->AddFilter(
1631 filter.get());
1632
Kevin McNeecbb8687a2017-11-14 19:10:591633 InputEventAckWaiter ack_observer(
1634 parent_iframe_node->current_frame_host()->GetRenderWidgetHost(),
1635 blink::WebInputEvent::kGestureScrollEnd);
wjmaclean6076ea0e2017-04-24 19:42:471636
kenrb42ba7e82016-05-16 18:54:181637 GURL site_url(embedded_test_server()->GetURL(
1638 "b.com", "/frame_tree/page_with_positioned_frame.html"));
1639 NavigateFrameToURL(parent_iframe_node, site_url);
1640
kenrb7b0c6f82016-08-02 20:03:341641 // Navigate the nested frame to a page large enough to have scrollbars.
kenrb3bdfea52016-07-08 21:50:301642 FrameTreeNode* nested_iframe_node = parent_iframe_node->child_at(0);
1643 GURL nested_site_url(embedded_test_server()->GetURL(
1644 "baz.com", "/tall_page.html"));
1645 NavigateFrameToURL(nested_iframe_node, nested_site_url);
1646
kenrb42ba7e82016-05-16 18:54:181647 EXPECT_EQ(
1648 " Site A ------------ proxies for B C\n"
1649 " +--Site B ------- proxies for A C\n"
1650 " +--Site C -- proxies for A B\n"
1651 "Where A = http://a.com/\n"
1652 " B = http://b.com/\n"
1653 " C = http://baz.com/",
1654 DepictFrameTree(root));
1655
1656 RenderWidgetHostViewBase* rwhv_parent =
1657 static_cast<RenderWidgetHostViewBase*>(
1658 parent_iframe_node->current_frame_host()
1659 ->GetRenderWidgetHost()
1660 ->GetView());
1661
kenrb42ba7e82016-05-16 18:54:181662 RenderWidgetHostViewBase* rwhv_nested =
1663 static_cast<RenderWidgetHostViewBase*>(
1664 nested_iframe_node->current_frame_host()
1665 ->GetRenderWidgetHost()
1666 ->GetView());
1667
jonross09d21de2018-06-13 12:31:361668 WaitForHitTestDataOrChildSurfaceReady(
1669 nested_iframe_node->current_frame_host());
kenrb42ba7e82016-05-16 18:54:181670
1671 // Save the original offset as a point of reference.
Jonathan9e66f6a2017-11-15 20:08:531672 filter->WaitForRect();
kenrb42ba7e82016-05-16 18:54:181673 gfx::Rect update_rect = filter->last_rect();
1674 int initial_y = update_rect.y();
Jonathan9e66f6a2017-11-15 20:08:531675 filter->ResetRectRunLoop();
kenrb42ba7e82016-05-16 18:54:181676
1677 // Scroll the parent frame downward.
dtapuska899ac222017-01-03 18:09:161678 blink::WebMouseWheelEvent scroll_event(
Blink Reformat1c4d759e2017-04-09 16:34:541679 blink::WebInputEvent::kMouseWheel, blink::WebInputEvent::kNoModifiers,
Daniel Cheng93c80a92018-02-14 19:02:431680 blink::WebInputEvent::GetStaticTimeStampForTests());
Blink Reformat1c4d759e2017-04-09 16:34:541681 scroll_event.SetPositionInWidget(1, 1);
1682 scroll_event.delta_x = 0.0f;
1683 scroll_event.delta_y = -5.0f;
sahel8505f882017-07-04 15:46:161684 scroll_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
wjmaclean417b02482017-04-20 15:50:031685 // Set has_precise_scroll_deltas to keep these events off the animated scroll
1686 // pathways, which currently break this test.
1687 // https://bugs.chromium.org/p/chromium/issues/detail?id=710513
1688 scroll_event.has_precise_scrolling_deltas = true;
saheld9c26df2016-06-07 15:09:561689 rwhv_parent->ProcessMouseWheelEvent(scroll_event, ui::LatencyInfo());
kenrb42ba7e82016-05-16 18:54:181690
Sahel Sharifye6d81f472018-07-11 20:40:261691 // The event router sends wheel events of a single scroll sequence to the
1692 // target under the first wheel event. Send a wheel end event to the current
1693 // target view before sending a wheel event to a different one.
1694 scroll_event.delta_y = 0.0f;
1695 scroll_event.phase = blink::WebMouseWheelEvent::kPhaseEnded;
1696 scroll_event.dispatch_type =
1697 blink::WebInputEvent::DispatchType::kEventNonBlocking;
1698 rwhv_parent->ProcessMouseWheelEvent(scroll_event, ui::LatencyInfo());
sahel8505f882017-07-04 15:46:161699
kenrb42ba7e82016-05-16 18:54:181700 // Ensure that the view position is propagated to the child properly.
Jonathan9e66f6a2017-11-15 20:08:531701 filter->WaitForRect();
kenrb42ba7e82016-05-16 18:54:181702 update_rect = filter->last_rect();
1703 EXPECT_LT(update_rect.y(), initial_y);
Jonathan9e66f6a2017-11-15 20:08:531704 filter->ResetRectRunLoop();
Kevin McNeecbb8687a2017-11-14 19:10:591705 ack_observer.Reset();
kenrb42ba7e82016-05-16 18:54:181706
1707 // Now scroll the nested frame upward, which should bubble to the parent.
1708 // The upscroll exceeds the amount that the frame was initially scrolled
1709 // down to account for rounding.
Blink Reformat1c4d759e2017-04-09 16:34:541710 scroll_event.delta_y = 6.0f;
sahel8505f882017-07-04 15:46:161711 scroll_event.dispatch_type = blink::WebInputEvent::DispatchType::kBlocking;
1712 scroll_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
saheld9c26df2016-06-07 15:09:561713 rwhv_nested->ProcessMouseWheelEvent(scroll_event, ui::LatencyInfo());
kenrb42ba7e82016-05-16 18:54:181714
Jonathan9e66f6a2017-11-15 20:08:531715 filter->WaitForRect();
kenrb42ba7e82016-05-16 18:54:181716 // This loop isn't great, but it accounts for the possibility of multiple
1717 // incremental updates happening as a result of the scroll animation.
1718 // A failure condition of this test is that the loop might not terminate
1719 // due to bubbling not working properly. If the overscroll bubbles to the
1720 // parent iframe then the nested frame's y coord will return to its
1721 // initial position.
1722 update_rect = filter->last_rect();
1723 while (update_rect.y() > initial_y) {
1724 base::RunLoop run_loop;
1725 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
1726 FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
1727 run_loop.Run();
1728 update_rect = filter->last_rect();
1729 }
1730
Sahel Sharifye6d81f472018-07-11 20:40:261731 // The event router sends wheel events of a single scroll sequence to the
1732 // target under the first wheel event. Send a wheel end event to the current
1733 // target view before sending a wheel event to a different one.
1734 scroll_event.delta_y = 0.0f;
1735 scroll_event.phase = blink::WebMouseWheelEvent::kPhaseEnded;
1736 scroll_event.dispatch_type =
1737 blink::WebInputEvent::DispatchType::kEventNonBlocking;
1738 rwhv_nested->ProcessMouseWheelEvent(scroll_event, ui::LatencyInfo());
sahel8505f882017-07-04 15:46:161739
Jonathan9e66f6a2017-11-15 20:08:531740 filter->ResetRectRunLoop();
wjmaclean6076ea0e2017-04-24 19:42:471741 // Once we've sent a wheel to the nested iframe that we expect to turn into
1742 // a bubbling scroll, we need to delay to make sure the GestureScrollBegin
1743 // from this new scroll doesn't hit the RenderWidgetHostImpl before the
1744 // GestureScrollEnd bubbled from the child.
1745 // This timing only seems to be needed for CrOS, but we'll enable it on
1746 // all platforms just to lessen the possibility of tests being flakey
1747 // on non-CrOS platforms.
Kevin McNeecbb8687a2017-11-14 19:10:591748 ack_observer.Wait();
kenrb42ba7e82016-05-16 18:54:181749
1750 // Scroll the parent down again in order to test scroll bubbling from
1751 // gestures.
Blink Reformat1c4d759e2017-04-09 16:34:541752 scroll_event.delta_y = -5.0f;
sahel8505f882017-07-04 15:46:161753 scroll_event.dispatch_type = blink::WebInputEvent::DispatchType::kBlocking;
1754 scroll_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
saheld9c26df2016-06-07 15:09:561755 rwhv_parent->ProcessMouseWheelEvent(scroll_event, ui::LatencyInfo());
kenrb42ba7e82016-05-16 18:54:181756
Sahel Sharifye6d81f472018-07-11 20:40:261757 // The event router sends wheel events of a single scroll sequence to the
1758 // target under the first wheel event. Send a wheel end event to the current
1759 // target view before sending a wheel event to a different one.
1760 scroll_event.delta_y = 0.0f;
1761 scroll_event.phase = blink::WebMouseWheelEvent::kPhaseEnded;
1762 scroll_event.dispatch_type =
1763 blink::WebInputEvent::DispatchType::kEventNonBlocking;
1764 rwhv_parent->ProcessMouseWheelEvent(scroll_event, ui::LatencyInfo());
sahel8505f882017-07-04 15:46:161765
kenrb42ba7e82016-05-16 18:54:181766 // Ensure ensuing offset change is received, and then reset the filter.
Jonathan9e66f6a2017-11-15 20:08:531767 filter->WaitForRect();
1768 filter->ResetRectRunLoop();
kenrb42ba7e82016-05-16 18:54:181769
1770 // Scroll down the nested iframe via gesture. This requires 3 separate input
1771 // events.
dtapuska899ac222017-01-03 18:09:161772 blink::WebGestureEvent gesture_event(
Blink Reformat1c4d759e2017-04-09 16:34:541773 blink::WebGestureEvent::kGestureScrollBegin,
1774 blink::WebInputEvent::kNoModifiers,
Ella Ge1116059d2018-03-21 02:06:131775 blink::WebInputEvent::GetStaticTimeStampForTests(),
Daniel Cheng7f9ec902019-04-18 05:07:001776 blink::WebGestureDevice::kTouchpad);
Ella Ge1116059d2018-03-21 02:06:131777 gesture_event.SetPositionInWidget(gfx::PointF(1, 1));
sahel8505f882017-07-04 15:46:161778 gesture_event.data.scroll_begin.delta_x_hint = 0.0f;
1779 gesture_event.data.scroll_begin.delta_y_hint = 6.0f;
kenrb42ba7e82016-05-16 18:54:181780 rwhv_nested->GetRenderWidgetHost()->ForwardGestureEvent(gesture_event);
1781
Ella Ge1116059d2018-03-21 02:06:131782 gesture_event =
1783 blink::WebGestureEvent(blink::WebGestureEvent::kGestureScrollUpdate,
1784 blink::WebInputEvent::kNoModifiers,
1785 blink::WebInputEvent::GetStaticTimeStampForTests(),
Daniel Cheng7f9ec902019-04-18 05:07:001786 blink::WebGestureDevice::kTouchpad);
Ella Ge1116059d2018-03-21 02:06:131787 gesture_event.SetPositionInWidget(gfx::PointF(1, 1));
Blink Reformat1c4d759e2017-04-09 16:34:541788 gesture_event.data.scroll_update.delta_x = 0.0f;
1789 gesture_event.data.scroll_update.delta_y = 6.0f;
1790 gesture_event.data.scroll_update.velocity_x = 0;
1791 gesture_event.data.scroll_update.velocity_y = 0;
kenrb42ba7e82016-05-16 18:54:181792 rwhv_nested->GetRenderWidgetHost()->ForwardGestureEvent(gesture_event);
1793
Ella Ge1116059d2018-03-21 02:06:131794 gesture_event =
1795 blink::WebGestureEvent(blink::WebGestureEvent::kGestureScrollEnd,
1796 blink::WebInputEvent::kNoModifiers,
1797 blink::WebInputEvent::GetStaticTimeStampForTests(),
Daniel Cheng7f9ec902019-04-18 05:07:001798 blink::WebGestureDevice::kTouchpad);
Ella Ge1116059d2018-03-21 02:06:131799 gesture_event.SetPositionInWidget(gfx::PointF(1, 1));
kenrb42ba7e82016-05-16 18:54:181800 rwhv_nested->GetRenderWidgetHost()->ForwardGestureEvent(gesture_event);
1801
Jonathan9e66f6a2017-11-15 20:08:531802 filter->WaitForRect();
kenrb42ba7e82016-05-16 18:54:181803 update_rect = filter->last_rect();
1804 // As above, if this loop does not terminate then it indicates an issue
1805 // with scroll bubbling.
1806 while (update_rect.y() > initial_y) {
1807 base::RunLoop run_loop;
1808 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
1809 FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
1810 run_loop.Run();
1811 update_rect = filter->last_rect();
1812 }
kenrb3bdfea52016-07-08 21:50:301813
1814 // Test that when the child frame absorbs all of the scroll delta, it does
1815 // not propagate to the parent (see https://crbug.com/621624).
Jonathan9e66f6a2017-11-15 20:08:531816 filter->ResetRectRunLoop();
Blink Reformat1c4d759e2017-04-09 16:34:541817 scroll_event.delta_y = -5.0f;
sahel8505f882017-07-04 15:46:161818 scroll_event.dispatch_type = blink::WebInputEvent::DispatchType::kBlocking;
1819 scroll_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
kenrb3bdfea52016-07-08 21:50:301820 rwhv_nested->ProcessMouseWheelEvent(scroll_event, ui::LatencyInfo());
1821 // It isn't possible to busy loop waiting on the renderer here because we
1822 // are explicitly testing that something does *not* happen. This creates a
1823 // small chance of false positives but shouldn't result in false negatives,
1824 // so flakiness implies this test is failing.
1825 {
1826 base::RunLoop run_loop;
1827 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
1828 FROM_HERE, run_loop.QuitClosure(), TestTimeouts::action_timeout());
1829 run_loop.Run();
1830 }
1831 DCHECK_EQ(filter->last_rect().x(), 0);
1832 DCHECK_EQ(filter->last_rect().y(), 0);
kenrb42ba7e82016-05-16 18:54:181833}
1834
Kevin McNeee21d23b2018-06-29 15:25:041835// Tests that scrolling with the keyboard will bubble unused scroll to the
1836// OOPIF's parent.
1837IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
1838 KeyboardScrollBubblingFromOOPIF) {
1839 GURL main_url(embedded_test_server()->GetURL(
1840 "a.com", "/frame_tree/page_with_iframe_in_scrollable_div.html"));
1841 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1842
1843 // It is safe to obtain the root frame tree node here, as it doesn't change.
1844 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
1845 ->GetFrameTree()
1846 ->root();
1847 ASSERT_EQ(1U, root->child_count());
1848
1849 FrameTreeNode* iframe_node = root->child_at(0);
1850
1851 EXPECT_EQ(
1852 " Site A ------------ proxies for B\n"
1853 " +--Site B ------- proxies for A\n"
1854 "Where A = http://a.com/\n"
1855 " B = http://b.com/",
1856 DepictFrameTree(root));
1857
1858 RenderWidgetHostViewBase* rwhv_child = static_cast<RenderWidgetHostViewBase*>(
1859 iframe_node->current_frame_host()->GetRenderWidgetHost()->GetView());
1860
1861 double initial_y = 0.0;
1862 ASSERT_TRUE(content::ExecuteScriptAndExtractDouble(
1863 root,
1864 "var wrapperDiv = document.getElementById('wrapper-div');"
1865 "var initial_y = wrapperDiv.scrollTop;"
1866 "var waitForScrollDownPromise = new Promise(function(resolve) {"
1867 " wrapperDiv.addEventListener('scroll', () => {"
1868 " if (wrapperDiv.scrollTop > initial_y)"
1869 " resolve(wrapperDiv.scrollTop);"
1870 " });"
1871 "});"
1872 "window.domAutomationController.send(initial_y);",
1873 &initial_y));
1874 EXPECT_DOUBLE_EQ(0.0, initial_y);
1875
1876 NativeWebKeyboardEvent key_event(
1877 blink::WebKeyboardEvent::kRawKeyDown, blink::WebInputEvent::kNoModifiers,
1878 blink::WebInputEvent::GetStaticTimeStampForTests());
1879 key_event.windows_key_code = ui::VKEY_DOWN;
1880 key_event.native_key_code =
1881 ui::KeycodeConverter::DomCodeToNativeKeycode(ui::DomCode::ARROW_DOWN);
1882 key_event.dom_code = static_cast<int>(ui::DomCode::ARROW_DOWN);
1883 key_event.dom_key = ui::DomKey::ARROW_DOWN;
1884
1885 rwhv_child->GetRenderWidgetHost()->ForwardKeyboardEvent(key_event);
1886
1887 key_event.SetType(blink::WebKeyboardEvent::kKeyUp);
1888 rwhv_child->GetRenderWidgetHost()->ForwardKeyboardEvent(key_event);
1889
1890 double scrolled_y = 0.0;
1891 ASSERT_TRUE(content::ExecuteScriptAndExtractDouble(
1892 root,
1893 "waitForScrollDownPromise.then((scrolled_y) => {"
1894 " window.domAutomationController.send(scrolled_y);"
1895 "});",
1896 &scrolled_y));
1897 EXPECT_GT(scrolled_y, 0.0);
1898}
1899
Sahel Sharify70c3bf52018-03-21 18:39:061900// Test that fling on an out-of-process iframe progresses properly.
Sahel Sharify7c8dfc22018-05-25 00:11:581901IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
1902 TouchscreenGestureFlingStart) {
Sahel Sharify70c3bf52018-03-21 18:39:061903 GURL main_url(embedded_test_server()->GetURL(
1904 "a.com", "/cross_site_iframe_factory.html?a(b)"));
1905 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1906
1907 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
1908 ->GetFrameTree()
1909 ->root();
1910 ASSERT_EQ(1U, root->child_count());
1911
1912 FrameTreeNode* child_iframe_node = root->child_at(0);
1913
1914 RenderWidgetHost* child_rwh =
1915 child_iframe_node->current_frame_host()->GetRenderWidgetHost();
jonross09d21de2018-06-13 12:31:361916 WaitForHitTestDataOrChildSurfaceReady(
1917 child_iframe_node->current_frame_host());
Sahel Sharify70c3bf52018-03-21 18:39:061918
Xida Chen2eec314f2018-06-07 11:42:421919 GenerateTapDownGesture(child_rwh);
Sahel Sharify70c3bf52018-03-21 18:39:061920 // Send a GSB to start scrolling sequence.
1921 blink::WebGestureEvent gesture_scroll_begin(
1922 blink::WebGestureEvent::kGestureScrollBegin,
1923 blink::WebInputEvent::kNoModifiers,
1924 blink::WebInputEvent::GetStaticTimeStampForTests());
Daniel Cheng7f9ec902019-04-18 05:07:001925 gesture_scroll_begin.SetSourceDevice(blink::WebGestureDevice::kTouchscreen);
Sahel Sharify70c3bf52018-03-21 18:39:061926 gesture_scroll_begin.data.scroll_begin.delta_hint_units =
Daniel Libbye15b80b2019-05-24 17:18:241927 ui::input_types::ScrollGranularity::kScrollByPrecisePixel;
Sahel Sharify70c3bf52018-03-21 18:39:061928 gesture_scroll_begin.data.scroll_begin.delta_x_hint = 0.f;
1929 gesture_scroll_begin.data.scroll_begin.delta_y_hint = 5.f;
1930 child_rwh->ForwardGestureEvent(gesture_scroll_begin);
1931
1932 // Send a GFS and wait for the ack of the first GSU generated from progressing
1933 // the fling on the browser.
1934 InputEventAckWaiter gesture_scroll_update_ack_observer(
1935 child_rwh, blink::WebInputEvent::kGestureScrollUpdate);
1936 gesture_scroll_update_ack_observer.Reset();
1937 blink::WebGestureEvent gesture_fling_start(
1938 blink::WebGestureEvent::kGestureFlingStart,
1939 blink::WebInputEvent::kNoModifiers,
1940 blink::WebInputEvent::GetStaticTimeStampForTests());
Daniel Cheng7f9ec902019-04-18 05:07:001941 gesture_fling_start.SetSourceDevice(blink::WebGestureDevice::kTouchscreen);
Sahel Sharify70c3bf52018-03-21 18:39:061942 gesture_fling_start.data.fling_start.velocity_x = 0.f;
1943 gesture_fling_start.data.fling_start.velocity_y = 50.f;
1944 child_rwh->ForwardGestureEvent(gesture_fling_start);
1945 gesture_scroll_update_ack_observer.Wait();
1946}
1947
Sahel Sharify7c8dfc22018-05-25 00:11:581948// Test that fling on an out-of-process iframe progresses properly.
1949IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, TouchpadGestureFlingStart) {
1950 GURL main_url(embedded_test_server()->GetURL(
1951 "a.com", "/cross_site_iframe_factory.html?a(b)"));
1952 EXPECT_TRUE(NavigateToURL(shell(), main_url));
1953
1954 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
1955 ->GetFrameTree()
1956 ->root();
1957 ASSERT_EQ(1U, root->child_count());
1958
1959 FrameTreeNode* child_iframe_node = root->child_at(0);
1960
1961 RenderWidgetHost* child_rwh =
1962 child_iframe_node->current_frame_host()->GetRenderWidgetHost();
1963
1964 // Send a wheel event with phaseBegan to start scrolling sequence.
1965 InputEventAckWaiter gesture_scroll_begin_ack_observer(
1966 child_rwh, blink::WebInputEvent::kGestureScrollBegin);
1967 blink::WebMouseWheelEvent scroll_event(
1968 blink::WebInputEvent::kMouseWheel, blink::WebInputEvent::kNoModifiers,
1969 blink::WebInputEvent::GetStaticTimeStampForTests());
1970 scroll_event.delta_x = 0.0f;
1971 scroll_event.delta_y = 5.0f;
1972 scroll_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
1973 scroll_event.has_precise_scrolling_deltas = true;
1974 child_rwh->ForwardWheelEvent(scroll_event);
1975 gesture_scroll_begin_ack_observer.Wait();
1976
1977 // Send a GFS and wait for the ack of the first GSU generated from progressing
1978 // the fling on the browser.
1979 InputEventAckWaiter gesture_scroll_update_ack_observer(
1980 child_rwh, blink::WebInputEvent::kGestureScrollUpdate);
1981 gesture_scroll_update_ack_observer.Reset();
1982 blink::WebGestureEvent gesture_fling_start(
1983 blink::WebGestureEvent::kGestureFlingStart,
1984 blink::WebInputEvent::kNoModifiers,
1985 blink::WebInputEvent::GetStaticTimeStampForTests());
Daniel Cheng7f9ec902019-04-18 05:07:001986 gesture_fling_start.SetSourceDevice(blink::WebGestureDevice::kTouchpad);
Sahel Sharify7c8dfc22018-05-25 00:11:581987 gesture_fling_start.data.fling_start.velocity_x = 0.f;
1988 gesture_fling_start.data.fling_start.velocity_y = 50.f;
1989 child_rwh->ForwardGestureEvent(gesture_fling_start);
1990 // The test will pass when the GSU ack arrives, since it shows that the fling
1991 // controller has properly generated a GSU event from progressing the fling.
1992 gesture_scroll_update_ack_observer.Wait();
1993}
1994
Sahel Sharifyc7a86bf62017-09-27 19:24:151995class ScrollObserver : public RenderWidgetHost::InputEventObserver {
1996 public:
1997 ScrollObserver(double delta_x, double delta_y) { Reset(delta_x, delta_y); }
1998 ~ScrollObserver() override {}
1999
2000 void OnInputEvent(const blink::WebInputEvent& event) override {
2001 if (event.GetType() == blink::WebInputEvent::kGestureScrollUpdate) {
2002 blink::WebGestureEvent received_update =
2003 *static_cast<const blink::WebGestureEvent*>(&event);
2004 remaining_delta_x_ -= received_update.data.scroll_update.delta_x;
2005 remaining_delta_y_ -= received_update.data.scroll_update.delta_y;
2006 } else if (event.GetType() == blink::WebInputEvent::kGestureScrollEnd) {
2007 if (message_loop_runner_->loop_running())
2008 message_loop_runner_->Quit();
2009 DCHECK_EQ(0, remaining_delta_x_);
2010 DCHECK_EQ(0, remaining_delta_y_);
2011 scroll_end_received_ = true;
2012 }
2013 }
2014
2015 void Wait() {
2016 if (!scroll_end_received_) {
2017 message_loop_runner_->Run();
2018 }
2019 }
2020
2021 void Reset(double delta_x, double delta_y) {
2022 message_loop_runner_ = new content::MessageLoopRunner;
2023 remaining_delta_x_ = delta_x;
2024 remaining_delta_y_ = delta_y;
2025 scroll_end_received_ = false;
2026 }
2027
2028 private:
2029 scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
2030 double remaining_delta_x_;
2031 double remaining_delta_y_;
2032 bool scroll_end_received_;
2033
2034 DISALLOW_COPY_AND_ASSIGN(ScrollObserver);
2035};
2036
Sahel Sharify40324fd2017-09-19 06:57:082037IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
2038 ScrollBubblingFromNestedOOPIFTest) {
2039 ui::GestureConfiguration::GetInstance()->set_scroll_debounce_interval_in_ms(
2040 0);
2041 GURL main_url(embedded_test_server()->GetURL(
2042 "/frame_tree/page_with_positioned_nested_frames.html"));
2043 EXPECT_TRUE(NavigateToURL(shell(), main_url));
jonross9e9d6802018-07-19 21:56:192044 RenderFrameSubmissionObserver frame_observer(shell()->web_contents());
Sahel Sharify40324fd2017-09-19 06:57:082045
2046 // It is safe to obtain the root frame tree node here, as it doesn't change.
2047 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
2048 ASSERT_EQ(1U, root->child_count());
2049
2050 FrameTreeNode* parent_iframe_node = root->child_at(0);
2051 GURL site_url(embedded_test_server()->GetURL(
2052 "a.com", "/frame_tree/page_with_positioned_frame.html"));
2053 EXPECT_EQ(site_url, parent_iframe_node->current_url());
2054
2055 FrameTreeNode* nested_iframe_node = parent_iframe_node->child_at(0);
2056 GURL nested_site_url(
2057 embedded_test_server()->GetURL("baz.com", "/title1.html"));
2058 EXPECT_EQ(nested_site_url, nested_iframe_node->current_url());
2059
2060 RenderWidgetHostViewBase* root_view = static_cast<RenderWidgetHostViewBase*>(
2061 root->current_frame_host()->GetRenderWidgetHost()->GetView());
2062
2063 RenderWidgetHostViewBase* rwhv_nested =
2064 static_cast<RenderWidgetHostViewBase*>(
2065 nested_iframe_node->current_frame_host()
2066 ->GetRenderWidgetHost()
2067 ->GetView());
2068
jonross09d21de2018-06-13 12:31:362069 WaitForHitTestDataOrChildSurfaceReady(
2070 nested_iframe_node->current_frame_host());
Sahel Sharify40324fd2017-09-19 06:57:082071
Kevin McNeecbb8687a2017-11-14 19:10:592072 InputEventAckWaiter ack_observer(
2073 root->current_frame_host()->GetRenderWidgetHost(),
2074 blink::WebInputEvent::kGestureScrollBegin);
Sahel Sharify40324fd2017-09-19 06:57:082075
Sahel Sharifyc7a86bf62017-09-27 19:24:152076 std::unique_ptr<ScrollObserver> scroll_observer;
Sahel Sharifye6d81f472018-07-11 20:40:262077
2078 // All GSU events will be wrapped between a single GSB-GSE pair. The expected
2079 // delta value is equal to summation of all scroll update deltas.
2080 scroll_observer = std::make_unique<ScrollObserver>(0, 15);
2081
Sahel Sharifyc7a86bf62017-09-27 19:24:152082 root->current_frame_host()->GetRenderWidgetHost()->AddInputEventObserver(
2083 scroll_observer.get());
2084
Sahel Sharify40324fd2017-09-19 06:57:082085 // Now scroll the nested frame upward, this must bubble all the way up to the
2086 // root.
2087 blink::WebMouseWheelEvent scroll_event(
2088 blink::WebInputEvent::kMouseWheel, blink::WebInputEvent::kNoModifiers,
Daniel Cheng93c80a92018-02-14 19:02:432089 blink::WebInputEvent::GetStaticTimeStampForTests());
Sahel Sharify40324fd2017-09-19 06:57:082090 gfx::Rect bounds = rwhv_nested->GetViewBounds();
jonross9e9d6802018-07-19 21:56:192091 float scale_factor =
2092 frame_observer.LastRenderFrameMetadata().page_scale_factor;
Sahel Sharify40324fd2017-09-19 06:57:082093 scroll_event.SetPositionInWidget(
2094 gfx::ToCeiledInt((bounds.x() - root_view->GetViewBounds().x() + 10) *
2095 scale_factor),
2096 gfx::ToCeiledInt((bounds.y() - root_view->GetViewBounds().y() + 10) *
2097 scale_factor));
2098 scroll_event.delta_x = 0.0f;
2099 scroll_event.delta_y = 5.0f;
2100 scroll_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
2101 scroll_event.has_precise_scrolling_deltas = true;
2102 rwhv_nested->ProcessMouseWheelEvent(scroll_event, ui::LatencyInfo());
Kevin McNeecbb8687a2017-11-14 19:10:592103 ack_observer.Wait();
Sahel Sharifyc7a86bf62017-09-27 19:24:152104
Sahel Sharifye6d81f472018-07-11 20:40:262105 // Send 10 wheel events with delta_y = 1 to the nested oopif.
Sahel Sharifyc7a86bf62017-09-27 19:24:152106 scroll_event.delta_y = 1.0f;
2107 scroll_event.phase = blink::WebMouseWheelEvent::kPhaseChanged;
Sahel Sharifye6d81f472018-07-11 20:40:262108 for (int i = 0; i < 10; i++)
Sahel Sharifyc7a86bf62017-09-27 19:24:152109 rwhv_nested->ProcessMouseWheelEvent(scroll_event, ui::LatencyInfo());
Sahel Sharifyc7a86bf62017-09-27 19:24:152110
Sahel Sharifye6d81f472018-07-11 20:40:262111 // Send a wheel end event to complete the scrolling sequence.
2112 scroll_event.delta_y = 0.0f;
2113 scroll_event.phase = blink::WebMouseWheelEvent::kPhaseEnded;
2114 rwhv_nested->ProcessMouseWheelEvent(scroll_event, ui::LatencyInfo());
2115 scroll_observer->Wait();
Sahel Sharify40324fd2017-09-19 06:57:082116}
2117
Sahel Sharify53a35452018-02-27 18:02:142118// Tests that scrolling bubbles from an oopif if its source body has
2119// "overflow:hidden" style.
2120IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
W. James MacLean5080de32018-09-06 16:01:462121 ScrollBubblingFromOOPIFWithBodyOverflowHidden) {
Sahel Sharify53a35452018-02-27 18:02:142122 GURL url_domain_a(embedded_test_server()->GetURL(
2123 "a.com", "/scrollable_page_with_iframe.html"));
2124 EXPECT_TRUE(NavigateToURL(shell(), url_domain_a));
jonross9e9d6802018-07-19 21:56:192125 RenderFrameSubmissionObserver frame_observer(shell()->web_contents());
Sahel Sharify53a35452018-02-27 18:02:142126 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
2127
2128 FrameTreeNode* iframe_node = root->child_at(0);
2129 GURL url_domain_b(
2130 embedded_test_server()->GetURL("b.com", "/body_overflow_hidden.html"));
2131 NavigateFrameToURL(iframe_node, url_domain_b);
jonross09d21de2018-06-13 12:31:362132 WaitForHitTestDataOrChildSurfaceReady(iframe_node->current_frame_host());
Sahel Sharify53a35452018-02-27 18:02:142133
2134 RenderWidgetHostViewBase* root_view = static_cast<RenderWidgetHostViewBase*>(
2135 root->current_frame_host()->GetRenderWidgetHost()->GetView());
2136
2137 RenderWidgetHostViewBase* child_view = static_cast<RenderWidgetHostViewBase*>(
2138 iframe_node->current_frame_host()->GetRenderWidgetHost()->GetView());
2139
2140 ScrollObserver scroll_observer(0, -5);
2141 root->current_frame_host()->GetRenderWidgetHost()->AddInputEventObserver(
2142 &scroll_observer);
2143
2144 // Now scroll the nested frame downward, this must bubble to the root since
2145 // the iframe source body is not scrollable.
2146 blink::WebMouseWheelEvent scroll_event(
2147 blink::WebInputEvent::kMouseWheel, blink::WebInputEvent::kNoModifiers,
2148 blink::WebInputEvent::GetStaticTimeStampForTests());
2149 gfx::Rect bounds = child_view->GetViewBounds();
jonross9e9d6802018-07-19 21:56:192150 float scale_factor =
2151 frame_observer.LastRenderFrameMetadata().page_scale_factor;
Sahel Sharify53a35452018-02-27 18:02:142152 scroll_event.SetPositionInWidget(
2153 gfx::ToCeiledInt((bounds.x() - root_view->GetViewBounds().x() + 10) *
2154 scale_factor),
2155 gfx::ToCeiledInt((bounds.y() - root_view->GetViewBounds().y() + 10) *
2156 scale_factor));
2157 scroll_event.delta_x = 0.0f;
2158 scroll_event.delta_y = -5.0f;
2159 scroll_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
2160 scroll_event.has_precise_scrolling_deltas = true;
2161 child_view->ProcessMouseWheelEvent(scroll_event, ui::LatencyInfo());
2162
Sahel Sharifye6d81f472018-07-11 20:40:262163 // Send a wheel end event to complete the scrolling sequence.
2164 scroll_event.delta_y = 0.0f;
2165 scroll_event.phase = blink::WebMouseWheelEvent::kPhaseEnded;
2166 child_view->ProcessMouseWheelEvent(scroll_event, ui::LatencyInfo());
Sahel Sharify53a35452018-02-27 18:02:142167
2168 scroll_observer.Wait();
2169}
2170
Kevin McNee612feb72018-03-01 00:24:222171// Ensure that the scrollability of a local subframe in an OOPIF is considered
2172// when acknowledging GestureScrollBegin events sent to OOPIFs.
Kevin McNeeb35d2f982018-03-05 22:20:562173IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, ScrollLocalSubframeInOOPIF) {
Kevin McNee612feb72018-03-01 00:24:222174 ui::GestureConfiguration::GetInstance()->set_scroll_debounce_interval_in_ms(
2175 0);
2176
2177 // This must be tall enough such that the outer iframe is not scrollable.
2178 GURL main_url(embedded_test_server()->GetURL(
2179 "a.com", "/frame_tree/page_with_tall_positioned_frame.html"));
2180 EXPECT_TRUE(NavigateToURL(shell(), main_url));
2181
2182 // It is safe to obtain the root frame tree node here, as it doesn't change.
2183 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
2184 ASSERT_EQ(1U, root->child_count());
2185
2186 FrameTreeNode* parent_iframe_node = root->child_at(0);
2187 GURL outer_frame_url(embedded_test_server()->GetURL(
2188 "baz.com", "/frame_tree/page_with_positioned_frame.html"));
2189 NavigateFrameToURL(parent_iframe_node, outer_frame_url);
2190
2191 // This must be tall enough such that the inner iframe is scrollable.
2192 FrameTreeNode* nested_iframe_node = parent_iframe_node->child_at(0);
2193 GURL inner_frame_url(
2194 embedded_test_server()->GetURL("baz.com", "/tall_page.html"));
2195 NavigateFrameToURL(nested_iframe_node, inner_frame_url);
2196
2197 ASSERT_EQ(
2198 " Site A ------------ proxies for B\n"
2199 " +--Site B ------- proxies for A\n"
2200 " +--Site B -- proxies for A\n"
2201 "Where A = http://a.com/\n"
2202 " B = http://baz.com/",
2203 DepictFrameTree(root));
2204
2205 RenderWidgetHostViewBase* rwhv_child = static_cast<RenderWidgetHostViewBase*>(
2206 nested_iframe_node->current_frame_host()
2207 ->GetRenderWidgetHost()
2208 ->GetView());
2209
jonross09d21de2018-06-13 12:31:362210 WaitForHitTestDataOrChildSurfaceReady(
2211 parent_iframe_node->current_frame_host());
Kevin McNee612feb72018-03-01 00:24:222212
2213 // When we scroll the inner frame, we should have the GSB be consumed.
2214 // The outer iframe not being scrollable should not cause the GSB to go
2215 // unconsumed.
2216 InputEventAckWaiter ack_observer(
2217 parent_iframe_node->current_frame_host()->GetRenderWidgetHost(),
2218 base::BindRepeating([](content::InputEventAckSource,
2219 content::InputEventAckState state,
2220 const blink::WebInputEvent& event) {
2221 return event.GetType() == blink::WebGestureEvent::kGestureScrollBegin &&
2222 state == content::INPUT_EVENT_ACK_STATE_CONSUMED;
2223 }));
2224
Kevin McNeeb35d2f982018-03-05 22:20:562225 // Wait until renderer's compositor thread is synced. Otherwise the non fast
2226 // scrollable regions won't be set when the event arrives.
2227 MainThreadFrameObserver observer(rwhv_child->GetRenderWidgetHost());
2228 observer.Wait();
2229
2230 // Now scroll the inner frame downward.
Kevin McNee612feb72018-03-01 00:24:222231 blink::WebMouseWheelEvent scroll_event(
2232 blink::WebInputEvent::kMouseWheel, blink::WebInputEvent::kNoModifiers,
2233 blink::WebInputEvent::GetStaticTimeStampForTests());
2234 scroll_event.SetPositionInWidget(90, 110);
2235 scroll_event.delta_x = 0.0f;
2236 scroll_event.delta_y = -50.0f;
2237 scroll_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
2238 scroll_event.has_precise_scrolling_deltas = true;
2239 rwhv_child->ProcessMouseWheelEvent(scroll_event, ui::LatencyInfo());
2240 ack_observer.Wait();
2241}
2242
Ehsan Karamad131b9d62018-03-01 18:35:072243// This test verifies that scrolling an element to view works across OOPIFs. The
2244// testing methodology is based on measuring bounding client rect position of
2245// nested <iframe>'s after the inner-most frame scrolls into view. The
2246// measurements are for two identical pages where one page does not have any
2247// OOPIFs while the other has some nested OOPIFs.
Clark DuVall0a411e832019-01-29 18:59:242248// TODO(crbug.com/827431): This test is flaking on all platforms.
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142249IN_PROC_BROWSER_TEST_F(SitePerProcessProgrammaticScrollTest,
Clark DuVall0a411e832019-01-29 18:59:242250 DISABLED_ScrollElementIntoView) {
Ehsan Karamad131b9d62018-03-01 18:35:072251 const GURL url_a(
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142252 embedded_test_server()->GetURL("a.com", kIframeOutOfViewHTML));
Ehsan Karamad131b9d62018-03-01 18:35:072253 const GURL url_b(
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142254 embedded_test_server()->GetURL("b.com", kIframeOutOfViewHTML));
Ehsan Karamad131b9d62018-03-01 18:35:072255 const GURL url_c(
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142256 embedded_test_server()->GetURL("c.com", kIframeOutOfViewHTML));
Ehsan Karamadf152db882017-10-23 17:41:252257
Ehsan Karamad131b9d62018-03-01 18:35:072258 // Number of <iframe>'s which will not be empty. The actual frame tree has two
2259 // more nodes one for root and one for the inner-most empty <iframe>.
2260 const size_t kNonEmptyIframesCount = 5;
Ehsan Karamad131b9d62018-03-01 18:35:072261 const std::string kScrollIntoViewScript =
2262 "document.body.scrollIntoView({'behavior' : 'instant'});";
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142263 const int kRectDimensionErrorTolerance = 0;
Ehsan Karamadf152db882017-10-23 17:41:252264
Ehsan Karamad131b9d62018-03-01 18:35:072265 // First, recursively set the |scrollTop| and |scrollLeft| of |document.body|
2266 // to its maximum and then navigate the <iframe> to |url_a|. The page will be
2267 // structured as a(a(a(a(a(a(a)))))) where the inner-most <iframe> is empty.
2268 ASSERT_TRUE(NavigateToURL(shell(), url_a));
2269 FrameTreeNode* node = web_contents()->GetFrameTree()->root();
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142270 WaitForOnLoad(node);
Ehsan Karamad131b9d62018-03-01 18:35:072271 std::vector<gfx::Rect> reference_page_bounds_before_scroll = {
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142272 GetBoundingClientRect(node, kIframeSelector)};
Ehsan Karamad131b9d62018-03-01 18:35:072273 node = node->child_at(0);
2274 for (size_t index = 0; index < kNonEmptyIframesCount; ++index) {
2275 NavigateFrameToURL(node, url_a);
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142276 WaitForOnLoad(node);
Ehsan Karamad131b9d62018-03-01 18:35:072277 // Store |document.querySelector('iframe').getBoundingClientRect()|.
2278 reference_page_bounds_before_scroll.push_back(
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142279 GetBoundingClientRect(node, kIframeSelector));
Ehsan Karamad131b9d62018-03-01 18:35:072280 node = node->child_at(0);
2281 }
2282 // Sanity-check: If the page is setup properly then all the <iframe>s should
2283 // be out of view and their bounding rect should not intersect with the
2284 // positive XY plane.
2285 for (const auto& rect : reference_page_bounds_before_scroll)
2286 ASSERT_FALSE(rect.Intersects(kPositiveXYPlane));
2287 // Now scroll the inner-most frame into view.
2288 ASSERT_TRUE(ExecuteScript(node, kScrollIntoViewScript));
2289 // Store current client bounds origins to later compare against those from the
2290 // page which contains OOPIFs.
2291 node = web_contents()->GetFrameTree()->root();
2292 std::vector<gfx::Rect> reference_page_bounds_after_scroll = {
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142293 GetBoundingClientRect(node, kIframeSelector)};
Ehsan Karamad131b9d62018-03-01 18:35:072294 node = node->child_at(0);
2295 for (size_t index = 0; index < kNonEmptyIframesCount; ++index) {
2296 reference_page_bounds_after_scroll.push_back(
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142297 GetBoundingClientRect(node, kIframeSelector));
Ehsan Karamad131b9d62018-03-01 18:35:072298 node = node->child_at(0);
Ehsan Karamadf152db882017-10-23 17:41:252299 }
2300
Ehsan Karamad131b9d62018-03-01 18:35:072301 // Repeat the same process for the page containing OOPIFs. The page is
2302 // structured as b(b(a(c(a(a(a)))))) where the inner-most <iframe> is empty.
2303 ASSERT_TRUE(NavigateToURL(shell(), url_b));
2304 node = web_contents()->GetFrameTree()->root();
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142305 WaitForOnLoad(node);
Ehsan Karamad131b9d62018-03-01 18:35:072306 std::vector<gfx::Rect> test_page_bounds_before_scroll = {
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142307 GetBoundingClientRect(node, kIframeSelector)};
Ehsan Karamad131b9d62018-03-01 18:35:072308 const GURL iframe_urls[] = {url_b, url_a, url_c, url_a, url_a};
2309 node = node->child_at(0);
2310 for (size_t index = 0; index < kNonEmptyIframesCount; ++index) {
2311 NavigateFrameToURL(node, iframe_urls[index]);
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142312 WaitForOnLoad(node);
Ehsan Karamad131b9d62018-03-01 18:35:072313 test_page_bounds_before_scroll.push_back(
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142314 GetBoundingClientRect(node, kIframeSelector));
Ehsan Karamad131b9d62018-03-01 18:35:072315 node = node->child_at(0);
Ehsan Karamadf152db882017-10-23 17:41:252316 }
Ehsan Karamad131b9d62018-03-01 18:35:072317 // Sanity-check: The bounds should match those from non-OOPIF page.
2318 for (size_t index = 0; index < kNonEmptyIframesCount; ++index) {
2319 ASSERT_TRUE(test_page_bounds_before_scroll[index].ApproximatelyEqual(
2320 reference_page_bounds_before_scroll[index],
2321 kRectDimensionErrorTolerance));
2322 }
2323 // Scroll the inner most OOPIF.
2324 ASSERT_TRUE(ExecuteScript(node, kScrollIntoViewScript));
2325 // Now traverse the chain bottom to top and verify the bounds match for each
2326 // <iframe>.
2327 int index = kNonEmptyIframesCount;
2328 RenderFrameHostImpl* current_rfh = node->current_frame_host()->GetParent();
2329 while (current_rfh) {
2330 gfx::Rect current_bounds =
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142331 GetBoundingClientRect(current_rfh->frame_tree_node(), kIframeSelector);
Ehsan Karamad131b9d62018-03-01 18:35:072332 gfx::Rect reference_bounds = reference_page_bounds_after_scroll[index];
2333 if (current_bounds.ApproximatelyEqual(reference_bounds,
2334 kRectDimensionErrorTolerance)) {
2335 current_rfh = current_rfh->GetParent();
2336 --index;
2337 } else {
2338 base::RunLoop run_loop;
2339 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
2340 FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
2341 run_loop.Run();
2342 }
2343 }
EhsanKbd2cea992017-11-23 18:49:082344}
2345
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142346// This test verifies that ScrollFocusedEditableElementIntoView works correctly
2347// for OOPIFs. Essentially, the test verifies that in a similar setup, the
2348// resultant page scale factor is the same for OOPIF and non-OOPIF cases. This
2349// also verifies that in response to the scroll command, the root-layer scrolls
2350// correctly and the <input> is visible in visual viewport.
Maria Khomenko63bdc052018-04-12 18:31:472351#if defined(OS_ANDROID)
2352// crbug.com/793616
2353#define MAYBE_ScrollFocusedEditableElementIntoView \
2354 DISABLED_ScrollFocusedEditableElementIntoView
2355#else
2356#define MAYBE_ScrollFocusedEditableElementIntoView \
2357 ScrollFocusedEditableElementIntoView
2358#endif
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142359IN_PROC_BROWSER_TEST_F(SitePerProcessProgrammaticScrollTest,
Maria Khomenko63bdc052018-04-12 18:31:472360 MAYBE_ScrollFocusedEditableElementIntoView) {
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142361 GURL url_a(embedded_test_server()->GetURL("a.com", kIframeOutOfViewHTML));
2362 GURL url_b(embedded_test_server()->GetURL("b.com", kIframeOutOfViewHTML));
2363
2364#if defined(OS_ANDROID)
2365 // The reason for Android specific code is that
2366 // AutoZoomFocusedNodeToLegibleScale is in blink's WebSettings and difficult
2367 // to access from here. It so happens that the setting is on for Android.
2368
2369 // A lower bound on the ratio of page scale factor after scroll. The actual
2370 // value depends on minReadableCaretHeight / caret_bounds.Height(). The page
2371 // is setup so caret height is quite small so the expected scale should be
2372 // larger than 2.0.
2373 float kLowerBoundOnScaleAfterScroll = 2.0;
2374 float kEpsilon = 0.1;
2375#endif
2376
2377 ASSERT_TRUE(NavigateToURL(shell(), url_a));
EhsanKbd2cea992017-11-23 18:49:082378 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142379 WaitForOnLoad(root);
2380 NavigateFrameToURL(root->child_at(0), url_a);
2381 WaitForOnLoad(root->child_at(0));
2382#if defined(OS_ANDROID)
2383 float scale_before_scroll_nonoopif = GetVisualViewportScale(root);
2384#endif
2385 AddFocusedInputField(root->child_at(0));
2386 // Focusing <input> causes scrollIntoView(). The following line makes sure
2387 // that the <iframe> is out of view again.
2388 SetWindowScroll(root, 0, 0);
2389 ASSERT_FALSE(GetVisualViewport(root).Intersects(
2390 GetBoundingClientRect(root, kIframeSelector)));
2391 root->child_at(0)
2392 ->current_frame_host()
2393 ->GetFrameInputHandler()
2394 ->ScrollFocusedEditableNodeIntoRect(gfx::Rect());
2395 WaitForElementVisible(root, kIframeSelector);
2396#if defined(OS_ANDROID)
2397 float scale_after_scroll_nonoopif = GetVisualViewportScale(root);
2398 // Increased scale means zoom triggered correctly.
2399 EXPECT_GT(scale_after_scroll_nonoopif - scale_before_scroll_nonoopif,
2400 kEpsilon);
2401 EXPECT_GT(scale_after_scroll_nonoopif, kLowerBoundOnScaleAfterScroll);
2402#endif
EhsanKbd2cea992017-11-23 18:49:082403
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142404 // Retry the test on an OOPIF page.
2405 Shell* new_shell = CreateBrowser();
2406 ASSERT_TRUE(NavigateToURL(new_shell, url_b));
2407 root = static_cast<WebContentsImpl*>(new_shell->web_contents())
2408 ->GetFrameTree()
2409 ->root();
2410 WaitForOnLoad(root);
2411#if defined(OS_ANDROID)
2412 float scale_before_scroll_oopif = GetVisualViewportScale(root);
2413 // Sanity-check:
2414 ASSERT_NEAR(scale_before_scroll_oopif, scale_before_scroll_nonoopif,
2415 kEpsilon);
2416#endif
2417 NavigateFrameToURL(root->child_at(0), url_a);
2418 WaitForOnLoad(root->child_at(0));
2419 AddFocusedInputField(root->child_at(0));
2420 SetWindowScroll(root, 0, 0);
2421 ASSERT_FALSE(GetVisualViewport(root).Intersects(
2422 GetBoundingClientRect(root, kIframeSelector)));
2423 root->child_at(0)
2424 ->current_frame_host()
2425 ->GetFrameInputHandler()
2426 ->ScrollFocusedEditableNodeIntoRect(gfx::Rect());
2427 WaitForElementVisible(root, kIframeSelector);
2428#if defined(OS_ANDROID)
2429 float scale_after_scroll_oopif = GetVisualViewportScale(root);
2430 EXPECT_GT(scale_after_scroll_oopif - scale_before_scroll_oopif, kEpsilon);
2431 EXPECT_GT(scale_after_scroll_oopif, kLowerBoundOnScaleAfterScroll);
2432 // The scale is based on the caret height and it should be the same in both
2433 // OOPIF and non-OOPIF pages.
2434 EXPECT_NEAR(scale_after_scroll_oopif, scale_after_scroll_nonoopif, kEpsilon);
2435#endif
2436 // Make sure the <input> is at least partly visible in the |visualViewport|.
2437 gfx::Rect final_visual_viewport_oopif = GetVisualViewport(root);
2438 gfx::Rect iframe_bounds_after_scroll_oopif =
2439 GetBoundingClientRect(root, kIframeSelector);
2440 gfx::Rect input_bounds_after_scroll_oopif =
2441 GetBoundingClientRect(root->child_at(0), kInputSelector);
2442 input_bounds_after_scroll_oopif +=
2443 iframe_bounds_after_scroll_oopif.OffsetFromOrigin();
EhsanKbd2cea992017-11-23 18:49:082444 ASSERT_TRUE(
Ehsan Karamadf2b6bb9a12018-04-06 17:13:142445 final_visual_viewport_oopif.Intersects(input_bounds_after_scroll_oopif));
Ehsan Karamadf152db882017-10-23 17:41:252446}
2447
David Bokan6f46897c2018-04-09 22:32:472448IN_PROC_BROWSER_TEST_F(SitePerProcessProgrammaticScrollTest,
2449 ScrollClippedFocusedEditableElementIntoView) {
2450 GURL url_a(embedded_test_server()->GetURL("a.com", kIframeClippedHTML));
2451 GURL child_url_b(embedded_test_server()->GetURL("b.com", kInputBoxHTML));
2452
2453 ASSERT_TRUE(NavigateToURL(shell(), url_a));
2454 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
2455 WaitForOnLoad(root);
2456 NavigateFrameToURL(root->child_at(0), child_url_b);
2457 WaitForOnLoad(root->child_at(0));
2458
2459 SetWindowScroll(root, 0, 0);
2460 SetWindowScroll(root->child_at(0), 1000, 2000);
2461
2462 float scale_before = GetVisualViewportScale(root);
2463
2464 // The input_box page focuses the input box on load. This call should
2465 // simulate the scroll into view we do when an input box is tapped.
2466 root->child_at(0)
2467 ->current_frame_host()
2468 ->GetFrameInputHandler()
2469 ->ScrollFocusedEditableNodeIntoRect(gfx::Rect());
2470
2471 // The scroll into view is animated on the compositor. Make sure we wait
2472 // until that's completed before testing the rects.
2473 WaitForElementVisible(root, kIframeSelector);
2474 WaitForViewportToStabilize(root);
2475
2476 // These rects are in the coordinate space of the root frame.
2477 gfx::Rect visual_viewport_rect = GetVisualViewport(root);
2478 gfx::Rect window_rect = GetBoundingClientRect(root, ":root");
2479 gfx::Rect iframe_rect = GetBoundingClientRect(root, "iframe");
2480 gfx::Rect clip_rect = GetBoundingClientRect(root, "#clip");
2481
2482 // This is in the coordinate space of the iframe, we'll add the iframe offset
2483 // after to put it into the root frame's coordinate space.
2484 gfx::Rect input_rect = GetBoundingClientRect(root->child_at(0), "input");
2485
2486 // Make sure the input rect is visible in the iframe.
2487 EXPECT_TRUE(gfx::Rect(iframe_rect.size()).Intersects(input_rect))
2488 << "Input box [" << input_rect.ToString() << "] isn't visible in iframe ["
2489 << gfx::Rect(iframe_rect.size()).ToString() << "]";
2490
2491 input_rect += iframe_rect.OffsetFromOrigin();
2492
2493 // Make sure the input rect is visible through the clipping layer.
2494 EXPECT_TRUE(clip_rect.Intersects(input_rect))
2495 << "Input box [" << input_rect.ToString() << "] isn't scrolled into view "
2496 << "of the clipping layer [" << clip_rect.ToString() << "]";
2497
2498 // And finally, it should be visible in the layout and visual viewports.
2499 EXPECT_TRUE(window_rect.Intersects(input_rect))
2500 << "Input box [" << input_rect.ToString() << "] isn't visible in the "
2501 << "layout viewport [" << window_rect.ToString() << "]";
2502 EXPECT_TRUE(visual_viewport_rect.Intersects(input_rect))
2503 << "Input box [" << input_rect.ToString() << "] isn't visible in the "
2504 << "visual viewport [" << visual_viewport_rect.ToString() << "]";
2505
2506 float scale_after = GetVisualViewportScale(root);
2507
2508// Make sure we still zoom in on the input box on platforms that zoom into the
2509// focused editable.
2510#if defined(OS_ANDROID)
2511 EXPECT_GT(scale_after, scale_before);
2512#else
2513 EXPECT_FLOAT_EQ(scale_after, scale_before);
2514#endif
2515}
2516
Dave Tapuskad6703f912019-01-31 23:13:112517IN_PROC_BROWSER_TEST_F(SitePerProcessProgrammaticScrollTest,
2518 ScrolledOutOfView) {
2519 GURL main_frame(
2520 embedded_test_server()->GetURL("a.com", kIframeOutOfViewHTML));
2521 GURL child_url_b(
2522 embedded_test_server()->GetURL("b.com", kIframeOutOfViewHTML));
2523
2524 // This will set up the page frame tree as A(B()).
2525 ASSERT_TRUE(NavigateToURL(shell(), main_frame));
2526 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
2527 WaitForOnLoad(root);
2528 NavigateFrameToURL(root->child_at(0), child_url_b);
2529 WaitForOnLoad(root->child_at(0));
2530
2531 FrameTreeNode* nested_iframe_node = root->child_at(0);
2532 RenderFrameProxyHost* proxy_to_parent =
2533 nested_iframe_node->render_manager()->GetProxyToParent();
2534 CrossProcessFrameConnector* connector =
2535 proxy_to_parent->cross_process_frame_connector();
Dave Tapuskabbc69519c2019-02-04 22:12:082536
2537 while (blink::mojom::FrameVisibility::kRenderedOutOfViewport !=
2538 connector->visibility()) {
2539 base::RunLoop run_loop;
2540 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
2541 FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
2542 run_loop.Run();
2543 }
Dave Tapuskad6703f912019-01-31 23:13:112544}
2545
Ehsan Karamad8504f3e2018-08-03 22:00:322546// This test verifies that smooth scrolling works correctly inside nested OOPIFs
2547// which are same origin with the parent. Note that since the frame tree has
2548// a A(B(A1())) structure, if and A1 and A2 shared the same
2549// SmoothScrollSequencer, then this test would time out or at best be flaky with
2550// random time outs. See https://crbug.com/865446 for more context.
2551IN_PROC_BROWSER_TEST_F(SitePerProcessProgrammaticScrollTest,
2552 SmoothScrollInNestedSameProcessOOPIF) {
2553 GURL main_frame(
2554 embedded_test_server()->GetURL("a.com", kIframeOutOfViewHTML));
2555 GURL child_url_b(
2556 embedded_test_server()->GetURL("b.com", kIframeOutOfViewHTML));
2557 GURL same_origin(
2558 embedded_test_server()->GetURL("a.com", kIframeOutOfViewHTML));
2559
2560 // This will set up the page frame tree as A(B(A1(A2()))) where A1 is later
2561 // asked to scroll the <iframe> element of A2 into view. The important bit
2562 // here is that the inner frame A1 is recursively scrolling (smoothly) an
2563 // element inside its document into view (A2's origin is irrelevant here).
2564 ASSERT_TRUE(NavigateToURL(shell(), main_frame));
2565 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
2566 WaitForOnLoad(root);
2567 NavigateFrameToURL(root->child_at(0), child_url_b);
2568 WaitForOnLoad(root->child_at(0));
2569 auto* nested_ftn = root->child_at(0)->child_at(0);
2570 NavigateFrameToURL(nested_ftn, same_origin);
2571 WaitForOnLoad(nested_ftn);
2572
2573 // *Smoothly* scroll the inner most frame into view.
2574 ASSERT_TRUE(ExecuteScript(
2575 nested_ftn,
2576 "document.querySelector('iframe').scrollIntoView({behavior: 'smooth'})"));
2577 WaitForElementVisible(root, kIframeSelector);
2578 WaitForElementVisible(root->child_at(0), kIframeSelector);
2579 WaitForElementVisible(nested_ftn, kIframeSelector);
2580}
2581
lazyboy2f1a9d12015-04-03 05:16:152582// Tests OOPIF rendering by checking that the RWH of the iframe generates
2583// OnSwapCompositorFrame message.
Ria Jiangabad8d9a2018-01-24 16:52:362584IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CompositorFrameSwapped) {
nick4e68f5252015-08-28 20:17:052585 GURL main_url(embedded_test_server()->GetURL(
2586 "a.com", "/cross_site_iframe_factory.html?a(baz)"));
davidsac6e6c35e42016-11-21 19:45:572587 EXPECT_TRUE(NavigateToURL(shell(), main_url));
lazyboy2f1a9d12015-04-03 05:16:152588
2589 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:002590 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
lazyboy2f1a9d12015-04-03 05:16:152591 ASSERT_EQ(1U, root->child_count());
2592
2593 FrameTreeNode* child_node = root->child_at(0);
nick4e68f5252015-08-28 20:17:052594 GURL site_url(embedded_test_server()->GetURL(
2595 "baz.com", "/cross_site_iframe_factory.html?baz()"));
lazyboy2f1a9d12015-04-03 05:16:152596 EXPECT_EQ(site_url, child_node->current_url());
2597 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
2598 child_node->current_frame_host()->GetSiteInstance());
jonrossafe76382018-05-25 16:44:322599 // Wait for CompositorFrame submission.
2600 RenderFrameSubmissionObserver observer(
2601 child_node->current_frame_host()
2602 ->GetRenderWidgetHost()
2603 ->render_frame_metadata_provider());
2604 observer.WaitForAnyFrameSubmission();
lazyboy2f1a9d12015-04-03 05:16:152605}
2606
creis0f6edddc2015-04-08 00:20:522607// Ensure that OOPIFs are deleted after navigating to a new main frame.
Alex Moshchuk777a6372017-07-25 21:59:432608IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CleanupCrossSiteIframe) {
nick4e68f5252015-08-28 20:17:052609 GURL main_url(embedded_test_server()->GetURL(
2610 "a.com", "/cross_site_iframe_factory.html?a(a,a(a,a(a)))"));
davidsac6e6c35e42016-11-21 19:45:572611 EXPECT_TRUE(NavigateToURL(shell(), main_url));
creis0f6edddc2015-04-08 00:20:522612
2613 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:002614 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
creis0f6edddc2015-04-08 00:20:522615
2616 TestNavigationObserver observer(shell()->web_contents());
2617
2618 // Load a cross-site page into both iframes.
2619 GURL foo_url = embedded_test_server()->GetURL("foo.com", "/title2.html");
2620 NavigateFrameToURL(root->child_at(0), foo_url);
2621 EXPECT_TRUE(observer.last_navigation_succeeded());
2622 EXPECT_EQ(foo_url, observer.last_navigation_url());
2623 NavigateFrameToURL(root->child_at(1), foo_url);
2624 EXPECT_TRUE(observer.last_navigation_succeeded());
2625 EXPECT_EQ(foo_url, observer.last_navigation_url());
2626
2627 // Ensure that we have created a new process for the subframes.
naskof95ab0e2015-05-23 02:27:242628 EXPECT_EQ(
2629 " Site A ------------ proxies for B\n"
2630 " |--Site B ------- proxies for A\n"
2631 " +--Site B ------- proxies for A\n"
nick4e68f5252015-08-28 20:17:052632 "Where A = http://a.com/\n"
naskof95ab0e2015-05-23 02:27:242633 " B = http://foo.com/",
2634 DepictFrameTree(root));
2635
2636 int subframe_process_id = root->child_at(0)
2637 ->current_frame_host()
2638 ->GetSiteInstance()
2639 ->GetProcess()
2640 ->GetID();
2641 int subframe_rvh_id = root->child_at(0)
2642 ->current_frame_host()
2643 ->render_view_host()
2644 ->GetRoutingID();
2645 EXPECT_TRUE(RenderViewHost::FromID(subframe_process_id, subframe_rvh_id));
creis0f6edddc2015-04-08 00:20:522646
2647 // Use Javascript in the parent to remove one of the frames and ensure that
2648 // the subframe goes away.
nickadef4a52016-06-09 18:45:542649 EXPECT_TRUE(ExecuteScript(shell(),
creis0f6edddc2015-04-08 00:20:522650 "document.body.removeChild("
2651 "document.querySelectorAll('iframe')[0])"));
2652 ASSERT_EQ(1U, root->child_count());
2653
2654 // Load a new same-site page in the top-level frame and ensure the other
2655 // subframe goes away.
nick4e68f5252015-08-28 20:17:052656 GURL new_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
davidsac6e6c35e42016-11-21 19:45:572657 EXPECT_TRUE(NavigateToURL(shell(), new_url));
creis0f6edddc2015-04-08 00:20:522658 ASSERT_EQ(0U, root->child_count());
naskof95ab0e2015-05-23 02:27:242659
2660 // Ensure the RVH for the subframe gets cleaned up when the frame goes away.
2661 EXPECT_FALSE(RenderViewHost::FromID(subframe_process_id, subframe_rvh_id));
creis0f6edddc2015-04-08 00:20:522662}
2663
Ken Buchanancacd0082018-01-22 20:38:122664IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, NavigateRemoteFrame) {
nick4e68f5252015-08-28 20:17:052665 GURL main_url(embedded_test_server()->GetURL(
2666 "a.com", "/cross_site_iframe_factory.html?a(a,a(a,a(a)))"));
davidsac6e6c35e42016-11-21 19:45:572667 EXPECT_TRUE(NavigateToURL(shell(), main_url));
japhet70ea1342014-09-30 21:56:392668
2669 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:002670 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
japhet70ea1342014-09-30 21:56:392671
clamyf1ccb4d2015-01-28 17:40:382672 TestNavigationObserver observer(shell()->web_contents());
japhet70ea1342014-09-30 21:56:392673
2674 // Load same-site page into iframe.
2675 FrameTreeNode* child = root->child_at(0);
nick4e68f5252015-08-28 20:17:052676 GURL http_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
japhet70ea1342014-09-30 21:56:392677 NavigateFrameToURL(child, http_url);
clamyf1ccb4d2015-01-28 17:40:382678 EXPECT_EQ(http_url, observer.last_navigation_url());
2679 EXPECT_TRUE(observer.last_navigation_succeeded());
japhet70ea1342014-09-30 21:56:392680
japhet70ea1342014-09-30 21:56:392681 // Load cross-site page into iframe.
nasko30374c72014-10-30 19:18:372682 GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html");
creis29f85682016-11-08 01:52:422683 {
2684 RenderFrameDeletedObserver deleted_observer(child->current_frame_host());
2685 NavigateFrameToURL(root->child_at(0), url);
2686 deleted_observer.WaitUntilDeleted();
2687 }
clamyf1ccb4d2015-01-28 17:40:382688 EXPECT_TRUE(observer.last_navigation_succeeded());
2689 EXPECT_EQ(url, observer.last_navigation_url());
japhet70ea1342014-09-30 21:56:392690
2691 // Ensure that we have created a new process for the subframe.
nick4e68f5252015-08-28 20:17:052692 EXPECT_EQ(
2693 " Site A ------------ proxies for B\n"
2694 " |--Site B ------- proxies for A\n"
2695 " +--Site A ------- proxies for B\n"
2696 " |--Site A -- proxies for B\n"
2697 " +--Site A -- proxies for B\n"
2698 " +--Site A -- proxies for B\n"
2699 "Where A = http://a.com/\n"
2700 " B = http://foo.com/",
2701 DepictFrameTree(root));
japhet70ea1342014-09-30 21:56:392702 SiteInstance* site_instance = child->current_frame_host()->GetSiteInstance();
2703 EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site_instance);
2704
2705 // Emulate the main frame changing the src of the iframe such that it
2706 // navigates cross-site.
nasko30374c72014-10-30 19:18:372707 url = embedded_test_server()->GetURL("bar.com", "/title3.html");
creis29f85682016-11-08 01:52:422708 {
2709 RenderFrameDeletedObserver deleted_observer(child->current_frame_host());
2710 NavigateIframeToURL(shell()->web_contents(), "child-0", url);
2711 deleted_observer.WaitUntilDeleted();
2712 }
clamyf1ccb4d2015-01-28 17:40:382713 EXPECT_TRUE(observer.last_navigation_succeeded());
2714 EXPECT_EQ(url, observer.last_navigation_url());
japhet70ea1342014-09-30 21:56:392715
2716 // Check again that a new process is created and is different from the
2717 // top level one and the previous one.
nick4e68f5252015-08-28 20:17:052718 EXPECT_EQ(
2719 " Site A ------------ proxies for C\n"
2720 " |--Site C ------- proxies for A\n"
2721 " +--Site A ------- proxies for C\n"
2722 " |--Site A -- proxies for C\n"
2723 " +--Site A -- proxies for C\n"
2724 " +--Site A -- proxies for C\n"
2725 "Where A = http://a.com/\n"
2726 " C = http://bar.com/",
2727 DepictFrameTree(root));
japhet70ea1342014-09-30 21:56:392728
japhete6adf142014-10-31 00:01:492729 // Navigate back to the parent's origin and ensure we return to the
2730 // parent's process.
creis29f85682016-11-08 01:52:422731 {
2732 RenderFrameDeletedObserver deleted_observer(child->current_frame_host());
2733 NavigateFrameToURL(child, http_url);
2734 deleted_observer.WaitUntilDeleted();
2735 }
clamyf1ccb4d2015-01-28 17:40:382736 EXPECT_EQ(http_url, observer.last_navigation_url());
2737 EXPECT_TRUE(observer.last_navigation_succeeded());
japhete6adf142014-10-31 00:01:492738 EXPECT_EQ(shell()->web_contents()->GetSiteInstance(),
2739 child->current_frame_host()->GetSiteInstance());
japhet70ea1342014-09-30 21:56:392740}
2741
lfgf52ea142015-03-07 23:03:332742IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
nick4e68f5252015-08-28 20:17:052743 NavigateRemoteFrameToBlankAndDataURLs) {
2744 GURL main_url(embedded_test_server()->GetURL(
2745 "a.com", "/cross_site_iframe_factory.html?a(a,a(a))"));
davidsac6e6c35e42016-11-21 19:45:572746 EXPECT_TRUE(NavigateToURL(shell(), main_url));
lfgf52ea142015-03-07 23:03:332747
2748 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:002749 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
lfgf52ea142015-03-07 23:03:332750
2751 TestNavigationObserver observer(shell()->web_contents());
2752
2753 // Load same-site page into iframe.
2754 FrameTreeNode* child = root->child_at(0);
nick4e68f5252015-08-28 20:17:052755 GURL http_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
lfgf52ea142015-03-07 23:03:332756 NavigateFrameToURL(child, http_url);
2757 EXPECT_EQ(http_url, observer.last_navigation_url());
2758 EXPECT_TRUE(observer.last_navigation_succeeded());
nick4e68f5252015-08-28 20:17:052759 EXPECT_EQ(
2760 " Site A\n"
2761 " |--Site A\n"
2762 " +--Site A\n"
2763 " +--Site A\n"
2764 "Where A = http://a.com/",
2765 DepictFrameTree(root));
lfgf52ea142015-03-07 23:03:332766
2767 // Load cross-site page into iframe.
2768 GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html");
naskod948aa862016-04-26 19:58:522769 NavigateFrameToURL(child, url);
lfgf52ea142015-03-07 23:03:332770 EXPECT_TRUE(observer.last_navigation_succeeded());
2771 EXPECT_EQ(url, observer.last_navigation_url());
nick4e68f5252015-08-28 20:17:052772 EXPECT_EQ(
2773 " Site A ------------ proxies for B\n"
2774 " |--Site B ------- proxies for A\n"
2775 " +--Site A ------- proxies for B\n"
2776 " +--Site A -- proxies for B\n"
2777 "Where A = http://a.com/\n"
2778 " B = http://foo.com/",
2779 DepictFrameTree(root));
lfgf52ea142015-03-07 23:03:332780
2781 // Navigate iframe to a data URL. The navigation happens from a script in the
2782 // parent frame, so the data URL should be committed in the same SiteInstance
2783 // as the parent frame.
lfgf2d4f912016-05-11 23:18:482784 RenderFrameDeletedObserver deleted_observer1(
2785 root->child_at(0)->current_frame_host());
lfgf52ea142015-03-07 23:03:332786 GURL data_url("data:text/html,dataurl");
nick4e68f5252015-08-28 20:17:052787 NavigateIframeToURL(shell()->web_contents(), "child-0", data_url);
lfgf52ea142015-03-07 23:03:332788 EXPECT_TRUE(observer.last_navigation_succeeded());
2789 EXPECT_EQ(data_url, observer.last_navigation_url());
2790
lfgf2d4f912016-05-11 23:18:482791 // Wait for the old process to exit, to verify that the proxies go away.
2792 deleted_observer1.WaitUntilDeleted();
2793
lfgf52ea142015-03-07 23:03:332794 // Ensure that we have navigated using the top level process.
nick4e68f5252015-08-28 20:17:052795 EXPECT_EQ(
2796 " Site A\n"
2797 " |--Site A\n"
2798 " +--Site A\n"
2799 " +--Site A\n"
2800 "Where A = http://a.com/",
2801 DepictFrameTree(root));
lfgf52ea142015-03-07 23:03:332802
2803 // Load cross-site page into iframe.
2804 url = embedded_test_server()->GetURL("bar.com", "/title2.html");
naskod948aa862016-04-26 19:58:522805 NavigateFrameToURL(child, url);
lfgf52ea142015-03-07 23:03:332806 EXPECT_TRUE(observer.last_navigation_succeeded());
2807 EXPECT_EQ(url, observer.last_navigation_url());
nick4e68f5252015-08-28 20:17:052808 EXPECT_EQ(
2809 " Site A ------------ proxies for C\n"
2810 " |--Site C ------- proxies for A\n"
2811 " +--Site A ------- proxies for C\n"
2812 " +--Site A -- proxies for C\n"
2813 "Where A = http://a.com/\n"
2814 " C = http://bar.com/",
2815 DepictFrameTree(root));
lfgf52ea142015-03-07 23:03:332816
2817 // Navigate iframe to about:blank. The navigation happens from a script in the
2818 // parent frame, so it should be committed in the same SiteInstance as the
2819 // parent frame.
lfgf2d4f912016-05-11 23:18:482820 RenderFrameDeletedObserver deleted_observer2(
2821 root->child_at(0)->current_frame_host());
Nasko Oskovcae49c82018-04-27 19:08:132822 GURL about_blank_url("about:blank#foo");
nick4e68f5252015-08-28 20:17:052823 NavigateIframeToURL(shell()->web_contents(), "child-0", about_blank_url);
lfgf52ea142015-03-07 23:03:332824 EXPECT_TRUE(observer.last_navigation_succeeded());
2825 EXPECT_EQ(about_blank_url, observer.last_navigation_url());
2826
lfgf2d4f912016-05-11 23:18:482827 // Wait for the old process to exit, to verify that the proxies go away.
2828 deleted_observer2.WaitUntilDeleted();
2829
lfgf52ea142015-03-07 23:03:332830 // Ensure that we have navigated using the top level process.
nick4e68f5252015-08-28 20:17:052831 EXPECT_EQ(
2832 " Site A\n"
2833 " |--Site A\n"
2834 " +--Site A\n"
2835 " +--Site A\n"
2836 "Where A = http://a.com/",
2837 DepictFrameTree(root));
naskod948aa862016-04-26 19:58:522838
2839 // Load cross-site page into iframe again.
2840 url = embedded_test_server()->GetURL("f00.com", "/title3.html");
2841 NavigateFrameToURL(child, url);
2842 EXPECT_TRUE(observer.last_navigation_succeeded());
2843 EXPECT_EQ(url, observer.last_navigation_url());
2844 EXPECT_EQ(
2845 " Site A ------------ proxies for D\n"
2846 " |--Site D ------- proxies for A\n"
2847 " +--Site A ------- proxies for D\n"
2848 " +--Site A -- proxies for D\n"
2849 "Where A = http://a.com/\n"
2850 " D = http://f00.com/",
2851 DepictFrameTree(root));
2852
2853 // Navigate the iframe itself to about:blank using a script executing in its
2854 // own context. It should stay in the same SiteInstance as before, not the
2855 // parent one.
naskod948aa862016-04-26 19:58:522856 TestFrameNavigationObserver frame_observer(child);
Nasko Oskovcae49c82018-04-27 19:08:132857 ExecuteScriptAsync(child, "window.location.href = 'about:blank#foo';");
naskod948aa862016-04-26 19:58:522858 frame_observer.Wait();
2859 EXPECT_EQ(about_blank_url, child->current_url());
2860
2861 // Ensure that we have navigated using the top level process.
2862 EXPECT_EQ(
2863 " Site A ------------ proxies for D\n"
2864 " |--Site D ------- proxies for A\n"
2865 " +--Site A ------- proxies for D\n"
2866 " +--Site A -- proxies for D\n"
2867 "Where A = http://a.com/\n"
2868 " D = http://f00.com/",
2869 DepictFrameTree(root));
lfgf52ea142015-03-07 23:03:332870}
2871
lazyboybb1af562015-02-04 02:36:022872// This test checks that killing a renderer process of a remote frame
2873// and then navigating some other frame to the same SiteInstance of the killed
2874// process works properly.
2875// This can be illustrated as follows,
2876// where 1/2/3 are FrameTreeNode-s and A/B are processes and B* is the killed
2877// B process:
lazyboyf2852202014-12-19 05:31:522878//
lazyboybb1af562015-02-04 02:36:022879// 1 A A A
2880// / \ -> / \ -> Kill B -> / \ -> Navigate 3 to B -> / \ .
2881// 2 3 B A B* A B* B
2882//
2883// Initially, node1.proxy_hosts_ = {B}
2884// After we kill B, we make sure B stays in node1.proxy_hosts_, then we navigate
2885// 3 to B and we expect that to complete normally.
2886// See http://crbug.com/432107.
2887//
2888// Note that due to http://crbug.com/450681, node2 cannot be re-navigated to
2889// site B and stays in not rendered state.
2890IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
2891 NavigateRemoteFrameToKilledProcess) {
2892 GURL main_url(embedded_test_server()->GetURL(
nick4e68f5252015-08-28 20:17:052893 "foo.com", "/cross_site_iframe_factory.html?foo.com(bar.com, foo.com)"));
davidsac6e6c35e42016-11-21 19:45:572894 EXPECT_TRUE(NavigateToURL(shell(), main_url));
lazyboybb1af562015-02-04 02:36:022895
2896 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:002897 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
lazyboybb1af562015-02-04 02:36:022898
2899 TestNavigationObserver observer(shell()->web_contents());
2900 ASSERT_EQ(2U, root->child_count());
2901
2902 // Make sure node2 points to the correct cross-site page.
nick4e68f5252015-08-28 20:17:052903 GURL site_b_url = embedded_test_server()->GetURL(
2904 "bar.com", "/cross_site_iframe_factory.html?bar.com()");
lazyboybb1af562015-02-04 02:36:022905 FrameTreeNode* node2 = root->child_at(0);
2906 EXPECT_EQ(site_b_url, node2->current_url());
2907
2908 // Kill that cross-site renderer.
Ria Jiangabad8d9a2018-01-24 16:52:362909 RenderProcessHost* child_process = node2->current_frame_host()->GetProcess();
lazyboybb1af562015-02-04 02:36:022910 RenderProcessHostWatcher crash_observer(
2911 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:452912 child_process->Shutdown(0);
lazyboybb1af562015-02-04 02:36:022913 crash_observer.Wait();
2914
2915 // Now navigate the second iframe (node3) to the same site as the node2.
2916 FrameTreeNode* node3 = root->child_at(1);
2917 NavigateFrameToURL(node3, site_b_url);
2918 EXPECT_TRUE(observer.last_navigation_succeeded());
2919 EXPECT_EQ(site_b_url, observer.last_navigation_url());
2920}
2921
avallee8e881592016-11-19 00:39:302922// This test ensures that WebContentsImpl::FocusOwningWebContents does not crash
2923// the browser if the currently focused frame's renderer has disappeared.
2924IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, RemoveFocusFromKilledFrame) {
2925 GURL main_url(embedded_test_server()->GetURL(
2926 "foo.com", "/cross_site_iframe_factory.html?foo.com(bar.com)"));
2927 NavigateToURL(shell(), main_url);
2928
2929 // It is safe to obtain the root frame tree node here, as it doesn't change.
2930 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
2931
2932 TestNavigationObserver observer(shell()->web_contents());
2933 ASSERT_EQ(1U, root->child_count());
2934
2935 // Make sure node2 points to the correct cross-site page.
2936 GURL site_b_url = embedded_test_server()->GetURL(
2937 "bar.com", "/cross_site_iframe_factory.html?bar.com()");
2938 FrameTreeNode* node2 = root->child_at(0);
2939 EXPECT_EQ(site_b_url, node2->current_url());
2940
2941 web_contents()->SetFocusedFrame(
2942 node2, node2->current_frame_host()->GetSiteInstance());
2943
2944 // Kill that cross-site renderer.
2945 RenderProcessHost* child_process = node2->current_frame_host()->GetProcess();
2946 RenderProcessHostWatcher crash_observer(
2947 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:452948 child_process->Shutdown(0);
avallee8e881592016-11-19 00:39:302949 crash_observer.Wait();
2950
2951 // Try to focus the root's owning WebContents.
2952 web_contents()->FocusOwningWebContents(
2953 root->current_frame_host()->GetRenderWidgetHost());
2954}
2955
lazyboybb1af562015-02-04 02:36:022956// This test is similar to
2957// SitePerProcessBrowserTest.NavigateRemoteFrameToKilledProcess with
2958// addition that node2 also has a cross-origin frame to site C.
2959//
2960// 1 A A A
2961// / \ / \ / \ / \ .
2962// 2 3 -> B A -> Kill B -> B* A -> Navigate 3 -> B* B
lazyboyf2852202014-12-19 05:31:522963// / /
2964// 4 C
2965//
lazyboybb1af562015-02-04 02:36:022966// Initially, node1.proxy_hosts_ = {B, C}
2967// After we kill B, we make sure B stays in node1.proxy_hosts_, but
2968// C gets cleared from node1.proxy_hosts_.
lazyboyf2852202014-12-19 05:31:522969//
lazyboybb1af562015-02-04 02:36:022970// Note that due to http://crbug.com/450681, node2 cannot be re-navigated to
2971// site B and stays in not rendered state.
lazyboyf2852202014-12-19 05:31:522972IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
lazyboybb1af562015-02-04 02:36:022973 NavigateRemoteFrameToKilledProcessWithSubtree) {
nick44bacf32015-04-14 02:06:392974 GURL main_url(embedded_test_server()->GetURL(
nick4e68f5252015-08-28 20:17:052975 "a.com", "/cross_site_iframe_factory.html?a(bar(baz), a)"));
davidsac6e6c35e42016-11-21 19:45:572976 EXPECT_TRUE(NavigateToURL(shell(), main_url));
lazyboyf2852202014-12-19 05:31:522977
2978 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:002979 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
clamyf1ccb4d2015-01-28 17:40:382980 TestNavigationObserver observer(shell()->web_contents());
lazyboyf2852202014-12-19 05:31:522981
2982 ASSERT_EQ(2U, root->child_count());
2983
nick4e68f5252015-08-28 20:17:052984 GURL site_b_url(embedded_test_server()->GetURL(
2985 "bar.com", "/cross_site_iframe_factory.html?bar(baz())"));
clamyf1ccb4d2015-01-28 17:40:382986 // We can't use a TestNavigationObserver to verify the URL here,
lazyboyf2852202014-12-19 05:31:522987 // since the frame has children that may have clobbered it in the observer.
2988 EXPECT_EQ(site_b_url, root->child_at(0)->current_url());
2989
2990 // Ensure that a new process is created for node2.
2991 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
2992 root->child_at(0)->current_frame_host()->GetSiteInstance());
2993 // Ensure that a new process is *not* created for node3.
2994 EXPECT_EQ(shell()->web_contents()->GetSiteInstance(),
2995 root->child_at(1)->current_frame_host()->GetSiteInstance());
2996
2997 ASSERT_EQ(1U, root->child_at(0)->child_count());
2998
lazyboybb1af562015-02-04 02:36:022999 // Make sure node4 points to the correct cross-site page.
lazyboyf2852202014-12-19 05:31:523000 FrameTreeNode* node4 = root->child_at(0)->child_at(0);
nick4e68f5252015-08-28 20:17:053001 GURL site_c_url(embedded_test_server()->GetURL(
3002 "baz.com", "/cross_site_iframe_factory.html?baz()"));
lazyboybb1af562015-02-04 02:36:023003 EXPECT_EQ(site_c_url, node4->current_url());
3004
3005 // |site_instance_c| is expected to go away once we kill |child_process_b|
3006 // below, so create a local scope so we can extend the lifetime of
3007 // |site_instance_c| with a refptr.
3008 {
nick44bacf32015-04-14 02:06:393009 // Initially each frame has proxies for the other sites.
3010 EXPECT_EQ(
3011 " Site A ------------ proxies for B C\n"
3012 " |--Site B ------- proxies for A C\n"
3013 " | +--Site C -- proxies for A B\n"
3014 " +--Site A ------- proxies for B C\n"
3015 "Where A = http://a.com/\n"
3016 " B = http://bar.com/\n"
3017 " C = http://baz.com/",
3018 DepictFrameTree(root));
lazyboybb1af562015-02-04 02:36:023019
nick44bacf32015-04-14 02:06:393020 // Kill the render process for Site B.
lazyboybb1af562015-02-04 02:36:023021 RenderProcessHost* child_process_b =
3022 root->child_at(0)->current_frame_host()->GetProcess();
3023 RenderProcessHostWatcher crash_observer(
3024 child_process_b, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:453025 child_process_b->Shutdown(0);
lazyboybb1af562015-02-04 02:36:023026 crash_observer.Wait();
3027
nick44bacf32015-04-14 02:06:393028 // The Site C frame (a child of the crashed Site B frame) should go away,
3029 // and there should be no remaining proxies for site C anywhere.
3030 EXPECT_EQ(
3031 " Site A ------------ proxies for B\n"
3032 " |--Site B ------- proxies for A\n"
3033 " +--Site A ------- proxies for B\n"
3034 "Where A = http://a.com/\n"
3035 " B = http://bar.com/ (no process)",
3036 DepictFrameTree(root));
lazyboybb1af562015-02-04 02:36:023037 }
3038
nick44bacf32015-04-14 02:06:393039 // Now navigate the second iframe (node3) to Site B also.
lazyboybb1af562015-02-04 02:36:023040 FrameTreeNode* node3 = root->child_at(1);
3041 GURL url = embedded_test_server()->GetURL("bar.com", "/title1.html");
3042 NavigateFrameToURL(node3, url);
clamyf1ccb4d2015-01-28 17:40:383043 EXPECT_TRUE(observer.last_navigation_succeeded());
lazyboybb1af562015-02-04 02:36:023044 EXPECT_EQ(url, observer.last_navigation_url());
nick44bacf32015-04-14 02:06:393045
3046 EXPECT_EQ(
3047 " Site A ------------ proxies for B\n"
3048 " |--Site B ------- proxies for A\n"
3049 " +--Site B ------- proxies for A\n"
3050 "Where A = http://a.com/\n"
3051 " B = http://bar.com/",
3052 DepictFrameTree(root));
lazyboybb1af562015-02-04 02:36:023053}
3054
creis93218682016-01-28 04:34:103055// Ensure that the renderer process doesn't crash when the main frame navigates
3056// a remote child to a page that results in a network error.
3057// See https://crbug.com/558016.
3058IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, NavigateRemoteAfterError) {
3059 GURL main_url(embedded_test_server()->GetURL(
3060 "a.com", "/cross_site_iframe_factory.html?a(a)"));
davidsac6e6c35e42016-11-21 19:45:573061 EXPECT_TRUE(NavigateToURL(shell(), main_url));
creis93218682016-01-28 04:34:103062
3063 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:003064 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
creis93218682016-01-28 04:34:103065
creis93218682016-01-28 04:34:103066 // Load same-site page into iframe.
clamy7531fd262016-02-26 12:37:153067 {
3068 TestNavigationObserver observer(shell()->web_contents());
3069 FrameTreeNode* child = root->child_at(0);
3070 GURL http_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
3071 NavigateFrameToURL(child, http_url);
3072 EXPECT_EQ(http_url, observer.last_navigation_url());
3073 EXPECT_TRUE(observer.last_navigation_succeeded());
3074 observer.Wait();
3075 }
creis93218682016-01-28 04:34:103076
3077 // Load cross-site page into iframe.
clamy7531fd262016-02-26 12:37:153078 {
3079 TestNavigationObserver observer(shell()->web_contents());
3080 FrameTreeNode* child = root->child_at(0);
3081 GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html");
3082 NavigateFrameToURL(root->child_at(0), url);
3083 EXPECT_TRUE(observer.last_navigation_succeeded());
3084 EXPECT_EQ(url, observer.last_navigation_url());
3085 observer.Wait();
creis93218682016-01-28 04:34:103086
clamy7531fd262016-02-26 12:37:153087 // Ensure that we have created a new process for the subframe.
3088 EXPECT_EQ(
3089 " Site A ------------ proxies for B\n"
3090 " +--Site B ------- proxies for A\n"
3091 "Where A = http://a.com/\n"
3092 " B = http://foo.com/",
3093 DepictFrameTree(root));
3094 SiteInstance* site_instance =
3095 child->current_frame_host()->GetSiteInstance();
3096 EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site_instance);
3097 }
creis93218682016-01-28 04:34:103098
3099 // Stop the test server and try to navigate the remote frame.
clamy7531fd262016-02-26 12:37:153100 {
3101 GURL url = embedded_test_server()->GetURL("bar.com", "/title3.html");
3102 EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete());
3103 NavigateIframeToURL(shell()->web_contents(), "child-0", url);
3104 }
creis93218682016-01-28 04:34:103105}
3106
creis1857908a2016-02-25 20:31:523107// Ensure that a cross-site page ends up in the correct process when it
3108// successfully loads after earlier encountering a network error for it.
3109// See https://crbug.com/560511.
3110// TODO(creis): Make the net error page show in the correct process as well,
3111// per https://crbug.com/588314.
3112IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, ProcessTransferAfterError) {
3113 GURL main_url(embedded_test_server()->GetURL(
3114 "a.com", "/cross_site_iframe_factory.html?a(a)"));
3115 EXPECT_TRUE(NavigateToURL(shell(), main_url));
3116
3117 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:003118 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
creis1857908a2016-02-25 20:31:523119 FrameTreeNode* child = root->child_at(0);
3120 GURL url_a = child->current_url();
3121
3122 // Disable host resolution in the test server and try to navigate the subframe
jam5c162d72017-01-26 16:47:193123 // cross-site, which will lead to a committed net error.
creis1857908a2016-02-25 20:31:523124 GURL url_b = embedded_test_server()->GetURL("b.com", "/title3.html");
John Abd-El-Maleka1e25a3a2017-12-04 23:21:083125 std::unique_ptr<URLLoaderInterceptor> url_loader_interceptor;
John Abd-El-Malek64aa466c2018-05-02 20:59:313126 url_loader_interceptor = std::make_unique<URLLoaderInterceptor>(
3127 base::BindRepeating([](URLLoaderInterceptor::RequestParams* params) {
3128 network::URLLoaderCompletionStatus status;
3129 status.error_code = net::ERR_NOT_IMPLEMENTED;
3130 params->client->OnComplete(status);
3131 return true;
3132 }));
jambcc67882017-04-28 18:20:093133
creis1857908a2016-02-25 20:31:523134 TestNavigationObserver observer(shell()->web_contents());
3135 NavigateIframeToURL(shell()->web_contents(), "child-0", url_b);
jam5c162d72017-01-26 16:47:193136 EXPECT_FALSE(observer.last_navigation_succeeded());
creis1857908a2016-02-25 20:31:523137 EXPECT_EQ(url_b, observer.last_navigation_url());
clamy6e0ee03e2016-03-02 15:15:523138 EXPECT_EQ(2, shell()->web_contents()->GetController().GetEntryCount());
3139
Nasko Oskov63dda2952018-02-09 19:35:423140 // Ensure that we have created a new process for the subframe.
3141 EXPECT_EQ(
3142 " Site A ------------ proxies for B\n"
3143 " +--Site B ------- proxies for A\n"
3144 "Where A = http://a.com/\n"
3145 " B = http://b.com/",
3146 DepictFrameTree(root));
3147 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
3148 child->current_frame_host()->GetSiteInstance());
creis1857908a2016-02-25 20:31:523149
Nasko Oskov63dda2952018-02-09 19:35:423150 // We have switched RenderFrameHosts for the subframe, so the last successful
3151 // url should be empty (since the frame only loaded an error page).
3152 EXPECT_EQ(GURL(), child->current_frame_host()->last_successful_url());
creis1857908a2016-02-25 20:31:523153 EXPECT_EQ(url_b, child->current_url());
3154 EXPECT_EQ("null", child->current_origin().Serialize());
3155
3156 // Try again after re-enabling host resolution.
John Abd-El-Malek64aa466c2018-05-02 20:59:313157 url_loader_interceptor.reset();
jambcc67882017-04-28 18:20:093158
creis1857908a2016-02-25 20:31:523159 NavigateIframeToURL(shell()->web_contents(), "child-0", url_b);
3160 EXPECT_TRUE(observer.last_navigation_succeeded());
3161 EXPECT_EQ(url_b, observer.last_navigation_url());
3162
3163 // The FrameTreeNode should have updated its URL and origin.
3164 EXPECT_EQ(url_b, child->current_frame_host()->last_successful_url());
3165 EXPECT_EQ(url_b, child->current_url());
3166 EXPECT_EQ(url_b.GetOrigin().spec(),
3167 child->current_origin().Serialize() + '/');
3168
3169 // Ensure that we have created a new process for the subframe.
clamy6e0ee03e2016-03-02 15:15:523170 // PlzNavigate: the subframe should still be in its separate process.
creis1857908a2016-02-25 20:31:523171 EXPECT_EQ(
3172 " Site A ------------ proxies for B\n"
3173 " +--Site B ------- proxies for A\n"
3174 "Where A = http://a.com/\n"
3175 " B = http://b.com/",
3176 DepictFrameTree(root));
3177 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
3178 child->current_frame_host()->GetSiteInstance());
3179
3180 // Make sure that the navigation replaced the error page and that going back
3181 // ends up on the original site.
3182 EXPECT_EQ(2, shell()->web_contents()->GetController().GetEntryCount());
3183 {
lfgf2d4f912016-05-11 23:18:483184 RenderFrameDeletedObserver deleted_observer(child->current_frame_host());
creis1857908a2016-02-25 20:31:523185 TestNavigationObserver back_load_observer(shell()->web_contents());
3186 shell()->web_contents()->GetController().GoBack();
3187 back_load_observer.Wait();
lfgf2d4f912016-05-11 23:18:483188
3189 // Wait for the old process to exit, to verify that the proxies go away.
3190 deleted_observer.WaitUntilDeleted();
creis1857908a2016-02-25 20:31:523191 }
3192 EXPECT_EQ(
3193 " Site A\n"
3194 " +--Site A\n"
3195 "Where A = http://a.com/",
3196 DepictFrameTree(root));
3197 EXPECT_EQ(shell()->web_contents()->GetSiteInstance(),
3198 child->current_frame_host()->GetSiteInstance());
3199 EXPECT_EQ(url_a, child->current_frame_host()->last_successful_url());
3200 EXPECT_EQ(url_a, child->current_url());
3201 EXPECT_EQ(url_a.GetOrigin().spec(),
3202 child->current_origin().Serialize() + '/');
3203}
3204
alexmosdcbe3fb42015-04-29 23:10:373205// Verify that killing a cross-site frame's process B and then navigating a
3206// frame to B correctly recreates all proxies in B.
3207//
3208// 1 A A A
3209// / | \ / | \ / | \ / | \ .
3210// 2 3 4 -> B A A -> Kill B -> B* A A -> B* B A
3211//
3212// After the last step, the test sends a postMessage from node 3 to node 4,
3213// verifying that a proxy for node 4 has been recreated in process B. This
3214// verifies the fix for https://crbug.com/478892.
3215IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
3216 NavigatingToKilledProcessRestoresAllProxies) {
3217 // Navigate to a page with three frames: one cross-site and two same-site.
3218 GURL main_url(embedded_test_server()->GetURL(
3219 "a.com", "/frame_tree/page_with_three_frames.html"));
davidsac6e6c35e42016-11-21 19:45:573220 EXPECT_TRUE(NavigateToURL(shell(), main_url));
alexmosdcbe3fb42015-04-29 23:10:373221
3222 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:003223 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmosdcbe3fb42015-04-29 23:10:373224 TestNavigationObserver observer(shell()->web_contents());
3225
3226 EXPECT_EQ(
3227 " Site A ------------ proxies for B\n"
3228 " |--Site B ------- proxies for A\n"
3229 " |--Site A ------- proxies for B\n"
3230 " +--Site A ------- proxies for B\n"
3231 "Where A = http://a.com/\n"
3232 " B = http://b.com/",
3233 DepictFrameTree(root));
3234
3235 // Kill the first subframe's b.com renderer.
3236 RenderProcessHost* child_process =
3237 root->child_at(0)->current_frame_host()->GetProcess();
3238 RenderProcessHostWatcher crash_observer(
3239 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:453240 child_process->Shutdown(0);
alexmosdcbe3fb42015-04-29 23:10:373241 crash_observer.Wait();
3242
3243 // Navigate the second subframe to b.com to recreate the b.com process.
3244 GURL b_url = embedded_test_server()->GetURL("b.com", "/post_message.html");
3245 NavigateFrameToURL(root->child_at(1), b_url);
alexmosdcbe3fb42015-04-29 23:10:373246 EXPECT_TRUE(observer.last_navigation_succeeded());
3247 EXPECT_EQ(b_url, observer.last_navigation_url());
3248 EXPECT_TRUE(root->child_at(1)->current_frame_host()->IsRenderFrameLive());
3249
3250 EXPECT_EQ(
3251 " Site A ------------ proxies for B\n"
3252 " |--Site B ------- proxies for A\n"
3253 " |--Site B ------- proxies for A\n"
3254 " +--Site A ------- proxies for B\n"
3255 "Where A = http://a.com/\n"
3256 " B = http://b.com/",
3257 DepictFrameTree(root));
3258
3259 // Check that third subframe's proxy is available in the b.com process by
alexmos9f8705a2015-05-06 19:58:593260 // sending it a postMessage from second subframe, and waiting for a reply.
3261 PostMessageAndWaitForReply(root->child_at(1),
3262 "postToSibling('subframe-msg','frame3')",
3263 "\"done-frame2\"");
alexmosdcbe3fb42015-04-29 23:10:373264}
3265
alexmosa3988992015-05-14 23:26:213266// Verify that proxy creation doesn't recreate a crashed process if no frame
3267// will be created in it.
3268//
3269// 1 A A A
3270// / | \ / | \ / | \ / | \ .
3271// 2 3 4 -> B A A -> Kill B -> B* A A -> B* A A
3272// \ .
3273// A
3274//
3275// The test kills process B (node 2), creates a child frame of node 4 in
3276// process A, and then checks that process B isn't resurrected to create a
3277// proxy for the new child frame. See https://crbug.com/476846.
3278IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
3279 CreateChildFrameAfterKillingProcess) {
3280 // Navigate to a page with three frames: one cross-site and two same-site.
3281 GURL main_url(embedded_test_server()->GetURL(
3282 "a.com", "/frame_tree/page_with_three_frames.html"));
3283 EXPECT_TRUE(NavigateToURL(shell(), main_url));
3284
3285 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:003286 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmosa3988992015-05-14 23:26:213287
3288 EXPECT_EQ(
3289 " Site A ------------ proxies for B\n"
3290 " |--Site B ------- proxies for A\n"
3291 " |--Site A ------- proxies for B\n"
3292 " +--Site A ------- proxies for B\n"
3293 "Where A = http://a.com/\n"
3294 " B = http://b.com/",
3295 DepictFrameTree(root));
3296 SiteInstance* b_site_instance =
3297 root->child_at(0)->current_frame_host()->GetSiteInstance();
3298
3299 // Kill the first subframe's renderer (B).
3300 RenderProcessHost* child_process =
3301 root->child_at(0)->current_frame_host()->GetProcess();
3302 RenderProcessHostWatcher crash_observer(
3303 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:453304 child_process->Shutdown(0);
alexmosa3988992015-05-14 23:26:213305 crash_observer.Wait();
3306
3307 // Add a new child frame to the third subframe.
3308 RenderFrameHostCreatedObserver frame_observer(shell()->web_contents(), 1);
3309 EXPECT_TRUE(ExecuteScript(
nickadef4a52016-06-09 18:45:543310 root->child_at(2),
alexmosa3988992015-05-14 23:26:213311 "document.body.appendChild(document.createElement('iframe'));"));
3312 frame_observer.Wait();
3313
3314 // The new frame should have a RenderFrameProxyHost for B, but it should not
3315 // be alive, and B should still not have a process (verified by last line of
3316 // expected DepictFrameTree output).
3317 EXPECT_EQ(
3318 " Site A ------------ proxies for B\n"
3319 " |--Site B ------- proxies for A\n"
3320 " |--Site A ------- proxies for B\n"
3321 " +--Site A ------- proxies for B\n"
3322 " +--Site A -- proxies for B\n"
3323 "Where A = http://a.com/\n"
3324 " B = http://b.com/ (no process)",
3325 DepictFrameTree(root));
3326 FrameTreeNode* grandchild = root->child_at(2)->child_at(0);
3327 RenderFrameProxyHost* grandchild_rfph =
3328 grandchild->render_manager()->GetRenderFrameProxyHost(b_site_instance);
3329 EXPECT_FALSE(grandchild_rfph->is_render_frame_proxy_live());
3330
3331 // Navigate the second subframe to b.com to recreate process B.
3332 TestNavigationObserver observer(shell()->web_contents());
3333 GURL b_url = embedded_test_server()->GetURL("b.com", "/title1.html");
3334 NavigateFrameToURL(root->child_at(1), b_url);
3335 EXPECT_TRUE(observer.last_navigation_succeeded());
3336 EXPECT_EQ(b_url, observer.last_navigation_url());
3337
3338 // Ensure that the grandchild RenderFrameProxy in B was created when process
3339 // B was restored.
3340 EXPECT_TRUE(grandchild_rfph->is_render_frame_proxy_live());
3341}
3342
alexmosd6ac54672015-06-25 18:10:143343// Verify that creating a child frame after killing and reloading an opener
3344// process doesn't crash. See https://crbug.com/501152.
3345// 1. Navigate to site A.
3346// 2. Open a popup with window.open and navigate it cross-process to site B.
3347// 3. Kill process A for the original tab.
3348// 4. Reload the original tab to resurrect process A.
3349// 5. Add a child frame to the top-level frame in the popup tab B.
3350// In step 5, we try to create proxies for the child frame in all SiteInstances
3351// for which its parent has proxies. This includes A. However, even though
3352// process A is live (step 4), the parent proxy in A is not live (which was
3353// incorrectly assumed previously). This is because step 4 does not resurrect
3354// proxies for popups opened before the crash.
3355IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
3356 CreateChildFrameAfterKillingOpener) {
3357 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
3358 EXPECT_TRUE(NavigateToURL(shell(), main_url));
3359
3360 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:003361 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmosd6ac54672015-06-25 18:10:143362 SiteInstance* site_instance_a = root->current_frame_host()->GetSiteInstance();
3363
3364 // Open a popup and navigate it cross-process to b.com.
3365 ShellAddedObserver new_shell_observer;
nickadef4a52016-06-09 18:45:543366 EXPECT_TRUE(ExecuteScript(root, "popup = window.open('about:blank');"));
alexmosd6ac54672015-06-25 18:10:143367 Shell* popup = new_shell_observer.GetShell();
3368 GURL popup_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
Alex Moshchuk7e26eca2018-03-03 01:34:293369 EXPECT_TRUE(NavigateToURLFromRenderer(popup, popup_url));
alexmosd6ac54672015-06-25 18:10:143370
3371 // Verify that each top-level frame has proxies in the other's SiteInstance.
3372 FrameTreeNode* popup_root =
3373 static_cast<WebContentsImpl*>(popup->web_contents())
3374 ->GetFrameTree()
3375 ->root();
3376 EXPECT_EQ(
3377 " Site A ------------ proxies for B\n"
3378 "Where A = http://a.com/\n"
3379 " B = http://b.com/",
3380 DepictFrameTree(root));
3381 EXPECT_EQ(
3382 " Site B ------------ proxies for A\n"
3383 "Where A = http://a.com/\n"
3384 " B = http://b.com/",
3385 DepictFrameTree(popup_root));
3386
3387 // Kill the first window's renderer (a.com).
3388 RenderProcessHost* child_process = root->current_frame_host()->GetProcess();
3389 RenderProcessHostWatcher crash_observer(
3390 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:453391 child_process->Shutdown(0);
alexmosd6ac54672015-06-25 18:10:143392 crash_observer.Wait();
3393 EXPECT_FALSE(root->current_frame_host()->IsRenderFrameLive());
3394
3395 // The proxy for the popup in a.com should've died.
3396 RenderFrameProxyHost* rfph =
3397 popup_root->render_manager()->GetRenderFrameProxyHost(site_instance_a);
3398 EXPECT_FALSE(rfph->is_render_frame_proxy_live());
3399
3400 // Recreate the a.com renderer.
3401 EXPECT_TRUE(NavigateToURL(shell(), main_url));
3402 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
3403
3404 // The popup's proxy in a.com should still not be live. Re-navigating the
3405 // main window to a.com doesn't reinitialize a.com proxies for popups
3406 // previously opened from the main window.
3407 EXPECT_FALSE(rfph->is_render_frame_proxy_live());
3408
3409 // Add a new child frame on the popup.
3410 RenderFrameHostCreatedObserver frame_observer(popup->web_contents(), 1);
3411 EXPECT_TRUE(ExecuteScript(
nickadef4a52016-06-09 18:45:543412 popup, "document.body.appendChild(document.createElement('iframe'));"));
alexmosd6ac54672015-06-25 18:10:143413 frame_observer.Wait();
3414
3415 // Both the child frame's and its parent's proxies should still not be live.
3416 // The main page can't reach them since it lost reference to the popup after
3417 // it crashed, so there is no need to create them.
3418 EXPECT_FALSE(rfph->is_render_frame_proxy_live());
3419 RenderFrameProxyHost* child_rfph =
3420 popup_root->child_at(0)->render_manager()->GetRenderFrameProxyHost(
3421 site_instance_a);
3422 EXPECT_TRUE(child_rfph);
3423 EXPECT_FALSE(child_rfph->is_render_frame_proxy_live());
3424}
3425
lazyboybb1af562015-02-04 02:36:023426// In A-embed-B-embed-C scenario, verify that killing process B clears proxies
3427// of C from the tree.
3428//
3429// 1 A A
3430// / \ / \ / \ .
3431// 2 3 -> B A -> Kill B -> B* A
3432// / /
3433// 4 C
3434//
3435// node1 is the root.
3436// Initially, both node1.proxy_hosts_ and node3.proxy_hosts_ contain C.
3437// After we kill B, make sure proxies for C are cleared.
3438IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
Ken Buchanancacd0082018-01-22 20:38:123439 KillingRendererClearsDescendantProxies) {
nick44bacf32015-04-14 02:06:393440 GURL main_url(embedded_test_server()->GetURL(
3441 "a.com", "/frame_tree/page_with_two_frames_nested.html"));
davidsac6e6c35e42016-11-21 19:45:573442 EXPECT_TRUE(NavigateToURL(shell(), main_url));
lazyboybb1af562015-02-04 02:36:023443
3444 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:003445 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
lazyboybb1af562015-02-04 02:36:023446 ASSERT_EQ(2U, root->child_count());
3447
Ria Jiangabad8d9a2018-01-24 16:52:363448 GURL site_b_url(embedded_test_server()->GetURL(
3449 "bar.com", "/frame_tree/page_with_one_frame.html"));
lazyboybb1af562015-02-04 02:36:023450 // We can't use a TestNavigationObserver to verify the URL here,
3451 // since the frame has children that may have clobbered it in the observer.
3452 EXPECT_EQ(site_b_url, root->child_at(0)->current_url());
3453
3454 // Ensure that a new process is created for node2.
3455 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
3456 root->child_at(0)->current_frame_host()->GetSiteInstance());
3457 // Ensure that a new process is *not* created for node3.
3458 EXPECT_EQ(shell()->web_contents()->GetSiteInstance(),
3459 root->child_at(1)->current_frame_host()->GetSiteInstance());
3460
3461 ASSERT_EQ(1U, root->child_at(0)->child_count());
3462
3463 // Make sure node4 points to the correct cross-site-page.
3464 FrameTreeNode* node4 = root->child_at(0)->child_at(0);
3465 GURL site_c_url(embedded_test_server()->GetURL("baz.com", "/title1.html"));
3466 EXPECT_EQ(site_c_url, node4->current_url());
lazyboyf2852202014-12-19 05:31:523467
creisceaabfd52015-05-27 20:26:463468 // |site_instance_c|'s frames and proxies are expected to go away once we kill
3469 // |child_process_b| below.
nick44bacf32015-04-14 02:06:393470 scoped_refptr<SiteInstanceImpl> site_instance_c =
3471 node4->current_frame_host()->GetSiteInstance();
lazyboyf2852202014-12-19 05:31:523472
nick44bacf32015-04-14 02:06:393473 // Initially proxies for both B and C will be present in the root.
3474 EXPECT_EQ(
3475 " Site A ------------ proxies for B C\n"
3476 " |--Site B ------- proxies for A C\n"
3477 " | +--Site C -- proxies for A B\n"
3478 " +--Site A ------- proxies for B C\n"
3479 "Where A = http://a.com/\n"
3480 " B = http://bar.com/\n"
3481 " C = http://baz.com/",
3482 DepictFrameTree(root));
creisceaabfd52015-05-27 20:26:463483
3484 EXPECT_GT(site_instance_c->active_frame_count(), 0U);
3485
nick44bacf32015-04-14 02:06:393486 // Kill process B.
3487 RenderProcessHost* child_process_b =
3488 root->child_at(0)->current_frame_host()->GetProcess();
3489 RenderProcessHostWatcher crash_observer(
3490 child_process_b, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:453491 child_process_b->Shutdown(0);
nick44bacf32015-04-14 02:06:393492 crash_observer.Wait();
lazyboyf2852202014-12-19 05:31:523493
nick44bacf32015-04-14 02:06:393494 // Make sure proxy C has gone from root.
3495 // Make sure proxy C has gone from node3 as well.
3496 // Make sure proxy B stays around in root and node3.
3497 EXPECT_EQ(
3498 " Site A ------------ proxies for B\n"
3499 " |--Site B ------- proxies for A\n"
3500 " +--Site A ------- proxies for B\n"
3501 "Where A = http://a.com/\n"
3502 " B = http://bar.com/ (no process)",
3503 DepictFrameTree(root));
lazyboyf2852202014-12-19 05:31:523504
creisceaabfd52015-05-27 20:26:463505 EXPECT_EQ(0U, site_instance_c->active_frame_count());
lazyboyf2852202014-12-19 05:31:523506}
3507
[email protected]81c6c5e2014-02-13 20:20:073508// Crash a subframe and ensures its children are cleared from the FrameTree.
3509// See http://crbug.com/338508.
nasko4feae972015-07-01 16:38:023510IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CrashSubframe) {
nick4e68f5252015-08-28 20:17:053511 GURL main_url(embedded_test_server()->GetURL(
3512 "a.com", "/cross_site_iframe_factory.html?a(b)"));
davidsac6e6c35e42016-11-21 19:45:573513 EXPECT_TRUE(NavigateToURL(shell(), main_url));
[email protected]81c6c5e2014-02-13 20:20:073514
[email protected]81c6c5e2014-02-13 20:20:073515 // Check the subframe process.
ekaramadfd1b5cfa2016-04-19 00:35:003516 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
nick4e68f5252015-08-28 20:17:053517 EXPECT_EQ(
3518 " Site A ------------ proxies for B\n"
3519 " +--Site B ------- proxies for A\n"
3520 "Where A = http://a.com/\n"
3521 " B = http://b.com/",
3522 DepictFrameTree(root));
[email protected]81c6c5e2014-02-13 20:20:073523 FrameTreeNode* child = root->child_at(0);
creise42f2a52014-09-18 18:14:573524 EXPECT_TRUE(
3525 child->current_frame_host()->render_view_host()->IsRenderViewLive());
3526 EXPECT_TRUE(child->current_frame_host()->IsRenderFrameLive());
3527
[email protected]81c6c5e2014-02-13 20:20:073528 // Crash the subframe process.
3529 RenderProcessHost* root_process = root->current_frame_host()->GetProcess();
3530 RenderProcessHost* child_process = child->current_frame_host()->GetProcess();
3531 {
3532 RenderProcessHostWatcher crash_observer(
Ria Jiangabad8d9a2018-01-24 16:52:363533 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:453534 child_process->Shutdown(0);
[email protected]81c6c5e2014-02-13 20:20:073535 crash_observer.Wait();
3536 }
3537
3538 // Ensure that the child frame still exists but has been cleared.
nick4e68f5252015-08-28 20:17:053539 EXPECT_EQ(
3540 " Site A ------------ proxies for B\n"
3541 " +--Site B ------- proxies for A\n"
3542 "Where A = http://a.com/\n"
3543 " B = http://b.com/ (no process)",
3544 DepictFrameTree(root));
3545 EXPECT_EQ(1U, root->child_count());
[email protected]58faf942014-02-20 21:03:583546 EXPECT_EQ(main_url, root->current_url());
3547 EXPECT_EQ(GURL(), child->current_url());
[email protected]81c6c5e2014-02-13 20:20:073548
creise42f2a52014-09-18 18:14:573549 EXPECT_FALSE(
3550 child->current_frame_host()->render_view_host()->IsRenderViewLive());
3551 EXPECT_FALSE(child->current_frame_host()->IsRenderFrameLive());
3552 EXPECT_FALSE(child->current_frame_host()->render_frame_created_);
3553
[email protected]81c6c5e2014-02-13 20:20:073554 // Now crash the top-level page to clear the child frame.
3555 {
3556 RenderProcessHostWatcher crash_observer(
Ria Jiangabad8d9a2018-01-24 16:52:363557 root_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:453558 root_process->Shutdown(0);
[email protected]81c6c5e2014-02-13 20:20:073559 crash_observer.Wait();
3560 }
3561 EXPECT_EQ(0U, root->child_count());
[email protected]58faf942014-02-20 21:03:583562 EXPECT_EQ(GURL(), root->current_url());
[email protected]81c6c5e2014-02-13 20:20:073563}
3564
alexmos46e85ec2015-04-03 21:04:353565// When a new subframe is added, related SiteInstances that can reach the
3566// subframe should create proxies for it (https://crbug.com/423587). This test
3567// checks that if A embeds B and later adds a new subframe A2, A2 gets a proxy
3568// in B's process.
3569IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CreateProxiesForNewFrames) {
nick44bacf32015-04-14 02:06:393570 GURL main_url(embedded_test_server()->GetURL(
3571 "b.com", "/frame_tree/page_with_one_frame.html"));
alexmos46e85ec2015-04-03 21:04:353572 EXPECT_TRUE(NavigateToURL(shell(), main_url));
3573
3574 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:003575 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmos46e85ec2015-04-03 21:04:353576 ASSERT_EQ(1U, root->child_count());
3577
3578 // Make sure the frame starts out at the correct cross-site URL.
3579 EXPECT_EQ(embedded_test_server()->GetURL("baz.com", "/title1.html"),
3580 root->child_at(0)->current_url());
3581
nick44bacf32015-04-14 02:06:393582 EXPECT_EQ(
3583 " Site A ------------ proxies for B\n"
3584 " +--Site B ------- proxies for A\n"
3585 "Where A = http://b.com/\n"
3586 " B = http://baz.com/",
3587 DepictFrameTree(root));
3588
alexmos46e85ec2015-04-03 21:04:353589 // Add a new child frame to the top-level frame.
3590 RenderFrameHostCreatedObserver frame_observer(shell()->web_contents(), 1);
nickadef4a52016-06-09 18:45:543591 EXPECT_TRUE(ExecuteScript(shell(), "addFrame('data:text/html,foo');"));
alexmos46e85ec2015-04-03 21:04:353592 frame_observer.Wait();
alexmos46e85ec2015-04-03 21:04:353593
nick44bacf32015-04-14 02:06:393594 // The new frame should have a proxy in Site B, for use by the old frame.
3595 EXPECT_EQ(
3596 " Site A ------------ proxies for B\n"
3597 " |--Site B ------- proxies for A\n"
3598 " +--Site A ------- proxies for B\n"
3599 "Where A = http://b.com/\n"
3600 " B = http://baz.com/",
3601 DepictFrameTree(root));
alexmos46e85ec2015-04-03 21:04:353602}
3603
[email protected]0f7d449e2013-01-23 15:12:353604// TODO(nasko): Disable this test until out-of-process iframes is ready and the
3605// security checks are back in place.
3606IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
3607 DISABLED_CrossSiteIframeRedirectOnce) {
svaldezc3a9a172015-11-03 22:01:333608 net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
Alan Cutterfb5689192019-03-28 08:39:193609 https_server.ServeFilesFromSourceDirectory(GetTestDataFilePath());
[email protected]bbdd1b20b2012-12-11 21:24:133610 ASSERT_TRUE(https_server.Start());
3611
svaldezc3a9a172015-11-03 22:01:333612 GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
3613 GURL http_url(embedded_test_server()->GetURL("/title1.html"));
3614 GURL https_url(https_server.GetURL("/title1.html"));
[email protected]bbdd1b20b2012-12-11 21:24:133615
davidsac6e6c35e42016-11-21 19:45:573616 EXPECT_TRUE(NavigateToURL(shell(), main_url));
[email protected]bbdd1b20b2012-12-11 21:24:133617
clamyf1ccb4d2015-01-28 17:40:383618 TestNavigationObserver observer(shell()->web_contents());
[email protected]bbdd1b20b2012-12-11 21:24:133619 {
3620 // Load cross-site client-redirect page into Iframe.
3621 // Should be blocked.
svaldezc3a9a172015-11-03 22:01:333622 GURL client_redirect_https_url(
3623 https_server.GetURL("/client-redirect?/title1.html"));
alexmos75648bb32015-01-07 21:06:283624 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
3625 client_redirect_https_url));
[email protected]bbdd1b20b2012-12-11 21:24:133626 // DidFailProvisionalLoad when navigating to client_redirect_https_url.
clamyf1ccb4d2015-01-28 17:40:383627 EXPECT_EQ(observer.last_navigation_url(), client_redirect_https_url);
3628 EXPECT_FALSE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:133629 }
3630
3631 {
3632 // Load cross-site server-redirect page into Iframe,
3633 // which redirects to same-site page.
svaldezc3a9a172015-11-03 22:01:333634 GURL server_redirect_http_url(
3635 https_server.GetURL("/server-redirect?" + http_url.spec()));
alexmos75648bb32015-01-07 21:06:283636 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
3637 server_redirect_http_url));
clamyf1ccb4d2015-01-28 17:40:383638 EXPECT_EQ(observer.last_navigation_url(), http_url);
3639 EXPECT_TRUE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:133640 }
3641
3642 {
3643 // Load cross-site server-redirect page into Iframe,
3644 // which redirects to cross-site page.
svaldezc3a9a172015-11-03 22:01:333645 GURL server_redirect_http_url(
3646 https_server.GetURL("/server-redirect?/title1.html"));
alexmos75648bb32015-01-07 21:06:283647 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
3648 server_redirect_http_url));
[email protected]bbdd1b20b2012-12-11 21:24:133649 // DidFailProvisionalLoad when navigating to https_url.
clamyf1ccb4d2015-01-28 17:40:383650 EXPECT_EQ(observer.last_navigation_url(), https_url);
3651 EXPECT_FALSE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:133652 }
3653
3654 {
3655 // Load same-site server-redirect page into Iframe,
3656 // which redirects to cross-site page.
svaldezc3a9a172015-11-03 22:01:333657 GURL server_redirect_http_url(
3658 embedded_test_server()->GetURL("/server-redirect?" + https_url.spec()));
alexmos75648bb32015-01-07 21:06:283659 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
3660 server_redirect_http_url));
[email protected]bbdd1b20b2012-12-11 21:24:133661
clamyf1ccb4d2015-01-28 17:40:383662 EXPECT_EQ(observer.last_navigation_url(), https_url);
3663 EXPECT_FALSE(observer.last_navigation_succeeded());
Lukasz Anforowicz9e0ce4e2017-09-28 19:09:153664 }
[email protected]bbdd1b20b2012-12-11 21:24:133665
3666 {
3667 // Load same-site client-redirect page into Iframe,
3668 // which redirects to cross-site page.
svaldezc3a9a172015-11-03 22:01:333669 GURL client_redirect_http_url(
3670 embedded_test_server()->GetURL("/client-redirect?" + https_url.spec()));
[email protected]bbdd1b20b2012-12-11 21:24:133671
3672 RedirectNotificationObserver load_observer2(
Ria Jiangabad8d9a2018-01-24 16:52:363673 NOTIFICATION_LOAD_STOP, Source<NavigationController>(
3674 &shell()->web_contents()->GetController()));
[email protected]bbdd1b20b2012-12-11 21:24:133675
alexmos75648bb32015-01-07 21:06:283676 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
3677 client_redirect_http_url));
[email protected]bbdd1b20b2012-12-11 21:24:133678
3679 // Same-site Client-Redirect Page should be loaded successfully.
clamyf1ccb4d2015-01-28 17:40:383680 EXPECT_EQ(observer.last_navigation_url(), client_redirect_http_url);
3681 EXPECT_TRUE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:133682
3683 // Redirecting to Cross-site Page should be blocked.
3684 load_observer2.Wait();
clamyf1ccb4d2015-01-28 17:40:383685 EXPECT_EQ(observer.last_navigation_url(), https_url);
3686 EXPECT_FALSE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:133687 }
3688
3689 {
3690 // Load same-site server-redirect page into Iframe,
3691 // which redirects to same-site page.
svaldezc3a9a172015-11-03 22:01:333692 GURL server_redirect_http_url(
3693 embedded_test_server()->GetURL("/server-redirect?/title1.html"));
alexmos75648bb32015-01-07 21:06:283694 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
3695 server_redirect_http_url));
clamyf1ccb4d2015-01-28 17:40:383696 EXPECT_EQ(observer.last_navigation_url(), http_url);
3697 EXPECT_TRUE(observer.last_navigation_succeeded());
Lukasz Anforowicz9e0ce4e2017-09-28 19:09:153698 }
[email protected]bbdd1b20b2012-12-11 21:24:133699
3700 {
3701 // Load same-site client-redirect page into Iframe,
3702 // which redirects to same-site page.
svaldezc3a9a172015-11-03 22:01:333703 GURL client_redirect_http_url(
3704 embedded_test_server()->GetURL("/client-redirect?" + http_url.spec()));
[email protected]bbdd1b20b2012-12-11 21:24:133705 RedirectNotificationObserver load_observer2(
Ria Jiangabad8d9a2018-01-24 16:52:363706 NOTIFICATION_LOAD_STOP, Source<NavigationController>(
3707 &shell()->web_contents()->GetController()));
[email protected]bbdd1b20b2012-12-11 21:24:133708
alexmos75648bb32015-01-07 21:06:283709 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
3710 client_redirect_http_url));
[email protected]bbdd1b20b2012-12-11 21:24:133711
3712 // Same-site Client-Redirect Page should be loaded successfully.
clamyf1ccb4d2015-01-28 17:40:383713 EXPECT_EQ(observer.last_navigation_url(), client_redirect_http_url);
3714 EXPECT_TRUE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:133715
3716 // Redirecting to Same-site Page should be loaded successfully.
3717 load_observer2.Wait();
clamyf1ccb4d2015-01-28 17:40:383718 EXPECT_EQ(observer.last_navigation_url(), http_url);
3719 EXPECT_TRUE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:133720 }
3721}
3722
[email protected]0f7d449e2013-01-23 15:12:353723// TODO(nasko): Disable this test until out-of-process iframes is ready and the
3724// security checks are back in place.
[email protected]bbdd1b20b2012-12-11 21:24:133725IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
[email protected]0f7d449e2013-01-23 15:12:353726 DISABLED_CrossSiteIframeRedirectTwice) {
svaldezc3a9a172015-11-03 22:01:333727 net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
Alan Cutterfb5689192019-03-28 08:39:193728 https_server.ServeFilesFromSourceDirectory(GetTestDataFilePath());
[email protected]bbdd1b20b2012-12-11 21:24:133729 ASSERT_TRUE(https_server.Start());
3730
svaldezc3a9a172015-11-03 22:01:333731 GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
3732 GURL http_url(embedded_test_server()->GetURL("/title1.html"));
3733 GURL https_url(https_server.GetURL("/title1.html"));
[email protected]bbdd1b20b2012-12-11 21:24:133734
davidsac6e6c35e42016-11-21 19:45:573735 EXPECT_TRUE(NavigateToURL(shell(), main_url));
[email protected]bbdd1b20b2012-12-11 21:24:133736
clamyf1ccb4d2015-01-28 17:40:383737 TestNavigationObserver observer(shell()->web_contents());
[email protected]bbdd1b20b2012-12-11 21:24:133738 {
3739 // Load client-redirect page pointing to a cross-site client-redirect page,
3740 // which eventually redirects back to same-site page.
svaldezc3a9a172015-11-03 22:01:333741 GURL client_redirect_https_url(
3742 https_server.GetURL("/client-redirect?" + http_url.spec()));
3743 GURL client_redirect_http_url(embedded_test_server()->GetURL(
3744 "/client-redirect?" + client_redirect_https_url.spec()));
[email protected]bbdd1b20b2012-12-11 21:24:133745
3746 // We should wait until second client redirect get cancelled.
3747 RedirectNotificationObserver load_observer2(
Ria Jiangabad8d9a2018-01-24 16:52:363748 NOTIFICATION_LOAD_STOP, Source<NavigationController>(
3749 &shell()->web_contents()->GetController()));
[email protected]bbdd1b20b2012-12-11 21:24:133750
alexmos75648bb32015-01-07 21:06:283751 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
3752 client_redirect_http_url));
[email protected]bbdd1b20b2012-12-11 21:24:133753
3754 // DidFailProvisionalLoad when navigating to client_redirect_https_url.
3755 load_observer2.Wait();
clamyf1ccb4d2015-01-28 17:40:383756 EXPECT_EQ(observer.last_navigation_url(), client_redirect_https_url);
3757 EXPECT_FALSE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:133758 }
3759
3760 {
3761 // Load server-redirect page pointing to a cross-site server-redirect page,
3762 // which eventually redirect back to same-site page.
svaldezc3a9a172015-11-03 22:01:333763 GURL server_redirect_https_url(
3764 https_server.GetURL("/server-redirect?" + http_url.spec()));
3765 GURL server_redirect_http_url(embedded_test_server()->GetURL(
3766 "/server-redirect?" + server_redirect_https_url.spec()));
alexmos75648bb32015-01-07 21:06:283767 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
3768 server_redirect_http_url));
clamyf1ccb4d2015-01-28 17:40:383769 EXPECT_EQ(observer.last_navigation_url(), http_url);
3770 EXPECT_TRUE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:133771 }
3772
3773 {
3774 // Load server-redirect page pointing to a cross-site server-redirect page,
3775 // which eventually redirects back to cross-site page.
svaldezc3a9a172015-11-03 22:01:333776 GURL server_redirect_https_url(
3777 https_server.GetURL("/server-redirect?" + https_url.spec()));
3778 GURL server_redirect_http_url(embedded_test_server()->GetURL(
3779 "/server-redirect?" + server_redirect_https_url.spec()));
alexmos75648bb32015-01-07 21:06:283780 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
3781 server_redirect_http_url));
[email protected]bbdd1b20b2012-12-11 21:24:133782
3783 // DidFailProvisionalLoad when navigating to https_url.
clamyf1ccb4d2015-01-28 17:40:383784 EXPECT_EQ(observer.last_navigation_url(), https_url);
3785 EXPECT_FALSE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:133786 }
3787
3788 {
3789 // Load server-redirect page pointing to a cross-site client-redirect page,
3790 // which eventually redirects back to same-site page.
svaldezc3a9a172015-11-03 22:01:333791 GURL client_redirect_http_url(
3792 https_server.GetURL("/client-redirect?" + http_url.spec()));
3793 GURL server_redirect_http_url(embedded_test_server()->GetURL(
3794 "/server-redirect?" + client_redirect_http_url.spec()));
alexmos75648bb32015-01-07 21:06:283795 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "test",
3796 server_redirect_http_url));
[email protected]bbdd1b20b2012-12-11 21:24:133797
3798 // DidFailProvisionalLoad when navigating to client_redirect_http_url.
clamyf1ccb4d2015-01-28 17:40:383799 EXPECT_EQ(observer.last_navigation_url(), client_redirect_http_url);
3800 EXPECT_FALSE(observer.last_navigation_succeeded());
[email protected]bbdd1b20b2012-12-11 21:24:133801 }
3802}
3803
naskoe6edde32014-10-17 15:36:483804// Ensure that when navigating a frame cross-process RenderFrameProxyHosts are
Alex Moshchuk27caae82017-09-11 23:11:183805// created in the FrameTree skipping the subtree of the navigating frame (but
3806// not the navigating frame itself).
Ria Jiangabad8d9a2018-01-24 16:52:363807IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, ProxyCreationSkipsSubtree) {
nick4e68f5252015-08-28 20:17:053808 GURL main_url(embedded_test_server()->GetURL(
3809 "a.com", "/cross_site_iframe_factory.html?a(a,a(a,a(a)))"));
davidsac6e6c35e42016-11-21 19:45:573810 EXPECT_TRUE(NavigateToURL(shell(), main_url));
naskoe6edde32014-10-17 15:36:483811
3812 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:003813 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
naskoe6edde32014-10-17 15:36:483814
Ivan Kotenkov2c0d2bb32017-11-01 15:41:283815 EXPECT_TRUE(root->child_at(1) != nullptr);
naskoe6edde32014-10-17 15:36:483816 EXPECT_EQ(2U, root->child_at(1)->child_count());
3817
3818 {
3819 // Load same-site page into iframe.
clamyf1ccb4d2015-01-28 17:40:383820 TestNavigationObserver observer(shell()->web_contents());
nick4e68f5252015-08-28 20:17:053821 GURL http_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
naskoe6edde32014-10-17 15:36:483822 NavigateFrameToURL(root->child_at(0), http_url);
clamyf1ccb4d2015-01-28 17:40:383823 EXPECT_EQ(http_url, observer.last_navigation_url());
3824 EXPECT_TRUE(observer.last_navigation_succeeded());
nick44bacf32015-04-14 02:06:393825 EXPECT_EQ(
3826 " Site A\n"
3827 " |--Site A\n"
3828 " +--Site A\n"
3829 " |--Site A\n"
3830 " +--Site A\n"
3831 " +--Site A\n"
nick4e68f5252015-08-28 20:17:053832 "Where A = http://a.com/",
nick44bacf32015-04-14 02:06:393833 DepictFrameTree(root));
naskoe6edde32014-10-17 15:36:483834 }
3835
3836 // Create the cross-site URL to navigate to.
nasko30374c72014-10-30 19:18:373837 GURL cross_site_url =
alexmos617df0372015-09-03 21:52:163838 embedded_test_server()->GetURL("foo.com", "/frame_tree/title2.html");
naskoe6edde32014-10-17 15:36:483839
3840 // Load cross-site page into the second iframe without waiting for the
3841 // navigation to complete. Once LoadURLWithParams returns, we would expect
3842 // proxies to have been created in the frame tree, but children of the
3843 // navigating frame to still be present. The reason is that we don't run the
3844 // message loop, so no IPCs that alter the frame tree can be processed.
3845 FrameTreeNode* child = root->child_at(1);
Ivan Kotenkov2c0d2bb32017-11-01 15:41:283846 SiteInstance* site = nullptr;
clamy610c63b32017-12-22 15:05:183847 std::string cross_site_rfh_type = "speculative";
naskoe6edde32014-10-17 15:36:483848 {
clamyf1ccb4d2015-01-28 17:40:383849 TestNavigationObserver observer(shell()->web_contents());
naskoe6edde32014-10-17 15:36:483850 TestFrameNavigationObserver navigation_observer(child);
3851 NavigationController::LoadURLParams params(cross_site_url);
3852 params.transition_type = PageTransitionFromInt(ui::PAGE_TRANSITION_LINK);
3853 params.frame_tree_node_id = child->frame_tree_node_id();
3854 child->navigator()->GetController()->LoadURLWithParams(params);
naskoe6edde32014-10-17 15:36:483855
clamy610c63b32017-12-22 15:05:183856 site = child->render_manager()->speculative_frame_host()->GetSiteInstance();
naskoe6edde32014-10-17 15:36:483857 EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site);
3858
clamya5298012015-06-02 13:28:213859 std::string tree = base::StringPrintf(
nick44bacf32015-04-14 02:06:393860 " Site A ------------ proxies for B\n"
3861 " |--Site A ------- proxies for B\n"
Alex Moshchuk27caae82017-09-11 23:11:183862 " +--Site A (B %s) -- proxies for B\n"
nick44bacf32015-04-14 02:06:393863 " |--Site A\n"
3864 " +--Site A\n"
3865 " +--Site A\n"
nick4e68f5252015-08-28 20:17:053866 "Where A = http://a.com/\n"
nick44bacf32015-04-14 02:06:393867 " B = http://foo.com/",
clamya5298012015-06-02 13:28:213868 cross_site_rfh_type.c_str());
3869 EXPECT_EQ(tree, DepictFrameTree(root));
nick44bacf32015-04-14 02:06:393870
naskoe6edde32014-10-17 15:36:483871 // Now that the verification is done, run the message loop and wait for the
3872 // navigation to complete.
3873 navigation_observer.Wait();
clamyf1ccb4d2015-01-28 17:40:383874 EXPECT_TRUE(observer.last_navigation_succeeded());
3875 EXPECT_EQ(cross_site_url, observer.last_navigation_url());
nick53d5cbf2015-04-23 22:50:143876
3877 EXPECT_EQ(
3878 " Site A ------------ proxies for B\n"
3879 " |--Site A ------- proxies for B\n"
3880 " +--Site B ------- proxies for A\n"
nick4e68f5252015-08-28 20:17:053881 "Where A = http://a.com/\n"
nick53d5cbf2015-04-23 22:50:143882 " B = http://foo.com/",
3883 DepictFrameTree(root));
naskoe6edde32014-10-17 15:36:483884 }
3885
3886 // Load another cross-site page into the same iframe.
alexmos617df0372015-09-03 21:52:163887 cross_site_url = embedded_test_server()->GetURL("bar.com", "/title3.html");
naskoe6edde32014-10-17 15:36:483888 {
3889 // Perform the same checks as the first cross-site navigation, since
3890 // there have been issues in subsequent cross-site navigations. Also ensure
3891 // that the SiteInstance has properly changed.
3892 // TODO(nasko): Once we have proper cleanup of resources, add code to
3893 // verify that the intermediate SiteInstance/RenderFrameHost have been
3894 // properly cleaned up.
clamyf1ccb4d2015-01-28 17:40:383895 TestNavigationObserver observer(shell()->web_contents());
naskoe6edde32014-10-17 15:36:483896 TestFrameNavigationObserver navigation_observer(child);
3897 NavigationController::LoadURLParams params(cross_site_url);
3898 params.transition_type = PageTransitionFromInt(ui::PAGE_TRANSITION_LINK);
3899 params.frame_tree_node_id = child->frame_tree_node_id();
3900 child->navigator()->GetController()->LoadURLWithParams(params);
naskoe6edde32014-10-17 15:36:483901
clamy610c63b32017-12-22 15:05:183902 SiteInstance* site2 =
3903 child->render_manager()->speculative_frame_host()->GetSiteInstance();
naskoe6edde32014-10-17 15:36:483904 EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site2);
3905 EXPECT_NE(site, site2);
3906
clamya5298012015-06-02 13:28:213907 std::string tree = base::StringPrintf(
nick44bacf32015-04-14 02:06:393908 " Site A ------------ proxies for B C\n"
3909 " |--Site A ------- proxies for B C\n"
Alex Moshchuk27caae82017-09-11 23:11:183910 " +--Site B (C %s) -- proxies for A C\n"
nick4e68f5252015-08-28 20:17:053911 "Where A = http://a.com/\n"
nick44bacf32015-04-14 02:06:393912 " B = http://foo.com/\n"
3913 " C = http://bar.com/",
clamya5298012015-06-02 13:28:213914 cross_site_rfh_type.c_str());
3915 EXPECT_EQ(tree, DepictFrameTree(root));
naskoe6edde32014-10-17 15:36:483916
3917 navigation_observer.Wait();
clamyf1ccb4d2015-01-28 17:40:383918 EXPECT_TRUE(observer.last_navigation_succeeded());
3919 EXPECT_EQ(cross_site_url, observer.last_navigation_url());
naskoe6edde32014-10-17 15:36:483920 EXPECT_EQ(0U, child->child_count());
3921 }
3922}
3923
lazyboy70605c32015-11-03 01:27:313924// Verify that "scrolling" property on frame elements propagates to child frames
3925// correctly.
3926// Does not work on android since android has scrollbars overlayed.
bokanc3cd4452016-11-09 04:11:153927// TODO(bokan): Pretty soon most/all platforms will use overlay scrollbars. This
3928// test should find a better way to check for scrollability. crbug.com/662196.
henrika42de1742017-12-01 09:33:193929// Flaky on Linux. crbug.com/790929.
Ken Buchanancacd0082018-01-22 20:38:123930#if defined(OS_ANDROID) || defined(OS_LINUX)
lazyboy70605c32015-11-03 01:27:313931#define MAYBE_FrameOwnerPropertiesPropagationScrolling \
Ria Jiangabad8d9a2018-01-24 16:52:363932 DISABLED_FrameOwnerPropertiesPropagationScrolling
lazyboy70605c32015-11-03 01:27:313933#else
3934#define MAYBE_FrameOwnerPropertiesPropagationScrolling \
Ria Jiangabad8d9a2018-01-24 16:52:363935 FrameOwnerPropertiesPropagationScrolling
lazyboy70605c32015-11-03 01:27:313936#endif
3937IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
3938 MAYBE_FrameOwnerPropertiesPropagationScrolling) {
ahest1d019b72016-02-02 07:24:423939#if defined(OS_MACOSX)
spqchan83534ab02016-02-23 19:28:273940 ui::test::ScopedPreferredScrollerStyle scroller_style_override(false);
ahest1d019b72016-02-02 07:24:423941#endif
lazyboy70605c32015-11-03 01:27:313942 GURL main_url(embedded_test_server()->GetURL(
3943 "a.com", "/frame_owner_properties_scrolling.html"));
davidsac6e6c35e42016-11-21 19:45:573944 EXPECT_TRUE(NavigateToURL(shell(), main_url));
lazyboy70605c32015-11-03 01:27:313945
3946 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:003947 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
lazyboy70605c32015-11-03 01:27:313948 ASSERT_EQ(1u, root->child_count());
3949
3950 EXPECT_EQ(
3951 " Site A ------------ proxies for B\n"
3952 " +--Site B ------- proxies for A\n"
3953 "Where A = http://a.com/\n"
3954 " B = http://b.com/",
3955 DepictFrameTree(root));
3956
3957 FrameTreeNode* child = root->child_at(0);
3958
3959 // If the available client width within the iframe is smaller than the
3960 // frame element's width, we assume there's a scrollbar.
3961 // Also note that just comparing clientHeight and scrollHeight of the frame's
3962 // document will not work.
3963 auto has_scrollbar = [](RenderFrameHostImpl* rfh) {
Nick Carterb7e71312018-08-03 23:36:133964 int client_width = EvalJs(rfh, "document.body.clientWidth").ExtractInt();
lazyboy70605c32015-11-03 01:27:313965 const int kFrameElementWidth = 200;
3966 return client_width < kFrameElementWidth;
3967 };
3968
3969 auto set_scrolling_property = [](RenderFrameHostImpl* parent_rfh,
3970 const std::string& value) {
Nick Carterb7e71312018-08-03 23:36:133971 EXPECT_TRUE(ExecJs(
lazyboy70605c32015-11-03 01:27:313972 parent_rfh,
Ria Jiangabad8d9a2018-01-24 16:52:363973 base::StringPrintf("document.getElementById('child-1').setAttribute("
3974 " 'scrolling', '%s');",
3975 value.c_str())));
lazyboy70605c32015-11-03 01:27:313976 };
3977
3978 // Run the test over variety of parent/child cases.
Ria Jiangabad8d9a2018-01-24 16:52:363979 GURL urls[] = {// Remote to remote.
3980 embedded_test_server()->GetURL("c.com", "/tall_page.html"),
3981 // Remote to local.
3982 embedded_test_server()->GetURL("a.com", "/tall_page.html"),
3983 // Local to remote.
3984 embedded_test_server()->GetURL("b.com", "/tall_page.html")};
3985 const std::string scrolling_values[] = {"yes", "auto", "no"};
lazyboy70605c32015-11-03 01:27:313986
Avi Drissman9d3ded92018-12-25 20:50:213987 for (size_t i = 0; i < base::size(scrolling_values); ++i) {
lazyboy70605c32015-11-03 01:27:313988 bool expect_scrollbar = scrolling_values[i] != "no";
3989 set_scrolling_property(root->current_frame_host(), scrolling_values[i]);
Avi Drissman9d3ded92018-12-25 20:50:213990 for (size_t j = 0; j < base::size(urls); ++j) {
lazyboy70605c32015-11-03 01:27:313991 NavigateFrameToURL(child, urls[j]);
lazyboy70605c32015-11-03 01:27:313992 EXPECT_EQ(expect_scrollbar, has_scrollbar(child->current_frame_host()));
3993 }
3994 }
3995}
3996
3997// Verify that "marginwidth" and "marginheight" properties on frame elements
3998// propagate to child frames correctly.
3999IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
4000 FrameOwnerPropertiesPropagationMargin) {
4001 GURL main_url(embedded_test_server()->GetURL(
4002 "a.com", "/frame_owner_properties_margin.html"));
davidsac6e6c35e42016-11-21 19:45:574003 EXPECT_TRUE(NavigateToURL(shell(), main_url));
lazyboy70605c32015-11-03 01:27:314004
4005 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:004006 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
lazyboy70605c32015-11-03 01:27:314007 ASSERT_EQ(1u, root->child_count());
4008
4009 EXPECT_EQ(
4010 " Site A ------------ proxies for B\n"
4011 " +--Site B ------- proxies for A\n"
4012 "Where A = http://a.com/\n"
4013 " B = http://b.com/",
4014 DepictFrameTree(root));
4015
4016 FrameTreeNode* child = root->child_at(0);
4017
Nick Carterb7e71312018-08-03 23:36:134018 EXPECT_EQ("10", EvalJs(child, "document.body.getAttribute('marginwidth');"));
4019 EXPECT_EQ("50", EvalJs(child, "document.body.getAttribute('marginheight');"));
lazyboy70605c32015-11-03 01:27:314020
4021 // Run the test over variety of parent/child cases.
Ria Jiangabad8d9a2018-01-24 16:52:364022 GURL urls[] = {// Remote to remote.
4023 embedded_test_server()->GetURL("c.com", "/title2.html"),
4024 // Remote to local.
4025 embedded_test_server()->GetURL("a.com", "/title1.html"),
4026 // Local to remote.
4027 embedded_test_server()->GetURL("b.com", "/title2.html")};
lazyboy70605c32015-11-03 01:27:314028
4029 int current_margin_width = 15;
4030 int current_margin_height = 25;
4031
4032 // Before each navigation, we change the marginwidth and marginheight
4033 // properties of the frame. We then check whether those properties are applied
4034 // correctly after the navigation has completed.
Avi Drissman9d3ded92018-12-25 20:50:214035 for (size_t i = 0; i < base::size(urls); ++i) {
lazyboy70605c32015-11-03 01:27:314036 // Change marginwidth and marginheight before navigating.
Nick Carterb7e71312018-08-03 23:36:134037 EXPECT_TRUE(ExecJs(
nickadef4a52016-06-09 18:45:544038 root,
Nick Carterb7e71312018-08-03 23:36:134039 base::StringPrintf("var child = document.getElementById('child-1');"
4040 "child.setAttribute('marginwidth', '%d');"
4041 "child.setAttribute('marginheight', '%d');",
4042 current_margin_width, current_margin_height)));
lazyboy70605c32015-11-03 01:27:314043
4044 NavigateFrameToURL(child, urls[i]);
lazyboy70605c32015-11-03 01:27:314045
Raul Tambre6c0c3f5b2019-02-04 17:44:174046 EXPECT_EQ(base::NumberToString(current_margin_width),
Nick Carterb7e71312018-08-03 23:36:134047 EvalJs(child, "document.body.getAttribute('marginwidth');"));
Raul Tambre6c0c3f5b2019-02-04 17:44:174048 EXPECT_EQ(base::NumberToString(current_margin_height),
Nick Carterb7e71312018-08-03 23:36:134049 EvalJs(child, "document.body.getAttribute('marginheight');"));
lazyboy70605c32015-11-03 01:27:314050
4051 current_margin_width += 5;
4052 current_margin_height += 10;
4053 }
4054}
4055
amalikae4100ce2016-10-06 07:27:584056// Verify that "csp" property on frame elements propagates to child frames
4057// correctly. See https://crbug.com/647588
4058IN_PROC_BROWSER_TEST_F(SitePerProcessEmbedderCSPEnforcementBrowserTest,
4059 FrameOwnerPropertiesPropagationCSP) {
4060 GURL main_url(embedded_test_server()->GetURL(
4061 "a.com", "/frame_owner_properties_csp.html"));
4062 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4063
4064 // It is safe to obtain the root frame tree node here, as it doesn't change.
4065 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
4066 ASSERT_EQ(1u, root->child_count());
4067
4068 EXPECT_EQ(
4069 " Site A ------------ proxies for B\n"
4070 " +--Site B ------- proxies for A\n"
4071 "Where A = http://a.com/\n"
4072 " B = http://b.com/",
4073 DepictFrameTree(root));
4074
4075 FrameTreeNode* child = root->child_at(0);
4076
Nick Carterb7e71312018-08-03 23:36:134077 EXPECT_EQ(
4078 "object-src \'none\'",
4079 EvalJs(root, "document.getElementById('child-1').getAttribute('csp');"));
amalikae4100ce2016-10-06 07:27:584080
4081 // Run the test over variety of parent/child cases.
4082 GURL urls[] = {// Remote to remote.
4083 embedded_test_server()->GetURL("c.com", "/title2.html"),
4084 // Remote to local.
4085 embedded_test_server()->GetURL("a.com", "/title1.html"),
4086 // Local to remote.
4087 embedded_test_server()->GetURL("b.com", "/title2.html")};
4088
4089 std::vector<std::string> csp_values = {"default-src a.com",
4090 "default-src b.com", "img-src c.com"};
4091
4092 // Before each navigation, we change the csp property of the frame.
4093 // We then check whether that property is applied
4094 // correctly after the navigation has completed.
Avi Drissman9d3ded92018-12-25 20:50:214095 for (size_t i = 0; i < base::size(urls); ++i) {
amalikae4100ce2016-10-06 07:27:584096 // Change csp before navigating.
4097 EXPECT_TRUE(ExecuteScript(
4098 root,
4099 base::StringPrintf("document.getElementById('child-1').setAttribute("
4100 " 'csp', '%s');",
4101 csp_values[i].c_str())));
4102
4103 NavigateFrameToURL(child, urls[i]);
4104 EXPECT_EQ(csp_values[i], child->frame_owner_properties().required_csp);
4105 // TODO(amalika): add checks that the CSP replication takes effect
4106 }
4107}
4108
nick9c79de22015-08-28 20:12:134109// Verify origin replication with an A-embed-B-embed-C-embed-A hierarchy.
dtapuskabbfc0472017-04-28 21:20:324110IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, OriginReplication) {
nick9c79de22015-08-28 20:12:134111 GURL main_url(embedded_test_server()->GetURL(
4112 "a.com", "/cross_site_iframe_factory.html?a(b(c(a),b), a)"));
alexmos35d7b932014-12-05 03:55:234113 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4114
4115 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:004116 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmos35d7b932014-12-05 03:55:234117
nick9c79de22015-08-28 20:12:134118 EXPECT_EQ(
4119 " Site A ------------ proxies for B C\n"
4120 " |--Site B ------- proxies for A C\n" // tiptop_child
4121 " | |--Site C -- proxies for A B\n" // middle_child
4122 " | | +--Site A -- proxies for B C\n" // lowest_child
4123 " | +--Site B -- proxies for A C\n"
4124 " +--Site A ------- proxies for B C\n"
4125 "Where A = http://a.com/\n"
4126 " B = http://b.com/\n"
4127 " C = http://c.com/",
4128 DepictFrameTree(root));
alexmos35d7b932014-12-05 03:55:234129
Nick Carterb7e71312018-08-03 23:36:134130 url::Origin a_origin =
4131 url::Origin::Create(embedded_test_server()->GetURL("a.com", "/"));
4132 url::Origin b_origin =
4133 url::Origin::Create(embedded_test_server()->GetURL("b.com", "/"));
4134 url::Origin c_origin =
4135 url::Origin::Create(embedded_test_server()->GetURL("c.com", "/"));
nick9c79de22015-08-28 20:12:134136 FrameTreeNode* tiptop_child = root->child_at(0);
4137 FrameTreeNode* middle_child = root->child_at(0)->child_at(0);
4138 FrameTreeNode* lowest_child = root->child_at(0)->child_at(0)->child_at(0);
alexmos35d7b932014-12-05 03:55:234139
nick9c79de22015-08-28 20:12:134140 // Check that b.com frame's location.ancestorOrigins contains the correct
alexmos17f643f2014-12-09 18:50:104141 // origin for the parent. The origin should have been replicated as part of
rockot067ca55f2016-09-30 22:00:154142 // the mojom::Renderer::CreateView message that created the parent's
4143 // RenderFrameProxy in b.com's process.
Nick Carterb7e71312018-08-03 23:36:134144 EXPECT_EQ(ListValueOf(a_origin),
4145 EvalJs(tiptop_child, "Array.from(location.ancestorOrigins);"));
alexmos17f643f2014-12-09 18:50:104146
nick9c79de22015-08-28 20:12:134147 // Check that c.com frame's location.ancestorOrigins contains the correct
alexmos17f643f2014-12-09 18:50:104148 // origin for its two ancestors. The topmost parent origin should be
rockot067ca55f2016-09-30 22:00:154149 // replicated as part of mojom::Renderer::CreateView, and the middle frame
rockot53be7caf2016-10-04 20:17:084150 // (b.com's) origin should be replicated as part of
4151 // mojom::Renderer::CreateFrameProxy sent for b.com's frame in c.com's
4152 // process.
Nick Carterb7e71312018-08-03 23:36:134153 EXPECT_EQ(ListValueOf(b_origin, a_origin),
4154 EvalJs(middle_child, "Array.from(location.ancestorOrigins);"));
nick9c79de22015-08-28 20:12:134155
4156 // Check that the nested a.com frame's location.ancestorOrigins contains the
4157 // correct origin for its three ancestors.
Nick Carterb7e71312018-08-03 23:36:134158 EXPECT_EQ(ListValueOf(c_origin, b_origin, a_origin),
4159 EvalJs(lowest_child, "Array.from(location.ancestorOrigins);"));
alexmos35d7b932014-12-05 03:55:234160}
4161
Becca Hughes60af7d42017-12-12 10:53:154162// Test that HasReceivedUserGesture and HasReceivedUserGestureBeforeNavigation
4163// are propagated correctly across origins.
4164IN_PROC_BROWSER_TEST_F(SitePerProcessAutoplayBrowserTest,
4165 PropagateUserGestureFlag) {
4166 GURL main_url(embedded_test_server()->GetURL(
4167 "example.com", "/media/autoplay/autoplay-enabled.html"));
4168 GURL foo_url(embedded_test_server()->GetURL(
4169 "foo.com", "/media/autoplay/autoplay-enabled.html"));
4170 GURL bar_url(embedded_test_server()->GetURL(
4171 "bar.com", "/media/autoplay/autoplay-enabled.html"));
4172 GURL secondary_url(embedded_test_server()->GetURL(
4173 "test.example.com", "/media/autoplay/autoplay-enabled.html"));
4174 GURL disabled_url(embedded_test_server()->GetURL(
4175 "test.example.com", "/media/autoplay/autoplay-disabled.html"));
4176
4177 // Load a page with an iframe that has autoplay.
Mounir Lamourif7246b92018-01-30 18:16:334178 OpenURLBlockUntilNavigationComplete(shell(), main_url);
Becca Hughes60af7d42017-12-12 10:53:154179 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
4180
4181 // Navigate the subframes to cross-origin pages.
4182 NavigateFrameAndWait(root->child_at(0), foo_url);
4183 NavigateFrameAndWait(root->child_at(0)->child_at(0), bar_url);
4184
4185 // Test that all frames can autoplay if there has been a gesture in the top
4186 // frame.
4187 EXPECT_TRUE(AutoplayAllowed(shell(), true));
4188 EXPECT_TRUE(AutoplayAllowed(root->child_at(0), false));
4189 EXPECT_TRUE(AutoplayAllowed(root->child_at(0)->child_at(0), false));
4190
4191 // Navigate to a new page on the same origin.
Mounir Lamourif7246b92018-01-30 18:16:334192 OpenURLBlockUntilNavigationComplete(shell(), secondary_url);
Becca Hughes60af7d42017-12-12 10:53:154193 root = web_contents()->GetFrameTree()->root();
4194
4195 // Navigate the subframes to cross-origin pages.
4196 NavigateFrameAndWait(root->child_at(0), foo_url);
4197 NavigateFrameAndWait(root->child_at(0)->child_at(0), bar_url);
4198
4199 // Test that all frames can autoplay because the gesture bit has been passed
4200 // through the navigation.
4201 EXPECT_TRUE(AutoplayAllowed(shell(), false));
4202 EXPECT_TRUE(AutoplayAllowed(root->child_at(0), false));
4203 EXPECT_TRUE(AutoplayAllowed(root->child_at(0)->child_at(0), false));
4204
4205 // Navigate to a page with autoplay disabled.
Mounir Lamourif7246b92018-01-30 18:16:334206 OpenURLBlockUntilNavigationComplete(shell(), disabled_url);
Becca Hughes60af7d42017-12-12 10:53:154207 NavigateFrameAndWait(root->child_at(0), foo_url);
4208
4209 // Test that autoplay is no longer allowed.
4210 EXPECT_TRUE(AutoplayAllowed(shell(), false));
4211 EXPECT_FALSE(AutoplayAllowed(root->child_at(0), false));
4212
4213 // Navigate to another origin and make sure autoplay is disabled.
Mounir Lamourif7246b92018-01-30 18:16:334214 OpenURLBlockUntilNavigationComplete(shell(), foo_url);
Becca Hughes60af7d42017-12-12 10:53:154215 NavigateFrameAndWait(root->child_at(0), bar_url);
4216 EXPECT_FALSE(AutoplayAllowed(shell(), false));
4217 EXPECT_FALSE(AutoplayAllowed(shell(), false));
4218}
4219
Patrick Noland4d4df3452018-02-26 19:25:564220IN_PROC_BROWSER_TEST_F(SitePerProcesScrollAnchorTest,
4221 RemoteToLocalScrollAnchorRestore) {
4222 GURL main_url(embedded_test_server()->GetURL(
4223 "a.com", "/page_with_samesite_iframe.html"));
4224 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4225
4226 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
4227 FrameTreeNode* child = root->child_at(0);
4228
4229 GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
4230 NavigateFrameToURL(child, frame_url);
4231
4232 EXPECT_NE(child->current_frame_host()->GetSiteInstance(),
4233 root->current_frame_host()->GetSiteInstance());
4234
4235 TestFrameNavigationObserver frame_observer2(child);
4236 EXPECT_TRUE(ExecuteScript(root, "window.history.back()"));
4237 frame_observer2.Wait();
4238
4239 EXPECT_EQ(child->current_frame_host()->GetSiteInstance(),
4240 root->current_frame_host()->GetSiteInstance());
4241}
4242
alexmosf832a2f2015-01-27 22:44:034243// Check that iframe sandbox flags are replicated correctly.
4244IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, SandboxFlagsReplication) {
4245 GURL main_url(embedded_test_server()->GetURL("/sandboxed_frames.html"));
Nick Carterb7e71312018-08-03 23:36:134246 const url::Origin main_origin = url::Origin::Create(main_url);
alexmosf832a2f2015-01-27 22:44:034247 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4248
4249 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:004250 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmosf832a2f2015-01-27 22:44:034251
clamyf1ccb4d2015-01-28 17:40:384252 TestNavigationObserver observer(shell()->web_contents());
alexmosf832a2f2015-01-27 22:44:034253
4254 // Navigate the second (sandboxed) subframe to a cross-site page with a
alexmos54dee8e2015-05-15 19:07:014255 // subframe.
alexmosf832a2f2015-01-27 22:44:034256 GURL foo_url(
4257 embedded_test_server()->GetURL("foo.com", "/frame_tree/1-1.html"));
4258 NavigateFrameToURL(root->child_at(1), foo_url);
alexmos54dee8e2015-05-15 19:07:014259 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
alexmosf832a2f2015-01-27 22:44:034260
clamyf1ccb4d2015-01-28 17:40:384261 // We can't use a TestNavigationObserver to verify the URL here,
alexmosf832a2f2015-01-27 22:44:034262 // since the frame has children that may have clobbered it in the observer.
4263 EXPECT_EQ(foo_url, root->child_at(1)->current_url());
4264
4265 // Load cross-site page into subframe's subframe.
4266 ASSERT_EQ(2U, root->child_at(1)->child_count());
4267 GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title1.html"));
4268 NavigateFrameToURL(root->child_at(1)->child_at(0), bar_url);
clamyf1ccb4d2015-01-28 17:40:384269 EXPECT_TRUE(observer.last_navigation_succeeded());
4270 EXPECT_EQ(bar_url, observer.last_navigation_url());
alexmosf832a2f2015-01-27 22:44:034271
4272 // Opening a popup in the sandboxed foo.com iframe should fail.
Nick Carterb7e71312018-08-03 23:36:134273 EXPECT_EQ(false, EvalJs(root->child_at(1),
4274 "!!window.open('data:text/html,dataurl');"));
alexmos6b294562015-03-05 19:24:104275 EXPECT_EQ(1u, Shell::windows().size());
alexmosf832a2f2015-01-27 22:44:034276
4277 // Opening a popup in a frame whose parent is sandboxed should also fail.
4278 // Here, bar.com frame's sandboxed parent frame is a remote frame in
4279 // bar.com's process.
Nick Carterb7e71312018-08-03 23:36:134280 EXPECT_EQ(false, EvalJs(root->child_at(1)->child_at(0),
4281 "!!window.open('data:text/html,dataurl');"));
alexmos6b294562015-03-05 19:24:104282 EXPECT_EQ(1u, Shell::windows().size());
alexmosf832a2f2015-01-27 22:44:034283
4284 // Same, but now try the case where bar.com frame's sandboxed parent is a
4285 // local frame in bar.com's process.
Nick Carterb7e71312018-08-03 23:36:134286 EXPECT_EQ(false, EvalJs(root->child_at(2)->child_at(0),
4287 "!!window.open('data:text/html,dataurl');"));
alexmos6b294562015-03-05 19:24:104288 EXPECT_EQ(1u, Shell::windows().size());
alexmosf832a2f2015-01-27 22:44:034289
4290 // Check that foo.com frame's location.ancestorOrigins contains the correct
4291 // origin for the parent, which should be unaffected by sandboxing.
Nick Carterb7e71312018-08-03 23:36:134292 EXPECT_EQ(ListValueOf(main_origin),
4293 EvalJs(root->child_at(1), "Array.from(location.ancestorOrigins);"));
alexmosf832a2f2015-01-27 22:44:034294
4295 // Now check location.ancestorOrigins for the bar.com frame. The middle frame
4296 // (foo.com's) origin should be unique, since that frame is sandboxed, and
4297 // the top frame should match |main_url|.
Nick Carterb7e71312018-08-03 23:36:134298 EXPECT_EQ(ListValueOf("null", main_origin),
4299 EvalJs(root->child_at(1)->child_at(0),
4300 "Array.from(location.ancestorOrigins);"));
alexmos6b294562015-03-05 19:24:104301}
4302
4303// Check that dynamic updates to iframe sandbox flags are propagated correctly.
4304IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, DynamicSandboxFlags) {
4305 GURL main_url(
4306 embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html"));
4307 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4308
4309 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:004310 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmos6b294562015-03-05 19:24:104311
4312 TestNavigationObserver observer(shell()->web_contents());
4313 ASSERT_EQ(2U, root->child_count());
4314
4315 // Make sure first frame starts out at the correct cross-site page.
4316 EXPECT_EQ(embedded_test_server()->GetURL("bar.com", "/title1.html"),
4317 root->child_at(0)->current_url());
4318
4319 // Navigate second frame to another cross-site page.
4320 GURL baz_url(embedded_test_server()->GetURL("baz.com", "/title1.html"));
4321 NavigateFrameToURL(root->child_at(1), baz_url);
4322 EXPECT_TRUE(observer.last_navigation_succeeded());
4323 EXPECT_EQ(baz_url, observer.last_navigation_url());
4324
4325 // Both frames should not be sandboxed to start with.
Blink Reformat1c4d759e2017-04-09 16:34:544326 EXPECT_EQ(blink::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:124327 root->child_at(0)->pending_frame_policy().sandbox_flags);
Blink Reformat1c4d759e2017-04-09 16:34:544328 EXPECT_EQ(blink::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:124329 root->child_at(0)->effective_frame_policy().sandbox_flags);
Blink Reformat1c4d759e2017-04-09 16:34:544330 EXPECT_EQ(blink::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:124331 root->child_at(1)->pending_frame_policy().sandbox_flags);
Blink Reformat1c4d759e2017-04-09 16:34:544332 EXPECT_EQ(blink::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:124333 root->child_at(1)->effective_frame_policy().sandbox_flags);
alexmos6b294562015-03-05 19:24:104334
4335 // Dynamically update sandbox flags for the first frame.
nickadef4a52016-06-09 18:45:544336 EXPECT_TRUE(ExecuteScript(
Ria Jiangabad8d9a2018-01-24 16:52:364337 shell(), "document.querySelector('iframe').sandbox='allow-scripts';"));
alexmos6b294562015-03-05 19:24:104338
4339 // Check that updated sandbox flags are propagated to browser process.
Ian Clellandcdc4f312017-10-13 22:24:124340 // The new flags should be reflected in pending_frame_policy().sandbox_flags,
4341 // while effective_frame_policy().sandbox_flags should still reflect the old
4342 // flags, because sandbox flag updates take place only after navigations.
4343 // "allow-scripts" resets both SandboxFlags::Scripts and
4344 // SandboxFlags::AutomaticFeatures bits per blink::parseSandboxPolicy().
dcheng5f60abb2015-05-28 01:39:364345 blink::WebSandboxFlags expected_flags =
Blink Reformat1c4d759e2017-04-09 16:34:544346 blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kScripts &
4347 ~blink::WebSandboxFlags::kAutomaticFeatures;
Ian Clellandcdc4f312017-10-13 22:24:124348 EXPECT_EQ(expected_flags,
4349 root->child_at(0)->pending_frame_policy().sandbox_flags);
Blink Reformat1c4d759e2017-04-09 16:34:544350 EXPECT_EQ(blink::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:124351 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmos6b294562015-03-05 19:24:104352
4353 // Navigate the first frame to a page on the same site. The new sandbox
avi98405c22015-05-21 20:47:064354 // flags should take effect.
alexmos6b294562015-03-05 19:24:104355 GURL bar_url(
4356 embedded_test_server()->GetURL("bar.com", "/frame_tree/2-4.html"));
4357 NavigateFrameToURL(root->child_at(0), bar_url);
avi98405c22015-05-21 20:47:064358 // (The new page has a subframe; wait for it to load as well.)
4359 ASSERT_TRUE(WaitForLoadStop(shell()->web_contents()));
alexmos6b294562015-03-05 19:24:104360 EXPECT_EQ(bar_url, root->child_at(0)->current_url());
4361 ASSERT_EQ(1U, root->child_at(0)->child_count());
4362
creis2069a0a2015-05-22 22:13:474363 EXPECT_EQ(
4364 " Site A ------------ proxies for B C\n"
4365 " |--Site B ------- proxies for A C\n"
4366 " | +--Site B -- proxies for A C\n"
4367 " +--Site C ------- proxies for A B\n"
4368 "Where A = http://127.0.0.1/\n"
4369 " B = http://bar.com/\n"
4370 " C = http://baz.com/",
4371 DepictFrameTree(root));
4372
alexmos6b294562015-03-05 19:24:104373 // Confirm that the browser process has updated the frame's current sandbox
4374 // flags.
Ian Clellandcdc4f312017-10-13 22:24:124375 EXPECT_EQ(expected_flags,
4376 root->child_at(0)->pending_frame_policy().sandbox_flags);
4377 EXPECT_EQ(expected_flags,
4378 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmos6b294562015-03-05 19:24:104379
4380 // Opening a popup in the now-sandboxed frame should fail.
Nick Carterb7e71312018-08-03 23:36:134381 EXPECT_EQ(false, EvalJs(root->child_at(0),
4382 "!!window.open('data:text/html,dataurl');"));
alexmos6b294562015-03-05 19:24:104383 EXPECT_EQ(1u, Shell::windows().size());
4384
4385 // Navigate the child of the now-sandboxed frame to a page on baz.com. The
4386 // child should inherit the latest sandbox flags from its parent frame, which
4387 // is currently a proxy in baz.com's renderer process. This checks that the
4388 // proxies of |root->child_at(0)| were also updated with the latest sandbox
4389 // flags.
4390 GURL baz_child_url(embedded_test_server()->GetURL("baz.com", "/title2.html"));
4391 NavigateFrameToURL(root->child_at(0)->child_at(0), baz_child_url);
4392 EXPECT_TRUE(observer.last_navigation_succeeded());
4393 EXPECT_EQ(baz_child_url, observer.last_navigation_url());
4394
creis2069a0a2015-05-22 22:13:474395 EXPECT_EQ(
4396 " Site A ------------ proxies for B C\n"
4397 " |--Site B ------- proxies for A C\n"
4398 " | +--Site C -- proxies for A B\n"
4399 " +--Site C ------- proxies for A B\n"
4400 "Where A = http://127.0.0.1/\n"
4401 " B = http://bar.com/\n"
4402 " C = http://baz.com/",
4403 DepictFrameTree(root));
4404
alexmos6b294562015-03-05 19:24:104405 // Opening a popup in the child of a sandboxed frame should fail.
Nick Carterb7e71312018-08-03 23:36:134406 EXPECT_EQ(false, EvalJs(root->child_at(0)->child_at(0),
4407 "!!window.open('data:text/html,dataurl');"));
alexmos6b294562015-03-05 19:24:104408 EXPECT_EQ(1u, Shell::windows().size());
alexmos6e940102016-01-19 22:47:254409
4410 // Child of a sandboxed frame should also be sandboxed on the browser side.
Ian Clellandcdc4f312017-10-13 22:24:124411 EXPECT_EQ(
4412 expected_flags,
4413 root->child_at(0)->child_at(0)->effective_frame_policy().sandbox_flags);
alexmos6b294562015-03-05 19:24:104414}
4415
4416// Check that dynamic updates to iframe sandbox flags are propagated correctly.
4417IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
4418 DynamicSandboxFlagsRemoteToLocal) {
4419 GURL main_url(
4420 embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html"));
4421 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4422
4423 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:004424 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmos6b294562015-03-05 19:24:104425
4426 TestNavigationObserver observer(shell()->web_contents());
4427 ASSERT_EQ(2U, root->child_count());
4428
4429 // Make sure the two frames starts out at correct URLs.
4430 EXPECT_EQ(embedded_test_server()->GetURL("bar.com", "/title1.html"),
4431 root->child_at(0)->current_url());
4432 EXPECT_EQ(embedded_test_server()->GetURL("/title1.html"),
4433 root->child_at(1)->current_url());
4434
4435 // Update the second frame's sandbox flags.
nickadef4a52016-06-09 18:45:544436 EXPECT_TRUE(ExecuteScript(
4437 shell(),
4438 "document.querySelectorAll('iframe')[1].sandbox='allow-scripts'"));
alexmos6b294562015-03-05 19:24:104439
4440 // Check that the current sandbox flags are updated but the effective
4441 // sandbox flags are not.
dcheng5f60abb2015-05-28 01:39:364442 blink::WebSandboxFlags expected_flags =
Blink Reformat1c4d759e2017-04-09 16:34:544443 blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kScripts &
4444 ~blink::WebSandboxFlags::kAutomaticFeatures;
Ian Clellandcdc4f312017-10-13 22:24:124445 EXPECT_EQ(expected_flags,
4446 root->child_at(1)->pending_frame_policy().sandbox_flags);
Blink Reformat1c4d759e2017-04-09 16:34:544447 EXPECT_EQ(blink::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:124448 root->child_at(1)->effective_frame_policy().sandbox_flags);
alexmos6b294562015-03-05 19:24:104449
4450 // Navigate the second subframe to a page on bar.com. This will trigger a
nasko8206fa12016-03-22 02:24:134451 // remote-to-local frame swap in bar.com's process.
alexmos6b294562015-03-05 19:24:104452 GURL bar_url(embedded_test_server()->GetURL(
4453 "bar.com", "/frame_tree/page_with_one_frame.html"));
4454 NavigateFrameToURL(root->child_at(1), bar_url);
alexmos6b294562015-03-05 19:24:104455 EXPECT_EQ(bar_url, root->child_at(1)->current_url());
4456 ASSERT_EQ(1U, root->child_at(1)->child_count());
4457
4458 // Confirm that the browser process has updated the current sandbox flags.
Ian Clellandcdc4f312017-10-13 22:24:124459 EXPECT_EQ(expected_flags,
4460 root->child_at(1)->pending_frame_policy().sandbox_flags);
4461 EXPECT_EQ(expected_flags,
4462 root->child_at(1)->effective_frame_policy().sandbox_flags);
alexmos6b294562015-03-05 19:24:104463
4464 // Opening a popup in the sandboxed second frame should fail.
Nick Carterb7e71312018-08-03 23:36:134465 EXPECT_EQ(false, EvalJs(root->child_at(1),
4466 "!!window.open('data:text/html,dataurl');"));
alexmos6b294562015-03-05 19:24:104467 EXPECT_EQ(1u, Shell::windows().size());
4468
4469 // Make sure that the child frame inherits the sandbox flags of its
4470 // now-sandboxed parent frame.
Nick Carterb7e71312018-08-03 23:36:134471 EXPECT_EQ(false, EvalJs(root->child_at(1)->child_at(0),
4472 "!!window.open('data:text/html,dataurl');"));
alexmos6b294562015-03-05 19:24:104473 EXPECT_EQ(1u, Shell::windows().size());
4474}
4475
4476// Check that dynamic updates to iframe sandbox flags are propagated correctly.
4477IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
4478 DynamicSandboxFlagsRendererInitiatedNavigation) {
4479 GURL main_url(
4480 embedded_test_server()->GetURL("/frame_tree/page_with_one_frame.html"));
4481 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4482
4483 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:004484 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmos6b294562015-03-05 19:24:104485
4486 TestNavigationObserver observer(shell()->web_contents());
4487 ASSERT_EQ(1U, root->child_count());
4488
4489 // Make sure the frame starts out at the correct cross-site page.
4490 EXPECT_EQ(embedded_test_server()->GetURL("baz.com", "/title1.html"),
4491 root->child_at(0)->current_url());
4492
4493 // The frame should not be sandboxed to start with.
Blink Reformat1c4d759e2017-04-09 16:34:544494 EXPECT_EQ(blink::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:124495 root->child_at(0)->pending_frame_policy().sandbox_flags);
Blink Reformat1c4d759e2017-04-09 16:34:544496 EXPECT_EQ(blink::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:124497 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmos6b294562015-03-05 19:24:104498
4499 // Dynamically update the frame's sandbox flags.
nickadef4a52016-06-09 18:45:544500 EXPECT_TRUE(ExecuteScript(
4501 shell(), "document.querySelector('iframe').sandbox='allow-scripts';"));
alexmos6b294562015-03-05 19:24:104502
4503 // Check that updated sandbox flags are propagated to browser process.
Ian Clellandcdc4f312017-10-13 22:24:124504 // The new flags should be set in pending_frame_policy().sandbox_flags, while
4505 // effective_frame_policy().sandbox_flags should still reflect the old flags,
4506 // because sandbox flag updates take place only after navigations.
4507 // "allow-scripts" resets both SandboxFlags::Scripts and
4508 // SandboxFlags::AutomaticFeatures bits per blink::parseSandboxPolicy().
dcheng5f60abb2015-05-28 01:39:364509 blink::WebSandboxFlags expected_flags =
Blink Reformat1c4d759e2017-04-09 16:34:544510 blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kScripts &
4511 ~blink::WebSandboxFlags::kAutomaticFeatures;
Ian Clellandcdc4f312017-10-13 22:24:124512 EXPECT_EQ(expected_flags,
4513 root->child_at(0)->pending_frame_policy().sandbox_flags);
Blink Reformat1c4d759e2017-04-09 16:34:544514 EXPECT_EQ(blink::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:124515 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmos6b294562015-03-05 19:24:104516
4517 // Perform a renderer-initiated same-site navigation in the first frame. The
4518 // new sandbox flags should take effect.
4519 TestFrameNavigationObserver frame_observer(root->child_at(0));
nickadef4a52016-06-09 18:45:544520 ASSERT_TRUE(
4521 ExecuteScript(root->child_at(0), "window.location.href='/title2.html'"));
alexmos6b294562015-03-05 19:24:104522 frame_observer.Wait();
4523 EXPECT_EQ(embedded_test_server()->GetURL("baz.com", "/title2.html"),
4524 root->child_at(0)->current_url());
4525
4526 // Confirm that the browser process has updated the frame's current sandbox
4527 // flags.
Ian Clellandcdc4f312017-10-13 22:24:124528 EXPECT_EQ(expected_flags,
4529 root->child_at(0)->pending_frame_policy().sandbox_flags);
4530 EXPECT_EQ(expected_flags,
4531 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmos6b294562015-03-05 19:24:104532
4533 // Opening a popup in the now-sandboxed frame should fail.
Nick Carterb7e71312018-08-03 23:36:134534 EXPECT_EQ(false, EvalJs(root->child_at(0),
4535 "!!window.open('data:text/html,dataurl');"));
alexmos6b294562015-03-05 19:24:104536 EXPECT_EQ(1u, Shell::windows().size());
alexmosf832a2f2015-01-27 22:44:034537}
4538
alexmos6e0ee0c2015-05-01 18:57:344539// Verify that when a new child frame is added, the proxies created for it in
4540// other SiteInstances have correct sandbox flags and origin.
4541//
4542// A A A
4543// / / \ / \ .
4544// B -> B A -> B A
4545// \ .
4546// B
4547//
4548// The test checks sandbox flags and origin for the proxy added in step 2, by
4549// checking whether the grandchild frame added in step 3 sees proper sandbox
4550// flags and origin for its (remote) parent. This wasn't addressed when
4551// https://crbug.com/423587 was fixed.
stanisc2b6e1d72016-05-11 01:31:544552// TODO(alexmos): Re-enable when https://crbug.com/610893 is fixed.
4553IN_PROC_BROWSER_TEST_F(
4554 SitePerProcessBrowserTest,
4555 DISABLED_ProxiesForNewChildFramesHaveCorrectReplicationState) {
alexmos6e0ee0c2015-05-01 18:57:344556 GURL main_url(
4557 embedded_test_server()->GetURL("/frame_tree/page_with_one_frame.html"));
4558 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4559
4560 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:004561 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmos6e0ee0c2015-05-01 18:57:344562 TestNavigationObserver observer(shell()->web_contents());
4563
4564 EXPECT_EQ(
4565 " Site A ------------ proxies for B\n"
4566 " +--Site B ------- proxies for A\n"
4567 "Where A = http://127.0.0.1/\n"
4568 " B = http://baz.com/",
4569 DepictFrameTree(root));
4570
4571 // In the root frame, add a new sandboxed local frame, which itself has a
4572 // child frame on baz.com. Wait for three RenderFrameHosts to be created:
4573 // the new sandboxed local frame, its child (while it's still local), and a
4574 // pending RFH when starting the cross-site navigation to baz.com.
4575 RenderFrameHostCreatedObserver frame_observer(shell()->web_contents(), 3);
nickadef4a52016-06-09 18:45:544576 EXPECT_TRUE(ExecuteScript(root,
4577 "addFrame('/frame_tree/page_with_one_frame.html',"
4578 " 'allow-scripts allow-same-origin'))"));
alexmos6e0ee0c2015-05-01 18:57:344579 frame_observer.Wait();
4580
4581 // Wait for the cross-site navigation to baz.com in the grandchild to finish.
4582 FrameTreeNode* bottom_child = root->child_at(1)->child_at(0);
4583 TestFrameNavigationObserver navigation_observer(bottom_child);
4584 navigation_observer.Wait();
4585
4586 EXPECT_EQ(
4587 " Site A ------------ proxies for B\n"
4588 " |--Site B ------- proxies for A\n"
4589 " +--Site A ------- proxies for B\n"
4590 " +--Site B -- proxies for A\n"
4591 "Where A = http://127.0.0.1/\n"
4592 " B = http://baz.com/",
4593 DepictFrameTree(root));
4594
4595 // Use location.ancestorOrigins to check that the grandchild on baz.com sees
4596 // correct origin for its parent.
Nick Carterb7e71312018-08-03 23:36:134597 EXPECT_EQ(ListValueOf(url::Origin::Create(main_url)),
4598 EvalJs(bottom_child, "Array.from(location.ancestorOrigins);"));
alexmos6e0ee0c2015-05-01 18:57:344599
4600 // Check that the sandbox flags in the browser process are correct.
dcheng5f60abb2015-05-28 01:39:364601 // "allow-scripts" resets both WebSandboxFlags::Scripts and
4602 // WebSandboxFlags::AutomaticFeatures bits per blink::parseSandboxPolicy().
4603 blink::WebSandboxFlags expected_flags =
Blink Reformat1c4d759e2017-04-09 16:34:544604 blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kScripts &
4605 ~blink::WebSandboxFlags::kAutomaticFeatures &
4606 ~blink::WebSandboxFlags::kOrigin;
Ian Clellandcdc4f312017-10-13 22:24:124607 EXPECT_EQ(expected_flags,
4608 root->child_at(1)->effective_frame_policy().sandbox_flags);
alexmos6e0ee0c2015-05-01 18:57:344609
4610 // The child of the sandboxed frame should've inherited sandbox flags, so it
4611 // should not be able to create popups.
Ian Clellandcdc4f312017-10-13 22:24:124612 EXPECT_EQ(expected_flags,
4613 bottom_child->effective_frame_policy().sandbox_flags);
Nick Carterb7e71312018-08-03 23:36:134614 EXPECT_EQ(false,
4615 EvalJs(bottom_child, "!!window.open('data:text/html,dataurl')"));
alexmos6e0ee0c2015-05-01 18:57:344616 EXPECT_EQ(1u, Shell::windows().size());
4617}
4618
alexmos998581d2015-01-22 01:01:594619// Verify that a child frame can retrieve the name property set by its parent.
4620IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, WindowNameReplication) {
4621 GURL main_url(embedded_test_server()->GetURL("/frame_tree/2-4.html"));
4622 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4623
4624 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:004625 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmos998581d2015-01-22 01:01:594626
clamyf1ccb4d2015-01-28 17:40:384627 TestNavigationObserver observer(shell()->web_contents());
alexmos998581d2015-01-22 01:01:594628
4629 // Load cross-site page into iframe.
4630 GURL frame_url =
4631 embedded_test_server()->GetURL("foo.com", "/frame_tree/3-1.html");
4632 NavigateFrameToURL(root->child_at(0), frame_url);
clamyf1ccb4d2015-01-28 17:40:384633 EXPECT_TRUE(observer.last_navigation_succeeded());
4634 EXPECT_EQ(frame_url, observer.last_navigation_url());
alexmos998581d2015-01-22 01:01:594635
4636 // Ensure that a new process is created for the subframe.
4637 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
4638 root->child_at(0)->current_frame_host()->GetSiteInstance());
4639
4640 // Check that the window.name seen by the frame matches the name attribute
4641 // specified by its parent in the iframe tag.
Nick Carterb7e71312018-08-03 23:36:134642 EXPECT_EQ("3-1-name", EvalJs(root->child_at(0), "window.name;"));
alexmos998581d2015-01-22 01:01:594643}
4644
alexmosbe2f4c32015-03-10 02:30:234645// Verify that dynamic updates to a frame's window.name propagate to the
4646// frame's proxies, so that the latest frame names can be used in navigations.
4647IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, DynamicWindowName) {
4648 GURL main_url(embedded_test_server()->GetURL("/frame_tree/2-4.html"));
4649 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4650
4651 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:004652 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmosbe2f4c32015-03-10 02:30:234653 TestNavigationObserver observer(shell()->web_contents());
4654
4655 // Load cross-site page into iframe.
4656 GURL frame_url =
4657 embedded_test_server()->GetURL("foo.com", "/frame_tree/3-1.html");
4658 NavigateFrameToURL(root->child_at(0), frame_url);
4659 EXPECT_TRUE(observer.last_navigation_succeeded());
4660 EXPECT_EQ(frame_url, observer.last_navigation_url());
4661
4662 // Browser process should know the child frame's original window.name
4663 // specified in the iframe element.
4664 EXPECT_EQ(root->child_at(0)->frame_name(), "3-1-name");
4665
4666 // Update the child frame's window.name.
nickadef4a52016-06-09 18:45:544667 EXPECT_TRUE(
4668 ExecuteScript(root->child_at(0), "window.name = 'updated-name';"));
alexmosbe2f4c32015-03-10 02:30:234669
4670 // The change should propagate to the browser process.
4671 EXPECT_EQ(root->child_at(0)->frame_name(), "updated-name");
4672
4673 // The proxy in the parent process should also receive the updated name.
yukishiinoc9d8a85a2017-01-26 09:55:574674 // Now iframe's name and the content window's name differ, so it shouldn't
4675 // be possible to access to the content window with the updated name.
Nick Carterb7e71312018-08-03 23:36:134676 //
4677 // TODO(yukishiino): The following expectation should be |true|, but we're
yukishiino8633d64a2017-03-15 18:06:254678 // intentionally disabling the name and origin check of the named access on
4679 // window. See also crbug.com/538562 and crbug.com/701489.
Nick Carterb7e71312018-08-03 23:36:134680 EXPECT_EQ(false, EvalJs(shell(), "frames['updated-name'] === undefined;"));
yukishiinoc9d8a85a2017-01-26 09:55:574681 // Change iframe's name to match the content window's name so that it can
4682 // reference the child frame by its new name in case of cross origin.
Ria Jiangabad8d9a2018-01-24 16:52:364683 EXPECT_TRUE(ExecuteScript(root, "window['3-1-id'].name = 'updated-name';"));
Nick Carterb7e71312018-08-03 23:36:134684 EXPECT_EQ(true, EvalJs(shell(), "frames['updated-name'] == frames[0];"));
alexmosbe2f4c32015-03-10 02:30:234685
4686 // Issue a renderer-initiated navigation from the root frame to the child
4687 // frame using the frame's name. Make sure correct frame is navigated.
4688 //
4689 // TODO(alexmos): When blink::createWindow is refactored to handle
4690 // RemoteFrames, this should also be tested via window.open(url, frame_name)
4691 // and a more complicated frame hierarchy (https://crbug.com/463742)
4692 TestFrameNavigationObserver frame_observer(root->child_at(0));
4693 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
nickadef4a52016-06-09 18:45:544694 EXPECT_TRUE(ExecuteScript(
4695 shell(),
danakj824a7ff2019-02-07 20:34:024696 JsReplace("frames['updated-name'].location.href = $1", foo_url)));
alexmosbe2f4c32015-03-10 02:30:234697 frame_observer.Wait();
4698 EXPECT_EQ(foo_url, root->child_at(0)->current_url());
4699}
4700
alexmosa7a4ff822015-04-27 17:59:564701// Verify that when a frame is navigated to a new origin, the origin update
4702// propagates to the frame's proxies.
4703IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, OriginUpdatesReachProxies) {
4704 GURL main_url(
4705 embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html"));
4706 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4707
4708 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:004709 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmosa7a4ff822015-04-27 17:59:564710 TestNavigationObserver observer(shell()->web_contents());
4711
4712 EXPECT_EQ(
4713 " Site A ------------ proxies for B\n"
4714 " |--Site B ------- proxies for A\n"
4715 " +--Site A ------- proxies for B\n"
4716 "Where A = http://127.0.0.1/\n"
4717 " B = http://bar.com/",
4718 DepictFrameTree(root));
4719
4720 // Navigate second subframe to a baz.com. This should send an origin update
4721 // to the frame's proxy in the bar.com (first frame's) process.
4722 GURL frame_url = embedded_test_server()->GetURL("baz.com", "/title2.html");
4723 NavigateFrameToURL(root->child_at(1), frame_url);
4724 EXPECT_TRUE(observer.last_navigation_succeeded());
4725 EXPECT_EQ(frame_url, observer.last_navigation_url());
4726
4727 // The first frame can't directly observe the second frame's origin with
4728 // JavaScript. Instead, try to navigate the second frame from the first
4729 // frame. This should fail with a console error message, which should
4730 // contain the second frame's updated origin (see blink::Frame::canNavigate).
dcheng59716272016-04-09 05:19:084731 std::unique_ptr<ConsoleObserverDelegate> console_delegate(
alexmosa7a4ff822015-04-27 17:59:564732 new ConsoleObserverDelegate(
4733 shell()->web_contents(),
4734 "Unsafe JavaScript attempt to initiate navigation*"));
4735 shell()->web_contents()->SetDelegate(console_delegate.get());
4736
4737 // frames[1] can't be used due to a bug where RemoteFrames are created out of
4738 // order (https://crbug.com/478792). Instead, target second frame by name.
mkwstce819952016-09-29 13:55:104739 EXPECT_TRUE(ExecuteScript(root->child_at(0),
4740 "try { parent.frames['frame2'].location.href = "
4741 "'data:text/html,foo'; } catch (e) {}"));
alexmosa7a4ff822015-04-27 17:59:564742 console_delegate->Wait();
4743
creisabdd2bd62015-11-21 01:14:584744 std::string frame_origin = root->child_at(1)->current_origin().Serialize();
alexmosa7a4ff822015-04-27 17:59:564745 EXPECT_EQ(frame_origin + "/", frame_url.GetOrigin().spec());
4746 EXPECT_TRUE(
brettwd97eede2015-07-06 22:09:004747 base::MatchPattern(console_delegate->message(), "*" + frame_origin + "*"))
alexmosa7a4ff822015-04-27 17:59:564748 << "Error message does not contain the frame's latest origin ("
4749 << frame_origin << ")";
4750}
4751
nasko3e8c20e2014-12-18 06:54:564752// Ensure that navigating subframes in --site-per-process mode properly fires
4753// the DidStopLoading event on WebContentsObserver.
4754IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CrossSiteDidStopLoading) {
4755 GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
davidsac6e6c35e42016-11-21 19:45:574756 EXPECT_TRUE(NavigateToURL(shell(), main_url));
nasko3e8c20e2014-12-18 06:54:564757
4758 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:004759 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
nasko3e8c20e2014-12-18 06:54:564760
clamyf1ccb4d2015-01-28 17:40:384761 TestNavigationObserver observer(shell()->web_contents());
nasko3e8c20e2014-12-18 06:54:564762
4763 // Load same-site page into iframe.
4764 FrameTreeNode* child = root->child_at(0);
4765 GURL http_url(embedded_test_server()->GetURL("/title1.html"));
4766 NavigateFrameToURL(child, http_url);
clamyf1ccb4d2015-01-28 17:40:384767 EXPECT_EQ(http_url, observer.last_navigation_url());
4768 EXPECT_TRUE(observer.last_navigation_succeeded());
nasko3e8c20e2014-12-18 06:54:564769
4770 // Load cross-site page into iframe.
4771 TestNavigationObserver nav_observer(shell()->web_contents(), 1);
4772 GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html");
4773 NavigationController::LoadURLParams params(url);
4774 params.transition_type = ui::PAGE_TRANSITION_LINK;
4775 params.frame_tree_node_id = child->frame_tree_node_id();
4776 child->navigator()->GetController()->LoadURLWithParams(params);
4777 nav_observer.Wait();
4778
4779 // Verify that the navigation succeeded and the expected URL was loaded.
clamyf1ccb4d2015-01-28 17:40:384780 EXPECT_TRUE(observer.last_navigation_succeeded());
4781 EXPECT_EQ(url, observer.last_navigation_url());
nasko3e8c20e2014-12-18 06:54:564782}
4783
creis0040d342015-02-19 01:42:374784// Ensure that the renderer does not crash when navigating a frame that has a
4785// sibling RemoteFrame. See https://crbug.com/426953.
4786IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
4787 NavigateWithSiblingRemoteFrame) {
4788 GURL main_url(
4789 embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html"));
davidsac6e6c35e42016-11-21 19:45:574790 EXPECT_TRUE(NavigateToURL(shell(), main_url));
creis0040d342015-02-19 01:42:374791
4792 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:004793 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
creis0040d342015-02-19 01:42:374794 TestNavigationObserver observer(shell()->web_contents());
4795
4796 // Make sure the first frame is out of process.
4797 ASSERT_EQ(2U, root->child_count());
4798 FrameTreeNode* node2 = root->child_at(0);
4799 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
4800 node2->current_frame_host()->GetSiteInstance());
4801
4802 // Make sure the second frame is in the parent's process.
4803 FrameTreeNode* node3 = root->child_at(1);
4804 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
4805 node3->current_frame_host()->GetSiteInstance());
4806
4807 // Navigate the second iframe (node3) to a URL in its own process.
4808 GURL title_url = embedded_test_server()->GetURL("/title2.html");
4809 NavigateFrameToURL(node3, title_url);
4810 EXPECT_TRUE(observer.last_navigation_succeeded());
4811 EXPECT_EQ(title_url, observer.last_navigation_url());
4812 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
4813 node3->current_frame_host()->GetSiteInstance());
4814 EXPECT_TRUE(node3->current_frame_host()->IsRenderFrameLive());
4815}
4816
kenrb77618cd52016-02-18 19:13:384817// Ensure that the renderer does not crash when a local frame with a remote
4818// parent frame is swapped from local to remote, then back to local again.
4819// See https://crbug.com/585654.
4820IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
4821 NavigateSiblingsToSameProcess) {
4822 GURL main_url(
4823 embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html"));
davidsac6e6c35e42016-11-21 19:45:574824 EXPECT_TRUE(NavigateToURL(shell(), main_url));
kenrb77618cd52016-02-18 19:13:384825
4826 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:004827 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
kenrb77618cd52016-02-18 19:13:384828
4829 FrameTreeNode* node2 = root->child_at(0);
4830 FrameTreeNode* node3 = root->child_at(1);
4831
4832 // Navigate the second iframe to the same process as the first.
4833 GURL frame_url = embedded_test_server()->GetURL("bar.com", "/title1.html");
4834 NavigateFrameToURL(node3, frame_url);
4835
4836 // Verify that they are in the same process.
4837 EXPECT_EQ(node2->current_frame_host()->GetSiteInstance(),
4838 node3->current_frame_host()->GetSiteInstance());
4839 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
4840 node3->current_frame_host()->GetSiteInstance());
4841
4842 // Navigate the first iframe into its parent's process.
4843 GURL title_url = embedded_test_server()->GetURL("/title2.html");
4844 NavigateFrameToURL(node2, title_url);
4845 EXPECT_NE(node2->current_frame_host()->GetSiteInstance(),
4846 node3->current_frame_host()->GetSiteInstance());
4847
4848 // Return the first iframe to the same process as its sibling, and ensure
4849 // that it does not crash.
4850 NavigateFrameToURL(node2, frame_url);
4851 EXPECT_EQ(node2->current_frame_host()->GetSiteInstance(),
4852 node3->current_frame_host()->GetSiteInstance());
4853 EXPECT_TRUE(node2->current_frame_host()->IsRenderFrameLive());
4854}
4855
alexmosf40ce5b02015-02-25 20:19:564856// Verify that load events for iframe elements work when the child frame is
4857// out-of-process. In such cases, the load event is forwarded from the child
4858// frame to the parent frame via the browser process.
4859IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, LoadEventForwarding) {
4860 // Load a page with a cross-site frame. The parent page has an onload
4861 // handler in the iframe element that appends "LOADED" to the document title.
4862 {
4863 GURL main_url(
4864 embedded_test_server()->GetURL("/frame_with_load_event.html"));
4865 base::string16 expected_title(base::UTF8ToUTF16("LOADED"));
4866 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
4867 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4868 EXPECT_EQ(title_watcher.WaitAndGetTitle(), expected_title);
4869 }
4870
4871 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:004872 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmosf40ce5b02015-02-25 20:19:564873
4874 // Load another cross-site page into the iframe and check that the load event
4875 // is fired.
4876 {
4877 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
4878 base::string16 expected_title(base::UTF8ToUTF16("LOADEDLOADED"));
4879 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
4880 TestNavigationObserver observer(shell()->web_contents());
4881 NavigateFrameToURL(root->child_at(0), foo_url);
4882 EXPECT_TRUE(observer.last_navigation_succeeded());
4883 EXPECT_EQ(foo_url, observer.last_navigation_url());
4884 EXPECT_EQ(title_watcher.WaitAndGetTitle(), expected_title);
4885 }
4886}
4887
alexmose7da5a12015-04-09 02:22:164888// Check that postMessage can be routed between cross-site iframes.
4889IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, SubframePostMessage) {
4890 GURL main_url(embedded_test_server()->GetURL(
4891 "/frame_tree/page_with_post_message_frames.html"));
4892 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4893
4894 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:004895 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmose7da5a12015-04-09 02:22:164896
alexmose7da5a12015-04-09 02:22:164897 ASSERT_EQ(2U, root->child_count());
4898
4899 // Verify the frames start at correct URLs. First frame should be
4900 // same-site; second frame should be cross-site.
4901 GURL same_site_url(embedded_test_server()->GetURL("/post_message.html"));
4902 EXPECT_EQ(same_site_url, root->child_at(0)->current_url());
Ria Jiangabad8d9a2018-01-24 16:52:364903 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/post_message.html"));
alexmose7da5a12015-04-09 02:22:164904 EXPECT_EQ(foo_url, root->child_at(1)->current_url());
4905 EXPECT_NE(root->child_at(0)->current_frame_host()->GetSiteInstance(),
4906 root->child_at(1)->current_frame_host()->GetSiteInstance());
4907
4908 // Send a message from first, same-site frame to second, cross-site frame.
4909 // Expect the second frame to reply back to the first frame.
alexmos9f8705a2015-05-06 19:58:594910 PostMessageAndWaitForReply(root->child_at(0),
4911 "postToSibling('subframe-msg','subframe2')",
4912 "\"done-subframe1\"");
alexmose7da5a12015-04-09 02:22:164913
4914 // Send a postMessage from second, cross-site frame to its parent. Expect
4915 // parent to send a reply to the frame.
4916 base::string16 expected_title(base::ASCIIToUTF16("subframe-msg"));
4917 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
alexmos9f8705a2015-05-06 19:58:594918 PostMessageAndWaitForReply(root->child_at(1), "postToParent('subframe-msg')",
4919 "\"done-subframe2\"");
alexmose7da5a12015-04-09 02:22:164920 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
4921
alexmose7da5a12015-04-09 02:22:164922 // Verify the total number of received messages for each subframe. First
alexmos58729042015-06-18 23:20:004923 // frame should have one message (reply from second frame). Second frame
alexmose7da5a12015-04-09 02:22:164924 // should have two messages (message from first frame and reply from parent).
alexmos58729042015-06-18 23:20:004925 // Parent should have one message (from second frame).
4926 EXPECT_EQ(1, GetReceivedMessages(root->child_at(0)));
4927 EXPECT_EQ(2, GetReceivedMessages(root->child_at(1)));
4928 EXPECT_EQ(1, GetReceivedMessages(root));
4929}
4930
csharrison264bca642016-05-06 19:27:094931// Check that renderer initiated navigations which commit a new RenderFrameHost
4932// do not crash if the original RenderFrameHost was being covered by an
4933// interstitial. See crbug.com/607964.
4934IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
4935 NavigateOpenerWithInterstitial) {
4936 EXPECT_TRUE(NavigateToURL(
4937 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
4938
4939 // Open a popup and navigate it to bar.com.
4940 ShellAddedObserver new_shell_observer;
4941 EXPECT_TRUE(ExecuteScript(web_contents(), "window.open('about:blank');"));
4942 Shell* popup = new_shell_observer.GetShell();
Alex Moshchuk7e26eca2018-03-03 01:34:294943 EXPECT_TRUE(NavigateToURLFromRenderer(
4944 popup,
4945 embedded_test_server()->GetURL("bar.com", "/navigate_opener.html")));
csharrison264bca642016-05-06 19:27:094946
4947 // Show an interstitial in the opener.
4948 TestInterstitialDelegate* delegate = new TestInterstitialDelegate;
4949 WebContentsImpl* opener_contents =
4950 static_cast<WebContentsImpl*>(web_contents());
4951 GURL interstitial_url("http://interstitial");
4952 InterstitialPageImpl* interstitial = new InterstitialPageImpl(
4953 opener_contents, static_cast<RenderWidgetHostDelegate*>(opener_contents),
4954 true, interstitial_url, delegate);
4955 interstitial->Show();
4956 WaitForInterstitialAttach(opener_contents);
4957
4958 // Now, navigate the opener cross-process using the popup while it still has
4959 // an interstitial. This should not crash.
4960 TestNavigationObserver navigation_observer(opener_contents);
nickadef4a52016-06-09 18:45:544961 EXPECT_TRUE(ExecuteScript(popup, "navigateOpener();"));
csharrison264bca642016-05-06 19:27:094962 navigation_observer.Wait();
4963}
4964
alexmos58729042015-06-18 23:20:004965// Check that postMessage can be sent from a subframe on a cross-process opener
4966// tab, and that its event.source points to a valid proxy.
4967IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
alexmos17e57532015-08-15 02:54:364968 PostMessageWithSubframeOnOpenerChain) {
alexmos58729042015-06-18 23:20:004969 GURL main_url(embedded_test_server()->GetURL(
4970 "a.com", "/frame_tree/page_with_post_message_frames.html"));
4971 EXPECT_TRUE(NavigateToURL(shell(), main_url));
4972
4973 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:004974 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmos58729042015-06-18 23:20:004975
4976 ASSERT_EQ(2U, root->child_count());
4977
4978 // Verify the initial state of the world. First frame should be same-site;
4979 // second frame should be cross-site.
4980 EXPECT_EQ(
4981 " Site A ------------ proxies for B\n"
4982 " |--Site A ------- proxies for B\n"
4983 " +--Site B ------- proxies for A\n"
4984 "Where A = http://a.com/\n"
4985 " B = http://foo.com/",
4986 DepictFrameTree(root));
4987
4988 // Open a popup from the first subframe (so that popup's window.opener points
4989 // to the subframe) and navigate it to bar.com.
4990 ShellAddedObserver new_shell_observer;
nickadef4a52016-06-09 18:45:544991 EXPECT_TRUE(ExecuteScript(root->child_at(0), "openPopup('about:blank');"));
alexmos58729042015-06-18 23:20:004992 Shell* popup = new_shell_observer.GetShell();
4993 GURL popup_url(
4994 embedded_test_server()->GetURL("bar.com", "/post_message.html"));
Alex Moshchuk7e26eca2018-03-03 01:34:294995 EXPECT_TRUE(NavigateToURLFromRenderer(popup, popup_url));
alexmos58729042015-06-18 23:20:004996
4997 // From the popup, open another popup for baz.com. This will be used to
4998 // check that the whole opener chain is processed when creating proxies and
4999 // not just an immediate opener.
5000 ShellAddedObserver new_shell_observer2;
nickadef4a52016-06-09 18:45:545001 EXPECT_TRUE(ExecuteScript(popup, "openPopup('about:blank');"));
alexmos58729042015-06-18 23:20:005002 Shell* popup2 = new_shell_observer2.GetShell();
5003 GURL popup2_url(
5004 embedded_test_server()->GetURL("baz.com", "/post_message.html"));
Alex Moshchuk7e26eca2018-03-03 01:34:295005 EXPECT_TRUE(NavigateToURLFromRenderer(popup2, popup2_url));
alexmos58729042015-06-18 23:20:005006
5007 // Ensure that we've created proxies for SiteInstances of both popups (C, D)
5008 // in the main window's frame tree.
5009 EXPECT_EQ(
5010 " Site A ------------ proxies for B C D\n"
5011 " |--Site A ------- proxies for B C D\n"
5012 " +--Site B ------- proxies for A C D\n"
5013 "Where A = http://a.com/\n"
5014 " B = http://foo.com/\n"
5015 " C = http://bar.com/\n"
5016 " D = http://baz.com/",
5017 DepictFrameTree(root));
5018
5019 // Check the first popup's frame tree as well. Note that it doesn't have a
5020 // proxy for foo.com, since foo.com can't reach the popup. It does have a
5021 // proxy for its opener a.com (which can reach it via the window.open
5022 // reference) and second popup (which can reach it via window.opener).
5023 FrameTreeNode* popup_root =
5024 static_cast<WebContentsImpl*>(popup->web_contents())
5025 ->GetFrameTree()
5026 ->root();
5027 EXPECT_EQ(
5028 " Site C ------------ proxies for A D\n"
5029 "Where A = http://a.com/\n"
5030 " C = http://bar.com/\n"
5031 " D = http://baz.com/",
5032 DepictFrameTree(popup_root));
5033
5034 // Send a message from first subframe on main page to the first popup and
5035 // wait for a reply back. The reply verifies that the proxy for the opener
5036 // tab's subframe is targeted properly.
5037 PostMessageAndWaitForReply(root->child_at(0), "postToPopup('subframe-msg')",
5038 "\"done-subframe1\"");
5039
alexmos5ac402d2015-07-09 07:51:105040 // Send a postMessage from the popup to window.opener and ensure that it
5041 // reaches subframe1. This verifies that the subframe opener information
5042 // propagated to the popup's RenderFrame. Wait for subframe1 to send a reply
5043 // message to the popup.
nickadef4a52016-06-09 18:45:545044 EXPECT_TRUE(ExecuteScript(popup, "window.name = 'popup';"));
alexmos5ac402d2015-07-09 07:51:105045 PostMessageAndWaitForReply(popup_root, "postToOpener('subframe-msg', '*')",
5046 "\"done-popup\"");
alexmos58729042015-06-18 23:20:005047
alexmos5ac402d2015-07-09 07:51:105048 // Second a postMessage from popup2 to window.opener.opener, which should
5049 // resolve to subframe1. This tests opener chains of length greater than 1.
5050 // As before, subframe1 will send a reply to popup2.
5051 FrameTreeNode* popup2_root =
5052 static_cast<WebContentsImpl*>(popup2->web_contents())
5053 ->GetFrameTree()
5054 ->root();
nickadef4a52016-06-09 18:45:545055 EXPECT_TRUE(ExecuteScript(popup2, "window.name = 'popup2';"));
alexmos5ac402d2015-07-09 07:51:105056 PostMessageAndWaitForReply(popup2_root,
5057 "postToOpenerOfOpener('subframe-msg', '*')",
5058 "\"done-popup2\"");
5059
5060 // Verify the total number of received messages for each subframe:
5061 // - 3 for first subframe (two from first popup, one from second popup)
5062 // - 2 for popup (both from first subframe)
5063 // - 1 for popup2 (reply from first subframe)
5064 // - 0 for other frames
alexmos58729042015-06-18 23:20:005065 EXPECT_EQ(0, GetReceivedMessages(root));
alexmos5ac402d2015-07-09 07:51:105066 EXPECT_EQ(3, GetReceivedMessages(root->child_at(0)));
alexmos58729042015-06-18 23:20:005067 EXPECT_EQ(0, GetReceivedMessages(root->child_at(1)));
alexmos5ac402d2015-07-09 07:51:105068 EXPECT_EQ(2, GetReceivedMessages(popup_root));
5069 EXPECT_EQ(1, GetReceivedMessages(popup2_root));
alexmose7da5a12015-04-09 02:22:165070}
5071
alexmos9f8705a2015-05-06 19:58:595072// Check that parent.frames[num] references correct sibling frames when the
5073// parent is remote. See https://crbug.com/478792.
5074IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, IndexedFrameAccess) {
5075 // Start on a page with three same-site subframes.
5076 GURL main_url(
5077 embedded_test_server()->GetURL("a.com", "/frame_tree/top.html"));
5078 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5079
5080 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:005081 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmos9f8705a2015-05-06 19:58:595082 ASSERT_EQ(3U, root->child_count());
5083 FrameTreeNode* child0 = root->child_at(0);
5084 FrameTreeNode* child1 = root->child_at(1);
5085 FrameTreeNode* child2 = root->child_at(2);
5086
5087 // Send each of the frames to a different site. Each new renderer will first
5088 // create proxies for the parent and two sibling subframes and then create
5089 // and insert the new RenderFrame into the frame tree.
5090 GURL b_url(embedded_test_server()->GetURL("b.com", "/post_message.html"));
5091 GURL c_url(embedded_test_server()->GetURL("c.com", "/post_message.html"));
5092 GURL d_url(embedded_test_server()->GetURL("d.com", "/post_message.html"));
5093 NavigateFrameToURL(child0, b_url);
alexmos9f8705a2015-05-06 19:58:595094 NavigateFrameToURL(child1, c_url);
alexmos9f8705a2015-05-06 19:58:595095 NavigateFrameToURL(child2, d_url);
alexmos9f8705a2015-05-06 19:58:595096
5097 EXPECT_EQ(
5098 " Site A ------------ proxies for B C D\n"
5099 " |--Site B ------- proxies for A C D\n"
5100 " |--Site C ------- proxies for A B D\n"
5101 " +--Site D ------- proxies for A B C\n"
5102 "Where A = http://a.com/\n"
5103 " B = http://b.com/\n"
5104 " C = http://c.com/\n"
5105 " D = http://d.com/",
5106 DepictFrameTree(root));
5107
5108 // Check that each subframe sees itself at correct index in parent.frames.
Nick Carterb7e71312018-08-03 23:36:135109 EXPECT_EQ(true, EvalJs(child0, "window === parent.frames[0];"));
5110 EXPECT_EQ(true, EvalJs(child1, "window === parent.frames[1];"));
5111 EXPECT_EQ(true, EvalJs(child2, "window === parent.frames[2];"));
alexmos9f8705a2015-05-06 19:58:595112
5113 // Send a postMessage from B to parent.frames[1], which should go to C, and
5114 // wait for reply.
5115 PostMessageAndWaitForReply(child0, "postToSibling('subframe-msg', 1)",
5116 "\"done-1-1-name\"");
5117
5118 // Send a postMessage from C to parent.frames[2], which should go to D, and
5119 // wait for reply.
5120 PostMessageAndWaitForReply(child1, "postToSibling('subframe-msg', 2)",
5121 "\"done-1-2-name\"");
5122
5123 // Verify the total number of received messages for each subframe.
alexmos58729042015-06-18 23:20:005124 EXPECT_EQ(1, GetReceivedMessages(child0));
5125 EXPECT_EQ(2, GetReceivedMessages(child1));
5126 EXPECT_EQ(1, GetReceivedMessages(child2));
alexmos9f8705a2015-05-06 19:58:595127}
5128
Ken Buchanancacd0082018-01-22 20:38:125129IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, RFPHDestruction) {
nick59dcb162015-04-09 20:29:015130 GURL main_url(embedded_test_server()->GetURL("/site_per_process_main.html"));
davidsac6e6c35e42016-11-21 19:45:575131 EXPECT_TRUE(NavigateToURL(shell(), main_url));
nick59dcb162015-04-09 20:29:015132
5133 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:005134 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
nick59dcb162015-04-09 20:29:015135
5136 TestNavigationObserver observer(shell()->web_contents());
5137
5138 // Load cross-site page into iframe.
5139 FrameTreeNode* child = root->child_at(0);
5140 GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html");
creis29f85682016-11-08 01:52:425141 {
5142 RenderFrameDeletedObserver deleted_observer(child->current_frame_host());
5143 NavigateFrameToURL(root->child_at(0), url);
5144 deleted_observer.WaitUntilDeleted();
5145 }
nick59dcb162015-04-09 20:29:015146 EXPECT_TRUE(observer.last_navigation_succeeded());
5147 EXPECT_EQ(url, observer.last_navigation_url());
nick44bacf32015-04-14 02:06:395148 EXPECT_EQ(
5149 " Site A ------------ proxies for B\n"
5150 " |--Site B ------- proxies for A\n"
5151 " +--Site A ------- proxies for B\n"
5152 " |--Site A -- proxies for B\n"
5153 " +--Site A -- proxies for B\n"
5154 " +--Site A -- proxies for B\n"
5155 "Where A = http://127.0.0.1/\n"
5156 " B = http://foo.com/",
5157 DepictFrameTree(root));
nick59dcb162015-04-09 20:29:015158
5159 // Load another cross-site page.
5160 url = embedded_test_server()->GetURL("bar.com", "/title3.html");
creis29f85682016-11-08 01:52:425161 {
5162 RenderFrameDeletedObserver deleted_observer(child->current_frame_host());
5163 NavigateIframeToURL(shell()->web_contents(), "test", url);
5164 deleted_observer.WaitUntilDeleted();
5165 }
nick59dcb162015-04-09 20:29:015166 EXPECT_TRUE(observer.last_navigation_succeeded());
5167 EXPECT_EQ(url, observer.last_navigation_url());
nick44bacf32015-04-14 02:06:395168 EXPECT_EQ(
5169 " Site A ------------ proxies for C\n"
5170 " |--Site C ------- proxies for A\n"
5171 " +--Site A ------- proxies for C\n"
5172 " |--Site A -- proxies for C\n"
5173 " +--Site A -- proxies for C\n"
5174 " +--Site A -- proxies for C\n"
5175 "Where A = http://127.0.0.1/\n"
5176 " C = http://bar.com/",
5177 DepictFrameTree(root));
nick59dcb162015-04-09 20:29:015178
5179 // Navigate back to the parent's origin.
creis29f85682016-11-08 01:52:425180 {
5181 RenderFrameDeletedObserver deleted_observer(child->current_frame_host());
5182 url = embedded_test_server()->GetURL("/title1.html");
5183 NavigateFrameToURL(child, url);
5184 // Wait for the old process to exit, to verify that the proxies go away.
5185 deleted_observer.WaitUntilDeleted();
5186 }
nick59dcb162015-04-09 20:29:015187 EXPECT_EQ(url, observer.last_navigation_url());
5188 EXPECT_TRUE(observer.last_navigation_succeeded());
lfgf2d4f912016-05-11 23:18:485189
nick44bacf32015-04-14 02:06:395190 EXPECT_EQ(
5191 " Site A\n"
5192 " |--Site A\n"
5193 " +--Site A\n"
5194 " |--Site A\n"
5195 " +--Site A\n"
5196 " +--Site A\n"
5197 "Where A = http://127.0.0.1/",
5198 DepictFrameTree(root));
nick59dcb162015-04-09 20:29:015199}
5200
alexmos4cf2aa32015-07-15 23:40:435201IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, OpenPopupWithRemoteParent) {
5202 GURL main_url(
5203 embedded_test_server()->GetURL("a.com", "/site_per_process_main.html"));
davidsac6e6c35e42016-11-21 19:45:575204 EXPECT_TRUE(NavigateToURL(shell(), main_url));
alexmos4cf2aa32015-07-15 23:40:435205
5206 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:005207 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmos4cf2aa32015-07-15 23:40:435208
5209 // Navigate first child cross-site.
5210 GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
5211 NavigateFrameToURL(root->child_at(0), frame_url);
5212
5213 // Open a popup from the first child.
nickadef4a52016-06-09 18:45:545214 Shell* new_shell =
5215 OpenPopup(root->child_at(0), GURL(url::kAboutBlankURL), "");
alexmos4cf2aa32015-07-15 23:40:435216 EXPECT_TRUE(new_shell);
5217
5218 // Check that the popup's opener is correct on both the browser and renderer
5219 // sides.
5220 FrameTreeNode* popup_root =
5221 static_cast<WebContentsImpl*>(new_shell->web_contents())
5222 ->GetFrameTree()
5223 ->root();
5224 EXPECT_EQ(root->child_at(0), popup_root->opener());
5225
Nick Carterb7e71312018-08-03 23:36:135226 EXPECT_EQ(frame_url.spec(),
5227 EvalJs(popup_root, "window.opener.location.href;"));
alexmos4cf2aa32015-07-15 23:40:435228
5229 // Now try the same with a cross-site popup and make sure it ends up in a new
5230 // process and with a correct opener.
5231 GURL popup_url(embedded_test_server()->GetURL("c.com", "/title2.html"));
nickadef4a52016-06-09 18:45:545232 Shell* cross_site_popup = OpenPopup(root->child_at(0), popup_url, "");
alexmos4cf2aa32015-07-15 23:40:435233 EXPECT_TRUE(cross_site_popup);
5234
5235 FrameTreeNode* cross_site_popup_root =
5236 static_cast<WebContentsImpl*>(cross_site_popup->web_contents())
5237 ->GetFrameTree()
5238 ->root();
5239 EXPECT_EQ(cross_site_popup_root->current_url(), popup_url);
5240
5241 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
5242 cross_site_popup->web_contents()->GetSiteInstance());
5243 EXPECT_NE(root->child_at(0)->current_frame_host()->GetSiteInstance(),
5244 cross_site_popup->web_contents()->GetSiteInstance());
5245
5246 EXPECT_EQ(root->child_at(0), cross_site_popup_root->opener());
5247
5248 // Ensure the popup's window.opener points to the right subframe. Note that
5249 // we can't check the opener's location as above since it's cross-origin.
Nick Carterb7e71312018-08-03 23:36:135250 EXPECT_EQ(true, EvalJs(cross_site_popup_root,
5251 "window.opener === window.opener.top.frames[0];"));
alexmos4cf2aa32015-07-15 23:40:435252}
5253
alexmos5b50b6742016-03-17 20:38:055254// Test that cross-process popups can't be navigated to disallowed URLs by
5255// their opener. This ensures that proper URL validation is performed when
5256// RenderFrameProxyHosts are navigated. See https://crbug.com/595339.
5257IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, NavigatePopupToIllegalURL) {
5258 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
5259 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5260
5261 // Open a cross-site popup.
5262 GURL popup_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
nickadef4a52016-06-09 18:45:545263 Shell* popup = OpenPopup(shell(), popup_url, "foo");
alexmos5b50b6742016-03-17 20:38:055264 EXPECT_TRUE(popup);
5265 EXPECT_NE(popup->web_contents()->GetSiteInstance(),
5266 shell()->web_contents()->GetSiteInstance());
5267
Alex Moshchuk5f8671e2018-10-19 02:10:115268 ConsoleObserverDelegate console_delegate(
5269 web_contents(), "Not allowed to load local resource:*");
5270 web_contents()->SetDelegate(&console_delegate);
5271
5272 // From the opener, navigate the popup to a file:/// URL. This should result
5273 // in a console error and stay on the old page.
alexmos5b50b6742016-03-17 20:38:055274 GURL file_url("file:///");
nickadef4a52016-06-09 18:45:545275 NavigateNamedFrame(shell(), file_url, "foo");
alexmos5b50b6742016-03-17 20:38:055276 EXPECT_TRUE(WaitForLoadStop(popup->web_contents()));
Alex Moshchuk5f8671e2018-10-19 02:10:115277 EXPECT_EQ(popup_url, popup->web_contents()->GetLastCommittedURL());
5278 EXPECT_TRUE(base::MatchPattern(console_delegate.message(),
5279 "Not allowed to load local resource: file:*"));
alexmos5b50b6742016-03-17 20:38:055280
5281 // Now try the same test with a chrome:// URL.
5282 GURL chrome_url(std::string(kChromeUIScheme) + "://" +
5283 std::string(kChromeUIGpuHost));
nickadef4a52016-06-09 18:45:545284 NavigateNamedFrame(shell(), chrome_url, "foo");
alexmos5b50b6742016-03-17 20:38:055285 EXPECT_TRUE(WaitForLoadStop(popup->web_contents()));
Alex Moshchuk5f8671e2018-10-19 02:10:115286 EXPECT_EQ(popup_url, popup->web_contents()->GetLastCommittedURL());
5287 EXPECT_TRUE(
5288 base::MatchPattern(console_delegate.message(),
Matt Siembor76b485722019-05-04 01:17:325289 std::string("Not allowed to load local resource: ") +
5290 kChromeUIScheme + ":*"));
alexmos5b50b6742016-03-17 20:38:055291}
5292
alexmos646fec02015-07-25 00:11:495293// Verify that named frames are discoverable from their opener's ancestors.
5294// See https://crbug.com/511474.
5295IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
alexmosb0f73be2015-09-30 22:11:335296 DiscoverNamedFrameFromAncestorOfOpener) {
alexmos646fec02015-07-25 00:11:495297 GURL main_url(
5298 embedded_test_server()->GetURL("a.com", "/site_per_process_main.html"));
davidsac6e6c35e42016-11-21 19:45:575299 EXPECT_TRUE(NavigateToURL(shell(), main_url));
alexmos646fec02015-07-25 00:11:495300
5301 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:005302 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmos646fec02015-07-25 00:11:495303
5304 // Navigate first child cross-site.
5305 GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
5306 NavigateFrameToURL(root->child_at(0), frame_url);
5307
5308 // Open a popup named "foo" from the first child.
nickadef4a52016-06-09 18:45:545309 Shell* foo_shell =
5310 OpenPopup(root->child_at(0), GURL(url::kAboutBlankURL), "foo");
alexmos646fec02015-07-25 00:11:495311 EXPECT_TRUE(foo_shell);
5312
5313 // Check that a proxy was created for the "foo" popup in a.com.
5314 FrameTreeNode* foo_root =
5315 static_cast<WebContentsImpl*>(foo_shell->web_contents())
5316 ->GetFrameTree()
5317 ->root();
5318 SiteInstance* site_instance_a = root->current_frame_host()->GetSiteInstance();
5319 RenderFrameProxyHost* popup_rfph_for_a =
5320 foo_root->render_manager()->GetRenderFrameProxyHost(site_instance_a);
5321 EXPECT_TRUE(popup_rfph_for_a);
5322
5323 // Verify that the main frame can find the "foo" popup by name. If
5324 // window.open targets the correct frame, the "foo" popup's current URL
5325 // should be updated to |named_frame_url|.
5326 GURL named_frame_url(embedded_test_server()->GetURL("c.com", "/title2.html"));
nickadef4a52016-06-09 18:45:545327 NavigateNamedFrame(shell(), named_frame_url, "foo");
alexmos646fec02015-07-25 00:11:495328 EXPECT_TRUE(WaitForLoadStop(foo_shell->web_contents()));
5329 EXPECT_EQ(named_frame_url, foo_root->current_url());
5330
5331 // Navigate the popup cross-site and ensure it's still reachable via
5332 // window.open from the main frame.
5333 GURL d_url(embedded_test_server()->GetURL("d.com", "/title3.html"));
Alex Moshchuk7e26eca2018-03-03 01:34:295334 EXPECT_TRUE(NavigateToURLFromRenderer(foo_shell, d_url));
alexmos646fec02015-07-25 00:11:495335 EXPECT_EQ(d_url, foo_root->current_url());
nickadef4a52016-06-09 18:45:545336 NavigateNamedFrame(shell(), named_frame_url, "foo");
alexmos646fec02015-07-25 00:11:495337 EXPECT_TRUE(WaitForLoadStop(foo_shell->web_contents()));
5338 EXPECT_EQ(named_frame_url, foo_root->current_url());
5339}
5340
5341// Similar to DiscoverNamedFrameFromAncestorOfOpener, but check that if a
5342// window is created without a name and acquires window.name later, it will
5343// still be discoverable from its opener's ancestors. Also, instead of using
5344// an opener's ancestor, this test uses a popup with same origin as that
5345// ancestor. See https://crbug.com/511474.
5346IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
5347 DiscoverFrameAfterSettingWindowName) {
5348 GURL main_url(
5349 embedded_test_server()->GetURL("a.com", "/site_per_process_main.html"));
davidsac6e6c35e42016-11-21 19:45:575350 EXPECT_TRUE(NavigateToURL(shell(), main_url));
alexmos646fec02015-07-25 00:11:495351
5352 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:005353 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmos646fec02015-07-25 00:11:495354
5355 // Open a same-site popup from the main frame.
5356 GURL a_com_url(embedded_test_server()->GetURL("a.com", "/title3.html"));
nickadef4a52016-06-09 18:45:545357 Shell* a_com_shell = OpenPopup(root->child_at(0), a_com_url, "");
alexmos646fec02015-07-25 00:11:495358 EXPECT_TRUE(a_com_shell);
5359
5360 // Navigate first child on main frame cross-site.
5361 GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
5362 NavigateFrameToURL(root->child_at(0), frame_url);
5363
5364 // Open an unnamed popup from the first child frame.
nickadef4a52016-06-09 18:45:545365 Shell* foo_shell =
5366 OpenPopup(root->child_at(0), GURL(url::kAboutBlankURL), "");
alexmos646fec02015-07-25 00:11:495367 EXPECT_TRUE(foo_shell);
5368
5369 // There should be no proxy created for the "foo" popup in a.com, since
5370 // there's no way for the two a.com frames to access it yet.
5371 FrameTreeNode* foo_root =
5372 static_cast<WebContentsImpl*>(foo_shell->web_contents())
5373 ->GetFrameTree()
5374 ->root();
5375 SiteInstance* site_instance_a = root->current_frame_host()->GetSiteInstance();
5376 EXPECT_FALSE(
5377 foo_root->render_manager()->GetRenderFrameProxyHost(site_instance_a));
5378
5379 // Set window.name in the popup's frame.
nickadef4a52016-06-09 18:45:545380 EXPECT_TRUE(ExecuteScript(foo_shell, "window.name = 'foo'"));
alexmos646fec02015-07-25 00:11:495381
5382 // A proxy for the popup should now exist in a.com.
5383 EXPECT_TRUE(
5384 foo_root->render_manager()->GetRenderFrameProxyHost(site_instance_a));
5385
5386 // Verify that the a.com popup can now find the "foo" popup by name.
5387 GURL named_frame_url(embedded_test_server()->GetURL("c.com", "/title2.html"));
nickadef4a52016-06-09 18:45:545388 NavigateNamedFrame(a_com_shell, named_frame_url, "foo");
alexmos646fec02015-07-25 00:11:495389 EXPECT_TRUE(WaitForLoadStop(foo_shell->web_contents()));
5390 EXPECT_EQ(named_frame_url, foo_root->current_url());
5391}
5392
alexmos95733002015-08-24 16:38:095393// Check that frame opener updates work with subframes. Set up a window with a
5394// popup and update openers for the popup's main frame and subframe to
5395// subframes on first window, as follows:
5396//
5397// foo +---- bar
5398// / \ | / \ .
5399// bar foo <-+ bar foo
5400// ^ |
5401// +--------------------+
5402//
5403// The sites are carefully set up so that both opener updates are cross-process
5404// but still allowed by Blink's navigation checks.
5405IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, UpdateSubframeOpener) {
5406 GURL main_url = embedded_test_server()->GetURL(
5407 "foo.com", "/frame_tree/page_with_two_frames.html");
5408 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5409
ekaramadfd1b5cfa2016-04-19 00:35:005410 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmos95733002015-08-24 16:38:095411 EXPECT_EQ(2U, root->child_count());
5412
5413 // From the top frame, open a popup and navigate it to a cross-site page with
5414 // two subframes.
nickadef4a52016-06-09 18:45:545415 Shell* popup_shell = OpenPopup(shell(), GURL(url::kAboutBlankURL), "popup");
alexmos95733002015-08-24 16:38:095416 EXPECT_TRUE(popup_shell);
5417 GURL popup_url(embedded_test_server()->GetURL(
5418 "bar.com", "/frame_tree/page_with_post_message_frames.html"));
Alex Moshchuk7e26eca2018-03-03 01:34:295419 EXPECT_TRUE(NavigateToURLFromRenderer(popup_shell, popup_url));
alexmos95733002015-08-24 16:38:095420
5421 FrameTreeNode* popup_root =
5422 static_cast<WebContentsImpl*>(popup_shell->web_contents())
5423 ->GetFrameTree()
5424 ->root();
5425 EXPECT_EQ(2U, popup_root->child_count());
5426
5427 // Popup's opener should point to main frame to start with.
5428 EXPECT_EQ(root, popup_root->opener());
5429
5430 // Update the popup's opener to the second subframe on the main page (which
5431 // is same-origin with the top frame, i.e., foo.com).
Nick Carterb7e71312018-08-03 23:36:135432 EXPECT_EQ(true, EvalJs(root->child_at(1), "!!window.open('','popup');"));
alexmos95733002015-08-24 16:38:095433
5434 // Check that updated opener propagated to the browser process and the
5435 // popup's bar.com process.
5436 EXPECT_EQ(root->child_at(1), popup_root->opener());
5437
Nick Carterb7e71312018-08-03 23:36:135438 EXPECT_EQ(true,
5439 EvalJs(popup_shell,
5440 "window.opener === window.opener.parent.frames['frame2'];"));
alexmos95733002015-08-24 16:38:095441
5442 // Now update opener on the popup's second subframe (foo.com) to the main
5443 // page's first subframe (bar.com).
Nick Carterb7e71312018-08-03 23:36:135444 EXPECT_EQ(true, EvalJs(root->child_at(0), "!!window.open('','subframe2');"));
alexmos95733002015-08-24 16:38:095445
5446 // Check that updated opener propagated to the browser process and the
5447 // foo.com process.
5448 EXPECT_EQ(root->child_at(0), popup_root->child_at(1)->opener());
5449
Nick Carterb7e71312018-08-03 23:36:135450 EXPECT_EQ(true,
5451 EvalJs(popup_root->child_at(1),
5452 "window.opener === window.opener.parent.frames['frame1'];"));
alexmos95733002015-08-24 16:38:095453}
5454
alexmos90325cf2015-09-02 17:18:395455// Check that when a subframe navigates to a new SiteInstance, the new
5456// SiteInstance will get a proxy for the opener of subframe's parent. I.e.,
5457// accessing parent.opener from the subframe should still work after a
5458// cross-process navigation.
5459IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
5460 NavigatingSubframePreservesOpenerInParent) {
5461 GURL main_url = embedded_test_server()->GetURL("a.com", "/post_message.html");
5462 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5463
ekaramadfd1b5cfa2016-04-19 00:35:005464 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmos90325cf2015-09-02 17:18:395465
5466 // Open a popup with a cross-site page that has a subframe.
5467 GURL popup_url(embedded_test_server()->GetURL(
5468 "b.com", "/cross_site_iframe_factory.html?b(b)"));
nickadef4a52016-06-09 18:45:545469 Shell* popup_shell = OpenPopup(shell(), popup_url, "popup");
alexmos90325cf2015-09-02 17:18:395470 EXPECT_TRUE(popup_shell);
5471 FrameTreeNode* popup_root =
5472 static_cast<WebContentsImpl*>(popup_shell->web_contents())
5473 ->GetFrameTree()
5474 ->root();
5475 EXPECT_EQ(1U, popup_root->child_count());
5476
5477 // Check that the popup's opener is correct in the browser process.
5478 EXPECT_EQ(root, popup_root->opener());
5479
5480 // Navigate popup's subframe to another site.
5481 GURL frame_url(embedded_test_server()->GetURL("c.com", "/post_message.html"));
5482 NavigateFrameToURL(popup_root->child_at(0), frame_url);
alexmos90325cf2015-09-02 17:18:395483
5484 // Check that the new subframe process still sees correct opener for its
5485 // parent by sending a postMessage to subframe's parent.opener.
Nick Carterb7e71312018-08-03 23:36:135486 EXPECT_EQ(true, EvalJs(popup_root->child_at(0), "!!parent.opener;"));
alexmos90325cf2015-09-02 17:18:395487
5488 base::string16 expected_title = base::ASCIIToUTF16("msg");
5489 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
Nick Carterb7e71312018-08-03 23:36:135490 EXPECT_EQ(true, EvalJs(popup_root->child_at(0),
5491 "postToOpenerOfParent('msg','*');"));
alexmos90325cf2015-09-02 17:18:395492 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
5493}
5494
alexmosa181efc2015-09-03 00:39:045495// Check that if a subframe has an opener, that opener is preserved when the
5496// subframe navigates cross-site.
5497IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, NavigateSubframeWithOpener) {
5498 GURL main_url(embedded_test_server()->GetURL(
5499 "foo.com", "/frame_tree/page_with_two_frames.html"));
5500 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5501
ekaramadfd1b5cfa2016-04-19 00:35:005502 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmosa181efc2015-09-03 00:39:045503 EXPECT_EQ(
5504 " Site A ------------ proxies for B\n"
5505 " |--Site B ------- proxies for A\n"
5506 " +--Site A ------- proxies for B\n"
5507 "Where A = http://foo.com/\n"
5508 " B = http://bar.com/",
5509 DepictFrameTree(root));
5510
5511 // Update the first (cross-site) subframe's opener to root frame.
Nick Carterb7e71312018-08-03 23:36:135512 EXPECT_EQ(true, EvalJs(root, "!!window.open('','frame1');"));
alexmosa181efc2015-09-03 00:39:045513
5514 // Check that updated opener propagated to the browser process and subframe's
5515 // process.
5516 EXPECT_EQ(root, root->child_at(0)->opener());
5517
Nick Carterb7e71312018-08-03 23:36:135518 EXPECT_EQ(true,
5519 EvalJs(root->child_at(0), "window.opener === window.parent;"));
alexmosa181efc2015-09-03 00:39:045520
5521 // Navigate the subframe with opener to another site.
5522 GURL frame_url(embedded_test_server()->GetURL("baz.com", "/title1.html"));
5523 NavigateFrameToURL(root->child_at(0), frame_url);
5524
5525 // Check that the subframe still sees correct opener in its new process.
Nick Carterb7e71312018-08-03 23:36:135526 EXPECT_EQ(true,
5527 EvalJs(root->child_at(0), "window.opener === window.parent;"));
alexmosa181efc2015-09-03 00:39:045528
5529 // Navigate second subframe to a new site. Check that the proxy that's
5530 // created for the first subframe in the new SiteInstance has correct opener.
5531 GURL frame2_url(embedded_test_server()->GetURL("qux.com", "/title1.html"));
5532 NavigateFrameToURL(root->child_at(1), frame2_url);
5533
Nick Carterb7e71312018-08-03 23:36:135534 EXPECT_EQ(true, EvalJs(root->child_at(1),
5535 "parent.frames['frame1'].opener === parent;"));
alexmosa181efc2015-09-03 00:39:045536}
5537
5538// Check that if a subframe has an opener, that opener is preserved when a new
5539// RenderFrameProxy is created for that subframe in another renderer process.
5540// Similar to NavigateSubframeWithOpener, but this test verifies the subframe
rockot53be7caf2016-10-04 20:17:085541// opener plumbing for mojom::Renderer::CreateFrameProxy(), whereas
5542// NavigateSubframeWithOpener targets mojom::Renderer::CreateFrame().
alexmosa181efc2015-09-03 00:39:045543IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
5544 NewRenderFrameProxyPreservesOpener) {
5545 GURL main_url(
5546 embedded_test_server()->GetURL("foo.com", "/post_message.html"));
5547 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5548
ekaramadfd1b5cfa2016-04-19 00:35:005549 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmosa181efc2015-09-03 00:39:045550
5551 // Open a popup with a cross-site page that has two subframes.
5552 GURL popup_url(embedded_test_server()->GetURL(
5553 "bar.com", "/frame_tree/page_with_post_message_frames.html"));
nickadef4a52016-06-09 18:45:545554 Shell* popup_shell = OpenPopup(shell(), popup_url, "popup");
alexmosa181efc2015-09-03 00:39:045555 EXPECT_TRUE(popup_shell);
5556 FrameTreeNode* popup_root =
5557 static_cast<WebContentsImpl*>(popup_shell->web_contents())
5558 ->GetFrameTree()
5559 ->root();
5560 EXPECT_EQ(
5561 " Site A ------------ proxies for B\n"
5562 " |--Site A ------- proxies for B\n"
5563 " +--Site B ------- proxies for A\n"
5564 "Where A = http://bar.com/\n"
5565 " B = http://foo.com/",
5566 DepictFrameTree(popup_root));
5567
5568 // Update the popup's second subframe's opener to root frame. This is
5569 // allowed because that subframe is in the same foo.com SiteInstance as the
5570 // root frame.
Nick Carterb7e71312018-08-03 23:36:135571 EXPECT_EQ(true, EvalJs(root, "!!window.open('','subframe2');"));
alexmosa181efc2015-09-03 00:39:045572
5573 // Check that the opener update propagated to the browser process and bar.com
5574 // process.
5575 EXPECT_EQ(root, popup_root->child_at(1)->opener());
Nick Carterb7e71312018-08-03 23:36:135576 EXPECT_EQ(true,
5577 EvalJs(popup_root->child_at(0),
5578 "parent.frames['subframe2'].opener && "
5579 " parent.frames['subframe2'].opener === parent.opener;"));
alexmosa181efc2015-09-03 00:39:045580
5581 // Navigate the popup's first subframe to another site.
5582 GURL frame_url(
5583 embedded_test_server()->GetURL("baz.com", "/post_message.html"));
5584 NavigateFrameToURL(popup_root->child_at(0), frame_url);
alexmosa181efc2015-09-03 00:39:045585
5586 // Check that the second subframe's opener is still correct in the first
5587 // subframe's new process. Verify it both in JS and with a postMessage.
Nick Carterb7e71312018-08-03 23:36:135588 EXPECT_EQ(true,
5589 EvalJs(popup_root->child_at(0),
5590 "parent.frames['subframe2'].opener && "
5591 " parent.frames['subframe2'].opener === parent.opener;"));
alexmosa181efc2015-09-03 00:39:045592
5593 base::string16 expected_title = base::ASCIIToUTF16("msg");
5594 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
Nick Carterb7e71312018-08-03 23:36:135595 EXPECT_EQ(true, EvalJs(popup_root->child_at(0),
5596 "postToOpenerOfSibling('subframe2', 'msg', '*');"));
alexmosa181efc2015-09-03 00:39:045597 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
5598}
5599
alexmosb97d6bb62015-09-24 07:31:265600// Test for https://crbug.com/515302. Perform two navigations, A->B->A, and
alexmos9aa61232016-04-26 21:54:025601// drop the SwapOut ACK from the A->B navigation, so that the second B->A
alexmosb97d6bb62015-09-24 07:31:265602// navigation is initiated before the first page receives the SwapOut ACK.
alexmos9aa61232016-04-26 21:54:025603// Ensure that this doesn't crash and that the RVH(A) is not reused in that
5604// case.
Dominic Battre0abaec212018-06-05 10:22:425605#if defined(OS_MACOSX)
5606#define MAYBE_RenderViewHostIsNotReusedAfterDelayedSwapOutACK \
5607 DISABLED_RenderViewHostIsNotReusedAfterDelayedSwapOutACK
5608#else
5609#define MAYBE_RenderViewHostIsNotReusedAfterDelayedSwapOutACK \
5610 RenderViewHostIsNotReusedAfterDelayedSwapOutACK
5611#endif
alexmosb97d6bb62015-09-24 07:31:265612IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
Dominic Battre0abaec212018-06-05 10:22:425613 MAYBE_RenderViewHostIsNotReusedAfterDelayedSwapOutACK) {
alexmosb97d6bb62015-09-24 07:31:265614 GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
davidsac6e6c35e42016-11-21 19:45:575615 EXPECT_TRUE(NavigateToURL(shell(), a_url));
alexmosb97d6bb62015-09-24 07:31:265616
ekaramadfd1b5cfa2016-04-19 00:35:005617 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmosb97d6bb62015-09-24 07:31:265618 RenderFrameHostImpl* rfh = root->current_frame_host();
5619 RenderViewHostImpl* rvh = rfh->render_view_host();
creise73d58ef2016-03-29 22:12:155620 int rvh_routing_id = rvh->GetRoutingID();
Alex Moshchuk27caae82017-09-11 23:11:185621 int rvh_process_id = rvh->GetProcess()->GetID();
creise73d58ef2016-03-29 22:12:155622 SiteInstanceImpl* site_instance = rfh->GetSiteInstance();
alexmosb97d6bb62015-09-24 07:31:265623 RenderFrameDeletedObserver deleted_observer(rfh);
5624
5625 // Install a BrowserMessageFilter to drop SwapOut ACK messages in A's
5626 // process.
danakjcdee06c2019-02-08 18:04:075627 auto filter = base::MakeRefCounted<DropMessageFilter>(
5628 FrameMsgStart, FrameHostMsg_SwapOut_ACK::ID);
alexmosb97d6bb62015-09-24 07:31:265629 rfh->GetProcess()->AddFilter(filter.get());
creisecf91fe42016-04-11 18:49:045630 rfh->DisableSwapOutTimerForTesting();
alexmosb97d6bb62015-09-24 07:31:265631
alexmos9aa61232016-04-26 21:54:025632 // Navigate to B. This must wait for DidCommitProvisionalLoad and not
5633 // DidStopLoading, so that the SwapOut timer doesn't call OnSwappedOut and
5634 // destroy |rfh| and |rvh| before they are checked in the test.
alexmosb97d6bb62015-09-24 07:31:265635 GURL b_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
5636 TestFrameNavigationObserver commit_observer(root);
danakj824a7ff2019-02-07 20:34:025637 EXPECT_TRUE(ExecuteScript(shell(), JsReplace("location = $1", b_url)));
nasko8206fa12016-03-22 02:24:135638 commit_observer.WaitForCommit();
creise73d58ef2016-03-29 22:12:155639 EXPECT_FALSE(deleted_observer.deleted());
alexmosb97d6bb62015-09-24 07:31:265640
5641 // Since the SwapOut ACK for A->B is dropped, the first page's
alexmos9aa61232016-04-26 21:54:025642 // RenderFrameHost should be pending deletion after the last navigation.
nasko19736cc9f2016-04-08 22:38:455643 EXPECT_FALSE(rfh->is_active());
alexmosb97d6bb62015-09-24 07:31:265644
Arthur Sonzogni4973b392018-08-20 18:14:455645 // Without the SwapOut ACK and timer, the process A will never shutdown.
5646 // Simulate the process being killed now.
5647 content::RenderProcessHostWatcher crash_observer(
5648 rvh->GetProcess(),
5649 content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
5650 EXPECT_TRUE(rvh->GetProcess()->Shutdown(0));
5651 crash_observer.Wait();
alexmosb97d6bb62015-09-24 07:31:265652
alexmos9aa61232016-04-26 21:54:025653 // Verify that the RVH and RFH for A were cleaned up.
5654 EXPECT_FALSE(root->frame_tree()->GetRenderViewHost(site_instance));
5655 EXPECT_TRUE(deleted_observer.deleted());
5656
Alex Moshchuk7e26eca2018-03-03 01:34:295657 // Start a navigation back to A, being careful to stay in the same
5658 // BrowsingInstance, and check that the RenderViewHost wasn't reused.
alexmosb97d6bb62015-09-24 07:31:265659 TestNavigationObserver navigation_observer(shell()->web_contents());
Alex Moshchuk7e26eca2018-03-03 01:34:295660 shell()->LoadURLForFrame(a_url, std::string(),
5661 ui::PageTransitionFromInt(ui::PAGE_TRANSITION_LINK));
alexmosb97d6bb62015-09-24 07:31:265662 RenderViewHostImpl* pending_rvh =
clamy610c63b32017-12-22 15:05:185663 root->render_manager()->speculative_frame_host()->render_view_host();
creise73d58ef2016-03-29 22:12:155664 EXPECT_EQ(site_instance, pending_rvh->GetSiteInstance());
Alex Moshchuk27caae82017-09-11 23:11:185665 EXPECT_FALSE(rvh_routing_id == pending_rvh->GetRoutingID() &&
5666 rvh_process_id == pending_rvh->GetProcess()->GetID());
alexmosb97d6bb62015-09-24 07:31:265667
alexmosb97d6bb62015-09-24 07:31:265668 // Make sure the last navigation finishes without crashing.
5669 navigation_observer.Wait();
5670}
5671
creise73d58ef2016-03-29 22:12:155672// Test for https://crbug.com/591478, where navigating to a cross-site page with
alexmos9aa61232016-04-26 21:54:025673// a subframe on the old site caused a crash while trying to reuse the old
5674// RenderViewHost.
creise73d58ef2016-03-29 22:12:155675IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
alexmos9aa61232016-04-26 21:54:025676 ReusePendingDeleteRenderViewHostForSubframe) {
creise73d58ef2016-03-29 22:12:155677 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
5678 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5679
creise73d58ef2016-03-29 22:12:155680 std::string script =
5681 "window.onunload = function() { "
5682 " var start = Date.now();"
5683 " while (Date.now() - start < 1000);"
5684 "}";
nickadef4a52016-06-09 18:45:545685 EXPECT_TRUE(ExecuteScript(shell(), script));
creise73d58ef2016-03-29 22:12:155686
creis05a1cb72016-03-31 21:26:245687 // Navigating cross-site with an iframe to the original site shouldn't crash.
creise73d58ef2016-03-29 22:12:155688 GURL second_url(embedded_test_server()->GetURL(
5689 "b.com", "/cross_site_iframe_factory.html?b(a)"));
5690 EXPECT_TRUE(NavigateToURL(shell(), second_url));
5691
creis05a1cb72016-03-31 21:26:245692 // If the subframe is created while the main frame is pending deletion, then
alexmos9aa61232016-04-26 21:54:025693 // the RVH will be reused. The main frame should've been swapped with a
5694 // proxy despite being the last active frame in the progress (see
5695 // https://crbug.com/568836), and this proxy should also be reused by the new
5696 // page.
5697 //
5698 // TODO(creis, alexmos): Find a way to assert this that isn't flaky. For now,
creis05a1cb72016-03-31 21:26:245699 // the test is just likely (not certain) to catch regressions by crashing.
creise73d58ef2016-03-29 22:12:155700}
5701
alexmosca2c6ba2015-10-01 21:52:255702// Check that when a cross-process frame acquires focus, the old focused frame
5703// loses focus and fires blur events. Starting on a page with a cross-site
5704// subframe, simulate mouse clicks to switch focus from root frame to subframe
5705// and then back to root frame.
5706IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
5707 CrossProcessFocusChangeFiresBlurEvents) {
5708 GURL main_url(
5709 embedded_test_server()->GetURL("a.com", "/page_with_input_field.html"));
5710 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5711
ekaramadfd1b5cfa2016-04-19 00:35:005712 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmosca2c6ba2015-10-01 21:52:255713
5714 EXPECT_EQ(
5715 " Site A ------------ proxies for B\n"
5716 " +--Site B ------- proxies for A\n"
5717 "Where A = http://a.com/\n"
5718 " B = http://b.com/",
5719 DepictFrameTree(root));
5720
5721 // Focus the main frame's text field. The return value "input-focus"
5722 // indicates that the focus event was fired correctly.
5723 std::string result;
nickadef4a52016-06-09 18:45:545724 EXPECT_TRUE(
5725 ExecuteScriptAndExtractString(shell(), "focusInputField()", &result));
alexmosca2c6ba2015-10-01 21:52:255726 EXPECT_EQ(result, "input-focus");
5727
5728 // The main frame should be focused.
5729 EXPECT_EQ(root, root->frame_tree()->GetFocusedFrame());
5730
5731 DOMMessageQueue msg_queue;
5732
5733 // Click on the cross-process subframe.
alexmosb1dc2162015-11-05 00:59:205734 SimulateMouseClick(
5735 root->child_at(0)->current_frame_host()->GetRenderWidgetHost(), 1, 1);
alexmosca2c6ba2015-10-01 21:52:255736
5737 // Check that the main frame lost focus and fired blur event on the input
5738 // text field.
5739 std::string status;
5740 while (msg_queue.WaitForMessage(&status)) {
5741 if (status == "\"input-blur\"")
5742 break;
5743 }
5744
5745 // The subframe should now be focused.
5746 EXPECT_EQ(root->child_at(0), root->frame_tree()->GetFocusedFrame());
5747
5748 // Click on the root frame.
Ria Jiangabad8d9a2018-01-24 16:52:365749 SimulateMouseClick(shell()->web_contents()->GetRenderViewHost()->GetWidget(),
5750 1, 1);
alexmosca2c6ba2015-10-01 21:52:255751
5752 // Check that the subframe lost focus and fired blur event on its
5753 // document's body.
5754 while (msg_queue.WaitForMessage(&status)) {
5755 if (status == "\"document-blur\"")
5756 break;
5757 }
5758
5759 // The root frame should be focused again.
5760 EXPECT_EQ(root, root->frame_tree()->GetFocusedFrame());
5761}
5762
alexmosb1dc2162015-11-05 00:59:205763// Check that when a cross-process subframe is focused, its parent's
5764// document.activeElement correctly returns the corresponding <iframe> element.
5765// The test sets up an A-embed-B-embed-C page and shifts focus A->B->A->C,
5766// checking document.activeElement after each change.
5767IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, DocumentActiveElement) {
5768 GURL main_url(embedded_test_server()->GetURL(
5769 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
5770 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5771
ekaramadfd1b5cfa2016-04-19 00:35:005772 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmosb1dc2162015-11-05 00:59:205773
5774 EXPECT_EQ(
5775 " Site A ------------ proxies for B C\n"
5776 " +--Site B ------- proxies for A C\n"
5777 " +--Site C -- proxies for A B\n"
5778 "Where A = http://a.com/\n"
5779 " B = http://b.com/\n"
5780 " C = http://c.com/",
5781 DepictFrameTree(root));
5782
5783 FrameTreeNode* child = root->child_at(0);
5784 FrameTreeNode* grandchild = root->child_at(0)->child_at(0);
5785
5786 // The main frame should be focused to start with.
5787 EXPECT_EQ(root, root->frame_tree()->GetFocusedFrame());
5788
alexmos21acae52015-11-07 01:04:435789 // Focus the b.com frame.
5790 FocusFrame(child);
alexmosb1dc2162015-11-05 00:59:205791 EXPECT_EQ(child, root->frame_tree()->GetFocusedFrame());
5792
alexmos21acae52015-11-07 01:04:435793 // Helper function to check a property of document.activeElement in the
5794 // specified frame.
alexmosb1dc2162015-11-05 00:59:205795 auto verify_active_element_property = [](RenderFrameHost* rfh,
5796 const std::string& property,
5797 const std::string& expected_value) {
5798 std::string script = base::StringPrintf(
Nick Carterb7e71312018-08-03 23:36:135799 "document.activeElement.%s.toLowerCase();", property.c_str());
5800 EXPECT_EQ(expected_value, EvalJs(rfh, script));
alexmosb1dc2162015-11-05 00:59:205801 };
5802
alexmos21acae52015-11-07 01:04:435803 // Verify that document.activeElement on main frame points to the <iframe>
5804 // element for the b.com frame.
alexmosb1dc2162015-11-05 00:59:205805 RenderFrameHost* root_rfh = root->current_frame_host();
5806 verify_active_element_property(root_rfh, "tagName", "iframe");
5807 verify_active_element_property(root_rfh, "src", child->current_url().spec());
5808
alexmos21acae52015-11-07 01:04:435809 // Focus the a.com main frame again.
5810 FocusFrame(root);
alexmosb1dc2162015-11-05 00:59:205811 EXPECT_EQ(root, root->frame_tree()->GetFocusedFrame());
5812
5813 // Main frame document's <body> should now be the active element.
5814 verify_active_element_property(root_rfh, "tagName", "body");
5815
alexmos21acae52015-11-07 01:04:435816 // Now shift focus from main frame to c.com frame.
5817 FocusFrame(grandchild);
alexmosb1dc2162015-11-05 00:59:205818
5819 // Check document.activeElement in main frame. It should still point to
5820 // <iframe> for the b.com frame, since Blink computes the focused iframe
5821 // element by walking the parent chain of the focused frame until it hits the
5822 // current frame. This logic should still work with remote frames.
5823 verify_active_element_property(root_rfh, "tagName", "iframe");
5824 verify_active_element_property(root_rfh, "src", child->current_url().spec());
5825
5826 // Check document.activeElement in b.com subframe. It should point to
5827 // <iframe> for the c.com frame. This is a tricky case where B needs to find
5828 // out that focus changed from one remote frame to another (A to C).
5829 RenderFrameHost* child_rfh = child->current_frame_host();
5830 verify_active_element_property(child_rfh, "tagName", "iframe");
5831 verify_active_element_property(child_rfh, "src",
5832 grandchild->current_url().spec());
5833}
5834
alexmos5357efb2015-12-16 21:44:005835// Check that window.focus works for cross-process subframes.
5836IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, SubframeWindowFocus) {
5837 GURL main_url(embedded_test_server()->GetURL(
5838 "a.com", "/cross_site_iframe_factory.html?a(b,c)"));
5839 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5840
ekaramadfd1b5cfa2016-04-19 00:35:005841 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmos5357efb2015-12-16 21:44:005842
5843 EXPECT_EQ(
5844 " Site A ------------ proxies for B C\n"
5845 " |--Site B ------- proxies for A C\n"
5846 " +--Site C ------- proxies for A B\n"
5847 "Where A = http://a.com/\n"
5848 " B = http://b.com/\n"
5849 " C = http://c.com/",
5850 DepictFrameTree(root));
5851
5852 FrameTreeNode* child1 = root->child_at(0);
5853 FrameTreeNode* child2 = root->child_at(1);
5854
5855 // The main frame should be focused to start with.
5856 EXPECT_EQ(root, root->frame_tree()->GetFocusedFrame());
5857
alexmos5357efb2015-12-16 21:44:005858 // Register focus and blur events that will send messages when each frame's
Nick Carterb7e71312018-08-03 23:36:135859 // window gets or loses focus, and configure some utility functions useful for
5860 // waiting for these messages.
5861 const char kSetupFocusEvents[] = R"(
5862 window.addEventListener('focus', function() {
5863 window.top.postMessage('%s-got-focus', '*');
5864 });
5865 window.addEventListener('blur', function() {
5866 window.top.postMessage('%s-lost-focus', '*');
5867 });
5868 function onEvent(target, eventName, property, value) {
5869 return new Promise((resolve, reject) => {
5870 function listener(event) {
5871 if (event[property] == value) {
5872 resolve();
5873 target.removeEventListener(eventName, listener);
5874 }
5875 };
5876 target.addEventListener(eventName, listener);
5877 });
5878 }
5879 function expectMessages(messageList) {
5880 var promiseList = messageList.map(
5881 (dataValue) => onEvent(window, 'message', 'data', dataValue));
5882 return Promise.all(promiseList);
5883 }
5884 )";
alexmos5357efb2015-12-16 21:44:005885 std::string script = base::StringPrintf(kSetupFocusEvents, "main", "main");
lukaszac7d6bd32017-07-11 00:19:315886 ExecuteScriptAsync(shell(), script);
alexmos5357efb2015-12-16 21:44:005887 script = base::StringPrintf(kSetupFocusEvents, "child1", "child1");
lukaszac7d6bd32017-07-11 00:19:315888 ExecuteScriptAsync(child1, script);
alexmos5357efb2015-12-16 21:44:005889 script = base::StringPrintf(kSetupFocusEvents, "child2", "child2");
lukaszac7d6bd32017-07-11 00:19:315890 ExecuteScriptAsync(child2, script);
alexmos5357efb2015-12-16 21:44:005891
5892 // Execute window.focus on the B subframe from the A main frame.
alexmos5357efb2015-12-16 21:44:005893 // Process A should fire a blur event, and process B should fire a focus
5894 // event. Wait for both events.
Nick Carterb7e71312018-08-03 23:36:135895 EXPECT_EQ(true, EvalJs(root, R"((async function() {
5896 allMessages = [];
5897 window.addEventListener('message', (event) => {
5898 allMessages.push(event.data);
5899 });
alexmos5357efb2015-12-16 21:44:005900
Nick Carterb7e71312018-08-03 23:36:135901 var messages = expectMessages(['main-lost-focus', 'child1-got-focus']);
5902 frames[0].focus();
5903 await messages;
5904
5905 return allMessages.length == 2 || allMessages;
5906 })())"));
5907
alexmos5357efb2015-12-16 21:44:005908 EXPECT_EQ(child1, root->frame_tree()->GetFocusedFrame());
5909
5910 // Now, execute window.focus on the C subframe from A main frame. This
5911 // checks that we can shift focus from one remote frame to another.
Nick Carterb7e71312018-08-03 23:36:135912 //
alexmos5357efb2015-12-16 21:44:005913 // Wait for the two subframes (B and C) to fire blur and focus events.
Nick Carterb7e71312018-08-03 23:36:135914 EXPECT_EQ(true, EvalJs(root, R"((async function() {
5915 var messages = expectMessages(['child1-lost-focus', 'child2-got-focus']);
5916 frames[1].focus();
5917 await messages;
5918 return allMessages.length == 4 || allMessages;
5919 })())"));
alexmos5357efb2015-12-16 21:44:005920
5921 // The C subframe should now be focused.
5922 EXPECT_EQ(child2, root->frame_tree()->GetFocusedFrame());
5923
Nick Carterb7e71312018-08-03 23:36:135924 // Install event listeners in the A main frame, expecting the main frame to
5925 // obtain focus.
5926 EXPECT_TRUE(
5927 ExecJs(root,
5928 "var messages = "
5929 " expectMessages(['child2-lost-focus', 'main-got-focus']);"));
5930
alexmos5357efb2015-12-16 21:44:005931 // window.focus the main frame from the C subframe.
lukaszac7d6bd32017-07-11 00:19:315932 ExecuteScriptAsync(child2, "parent.focus()");
alexmos5357efb2015-12-16 21:44:005933
Nick Carterb7e71312018-08-03 23:36:135934 // Wait for the messages to arrive in the A main frame.
5935 EXPECT_EQ(true, EvalJs(root, R"((async function() {
5936 await messages;
5937 return allMessages.length == 6 || allMessages;
5938 })())"));
alexmos5357efb2015-12-16 21:44:005939
5940 // The main frame should now be focused.
5941 EXPECT_EQ(root, root->frame_tree()->GetFocusedFrame());
5942}
5943
Alex Moshchukf1d9af02018-01-18 00:54:245944// Check that when a subframe has focus, and another subframe navigates
5945// cross-site to a new renderer process, this doesn't reset the focused frame
5946// to the main frame. See https://crbug.com/802156.
5947IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
5948 SubframeFocusNotLostWhenAnotherFrameNavigatesCrossSite) {
5949 GURL main_url(embedded_test_server()->GetURL(
5950 "a.com", "/cross_site_iframe_factory.html?a(a,a)"));
5951 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5952
5953 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
5954 FrameTreeNode* child1 = root->child_at(0);
5955 FrameTreeNode* child2 = root->child_at(1);
5956
5957 // The main frame should be focused to start with.
5958 EXPECT_EQ(root, root->frame_tree()->GetFocusedFrame());
5959
5960 // Add an <input> element to the first subframe.
5961 ExecuteScriptAsync(
5962 child1, "document.body.appendChild(document.createElement('input'))");
5963
5964 // Focus the first subframe using window.focus().
5965 FrameFocusedObserver focus_observer(child1->current_frame_host());
5966 ExecuteScriptAsync(root, "frames[0].focus()");
5967 focus_observer.Wait();
5968 EXPECT_EQ(child1, root->frame_tree()->GetFocusedFrame());
5969
5970 // Give focus to the <input> element in the first subframe.
5971 ExecuteScriptAsync(child1, "document.querySelector('input').focus()");
5972
5973 // Now, navigate second subframe cross-site. Ensure that this won't change
5974 // the focused frame.
5975 GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
5976 NavigateFrameToURL(child2, b_url);
5977 // This is needed because the incorrect focused frame change as in
5978 // https://crbug.com/802156 requires an additional post-commit IPC roundtrip.
5979 base::RunLoop().RunUntilIdle();
5980 EXPECT_EQ(child1, root->frame_tree()->GetFocusedFrame());
5981
5982 // The <input> in first subframe should still be the activeElement.
5983 std::string activeTag;
5984 EXPECT_TRUE(ExecuteScriptAndExtractString(
5985 child1, "domAutomationController.send(document.activeElement.tagName)",
5986 &activeTag));
5987 EXPECT_EQ("input", base::ToLowerASCII(activeTag));
5988}
5989
lfg3faeb652015-10-21 16:34:385990// Tests that we are using the correct RenderFrameProxy when navigating an
5991// opener window.
5992IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, OpenerSetLocation) {
5993 // Navigate the main window.
5994 GURL main_url(embedded_test_server()->GetURL("/title1.html"));
5995 EXPECT_TRUE(NavigateToURL(shell(), main_url));
5996 EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), main_url);
5997
5998 // Load cross-site page into a new window.
5999 GURL cross_url = embedded_test_server()->GetURL("foo.com", "/title1.html");
nickadef4a52016-06-09 18:45:546000 Shell* popup = OpenPopup(shell(), cross_url, "");
lfg3faeb652015-10-21 16:34:386001 EXPECT_EQ(popup->web_contents()->GetLastCommittedURL(), cross_url);
6002
6003 // Use new window to navigate main window.
danakj824a7ff2019-02-07 20:34:026004 EXPECT_TRUE(ExecuteScript(
6005 popup, JsReplace("window.opener.location.href = $1", cross_url)));
lfg3faeb652015-10-21 16:34:386006 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
6007 EXPECT_EQ(shell()->web_contents()->GetLastCommittedURL(), cross_url);
6008}
6009
alexmosba1fb7152015-12-12 07:20:306010// Test for https://crbug.com/526304, where a parent frame executes a
6011// remote-to-local navigation on a child frame and immediately removes the same
6012// child frame. This test exercises the path where the detach happens before
6013// the provisional local frame is created.
6014IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
6015 NavigateProxyAndDetachBeforeProvisionalFrameCreation) {
6016 GURL main_url(embedded_test_server()->GetURL(
6017 "a.com", "/cross_site_iframe_factory.html?a(b,b)"));
6018 EXPECT_TRUE(NavigateToURL(shell(), main_url));
6019
6020 WebContents* contents = shell()->web_contents();
6021 FrameTreeNode* root =
6022 static_cast<WebContentsImpl*>(contents)->GetFrameTree()->root();
6023 EXPECT_EQ(2U, root->child_count());
6024
6025 // Navigate the first child frame to 'about:blank' (which is a
6026 // remote-to-local transition), and then detach it.
Ria Jiang79f58ed922018-04-10 03:30:526027 FrameDeletedObserver observer(root->child_at(0)->current_frame_host());
alexmosba1fb7152015-12-12 07:20:306028 std::string script =
6029 "var f = document.querySelector('iframe');"
6030 "f.contentWindow.location.href = 'about:blank';"
6031 "setTimeout(function() { document.body.removeChild(f); }, 0);";
nickadef4a52016-06-09 18:45:546032 EXPECT_TRUE(ExecuteScript(root, script));
alexmosba1fb7152015-12-12 07:20:306033 observer.Wait();
6034 EXPECT_EQ(1U, root->child_count());
6035
6036 // Make sure the main frame renderer does not crash and ignores the
6037 // navigation to the frame that's already been deleted.
Nick Carterb7e71312018-08-03 23:36:136038 EXPECT_EQ(1, EvalJs(root, "frames.length"));
alexmosba1fb7152015-12-12 07:20:306039}
6040
6041// Test for a variation of https://crbug.com/526304, where a child frame does a
6042// remote-to-local navigation, and the parent frame removes that child frame
6043// after the provisional local frame is created and starts to navigate, but
6044// before it commits.
6045IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
6046 NavigateProxyAndDetachBeforeCommit) {
6047 GURL main_url(embedded_test_server()->GetURL(
6048 "a.com", "/cross_site_iframe_factory.html?a(b,b)"));
6049 EXPECT_TRUE(NavigateToURL(shell(), main_url));
6050
6051 WebContents* contents = shell()->web_contents();
6052 FrameTreeNode* root =
6053 static_cast<WebContentsImpl*>(contents)->GetFrameTree()->root();
6054 EXPECT_EQ(2U, root->child_count());
6055 FrameTreeNode* child = root->child_at(0);
6056
6057 // Start a remote-to-local navigation for the child, but don't wait for
6058 // commit.
6059 GURL same_site_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
6060 NavigationController::LoadURLParams params(same_site_url);
6061 params.transition_type = ui::PAGE_TRANSITION_LINK;
6062 params.frame_tree_node_id = child->frame_tree_node_id();
6063 child->navigator()->GetController()->LoadURLWithParams(params);
6064
6065 // Tell parent to remove the first child. This should happen after the
6066 // previous navigation starts but before it commits.
Ria Jiang79f58ed922018-04-10 03:30:526067 FrameDeletedObserver observer(child->current_frame_host());
alexmosba1fb7152015-12-12 07:20:306068 EXPECT_TRUE(ExecuteScript(
nickadef4a52016-06-09 18:45:546069 root, "document.body.removeChild(document.querySelector('iframe'));"));
alexmosba1fb7152015-12-12 07:20:306070 observer.Wait();
6071 EXPECT_EQ(1U, root->child_count());
6072
6073 // Make sure the a.com renderer does not crash.
Nick Carterb7e71312018-08-03 23:36:136074 EXPECT_EQ(1, EvalJs(root, "frames.length;"));
alexmosba1fb7152015-12-12 07:20:306075}
6076
nasko13b8e772016-03-03 19:41:356077// Similar to NavigateProxyAndDetachBeforeCommit, but uses a synchronous
6078// navigation to about:blank and the parent removes the child frame in a load
6079// event handler for the subframe.
alexmosf076d912017-01-23 22:27:576080IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, NavigateAboutBlankAndDetach) {
nasko13b8e772016-03-03 19:41:356081 GURL main_url(
6082 embedded_test_server()->GetURL("a.com", "/remove_frame_on_load.html"));
6083 EXPECT_TRUE(NavigateToURL(shell(), main_url));
6084
6085 WebContents* contents = shell()->web_contents();
6086 FrameTreeNode* root =
6087 static_cast<WebContentsImpl*>(contents)->GetFrameTree()->root();
6088 EXPECT_EQ(1U, root->child_count());
6089 FrameTreeNode* child = root->child_at(0);
6090 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
6091 child->current_frame_host()->GetSiteInstance());
6092
alexmosf076d912017-01-23 22:27:576093 // Navigate the child frame to "about:blank" from the parent document and
6094 // wait for it to be removed.
Ria Jiang79f58ed922018-04-10 03:30:526095 FrameDeletedObserver observer(child->current_frame_host());
nickadef4a52016-06-09 18:45:546096 EXPECT_TRUE(ExecuteScript(
6097 root, base::StringPrintf("f.src = '%s'", url::kAboutBlankURL)));
nasko13b8e772016-03-03 19:41:356098 observer.Wait();
6099
6100 // Make sure the a.com renderer does not crash and the frame is removed.
Nick Carterb7e71312018-08-03 23:36:136101 EXPECT_EQ(0, EvalJs(root, "frames.length;"));
nasko13b8e772016-03-03 19:41:356102}
6103
alexmose846e412015-12-18 07:46:456104// Test for https://crbug.com/568670. In A-embed-B, simultaneously have B
6105// create a new (local) child frame, and have A detach B's proxy. The child
6106// frame creation sends an IPC to create a new proxy in A's process, and if
6107// that IPC arrives after the detach, the new frame's parent (a proxy) won't be
6108// available, and this shouldn't cause RenderFrameProxy::CreateFrameProxy to
6109// crash.
6110IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
6111 RaceBetweenCreateChildFrameAndDetachParentProxy) {
6112 GURL main_url(embedded_test_server()->GetURL(
6113 "a.com", "/cross_site_iframe_factory.html?a(b)"));
6114 EXPECT_TRUE(NavigateToURL(shell(), main_url));
6115
6116 WebContents* contents = shell()->web_contents();
6117 FrameTreeNode* root =
6118 static_cast<WebContentsImpl*>(contents)->GetFrameTree()->root();
6119
6120 // Simulate subframe B creating a new child frame in parallel to main frame A
6121 // detaching subframe B. We can't use ExecuteScript in both A and B to do
6122 // this simultaneously, as that won't guarantee the timing that we want.
6123 // Instead, tell A to detach B and then send a fake proxy creation IPC to A
6124 // that would've come from create-child-frame code in B. Prepare parameters
6125 // for that IPC ahead of the detach, while B's FrameTreeNode still exists.
6126 SiteInstance* site_instance_a = root->current_frame_host()->GetSiteInstance();
6127 RenderProcessHost* process_a =
6128 root->render_manager()->current_frame_host()->GetProcess();
6129 int new_routing_id = process_a->GetNextRoutingID();
6130 int view_routing_id =
6131 root->frame_tree()->GetRenderViewHost(site_instance_a)->GetRoutingID();
6132 int parent_routing_id =
6133 root->child_at(0)->render_manager()->GetProxyToParent()->GetRoutingID();
6134
6135 // Tell main frame A to delete its subframe B.
Ria Jiang79f58ed922018-04-10 03:30:526136 FrameDeletedObserver observer(root->child_at(0)->current_frame_host());
alexmose846e412015-12-18 07:46:456137 EXPECT_TRUE(ExecuteScript(
nickadef4a52016-06-09 18:45:546138 root, "document.body.removeChild(document.querySelector('iframe'));"));
alexmose846e412015-12-18 07:46:456139
6140 // Send the message to create a proxy for B's new child frame in A. This
6141 // used to crash, as parent_routing_id refers to a proxy that doesn't exist
6142 // anymore.
rockotee0368982016-10-16 22:44:326143 process_a->GetRendererInterface()->CreateFrameProxy(
alexmose846e412015-12-18 07:46:456144 new_routing_id, view_routing_id, MSG_ROUTING_NONE, parent_routing_id,
Dmitry Gozman89361212018-02-13 16:10:446145 FrameReplicationState(), base::UnguessableToken::Create());
alexmose846e412015-12-18 07:46:456146
6147 // Ensure the subframe is detached in the browser process.
6148 observer.Wait();
6149 EXPECT_EQ(0U, root->child_count());
6150
6151 // Make sure process A did not crash.
Nick Carterb7e71312018-08-03 23:36:136152 EXPECT_EQ(0, EvalJs(root, "frames.length;"));
alexmose846e412015-12-18 07:46:456153}
6154
naskoeab5c5582015-12-15 05:20:006155// This test ensures that the RenderFrame isn't leaked in the renderer process
6156// if a pending cross-process navigation is cancelled. The test works by trying
6157// to create a new RenderFrame with the same routing id. If there is an
6158// entry with the same routing ID, a CHECK is hit and the process crashes.
6159IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
6160 SubframePendingAndBackToSameSiteInstance) {
6161 GURL main_url(embedded_test_server()->GetURL(
6162 "a.com", "/cross_site_iframe_factory.html?a(b)"));
davidsac6e6c35e42016-11-21 19:45:576163 EXPECT_TRUE(NavigateToURL(shell(), main_url));
naskoeab5c5582015-12-15 05:20:006164
6165 // Capture the FrameTreeNode this test will be navigating.
ekaramadfd1b5cfa2016-04-19 00:35:006166 FrameTreeNode* node = web_contents()->GetFrameTree()->root()->child_at(0);
naskoeab5c5582015-12-15 05:20:006167 EXPECT_TRUE(node);
6168 EXPECT_NE(node->current_frame_host()->GetSiteInstance(),
6169 node->parent()->current_frame_host()->GetSiteInstance());
6170
6171 // Navigate to the site of the parent, but to a page that will not commit.
6172 GURL same_site_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
clamy0ffcb7d2018-09-17 13:24:246173 TestNavigationManager stalled_navigation(web_contents(), same_site_url);
naskoeab5c5582015-12-15 05:20:006174 {
6175 NavigationController::LoadURLParams params(same_site_url);
6176 params.transition_type = ui::PAGE_TRANSITION_LINK;
6177 params.frame_tree_node_id = node->frame_tree_node_id();
6178 node->navigator()->GetController()->LoadURLWithParams(params);
clamy0ffcb7d2018-09-17 13:24:246179 EXPECT_TRUE(stalled_navigation.WaitForResponse());
naskoeab5c5582015-12-15 05:20:006180 }
6181
6182 // Grab the routing id of the pending RenderFrameHost and set up a process
6183 // observer to ensure there is no crash when a new RenderFrame creation is
6184 // attempted.
6185 RenderProcessHost* process =
clamy610c63b32017-12-22 15:05:186186 node->render_manager()->speculative_frame_host()->GetProcess();
naskoeab5c5582015-12-15 05:20:006187 RenderProcessHostWatcher watcher(
6188 process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
6189 int frame_routing_id =
clamy610c63b32017-12-22 15:05:186190 node->render_manager()->speculative_frame_host()->GetRoutingID();
arthursonzogni1efc7b962019-03-14 09:36:336191 int previous_routing_id =
naskoeab5c5582015-12-15 05:20:006192 node->render_manager()->GetProxyToParent()->GetRoutingID();
6193
6194 // Now go to c.com so the navigation to a.com is cancelled and send an IPC
6195 // to create a new RenderFrame with the routing id of the previously pending
6196 // one.
6197 NavigateFrameToURL(node,
6198 embedded_test_server()->GetURL("c.com", "/title2.html"));
6199 {
rockot53be7caf2016-10-04 20:17:086200 mojom::CreateFrameParamsPtr params = mojom::CreateFrameParams::New();
6201 params->routing_id = frame_routing_id;
Oksana Zhuravlova8b88e572019-01-07 21:54:006202 params->interface_bundle = mojom::DocumentScopedInterfaceBundle::New();
6203 mojo::MakeRequest(&params->interface_bundle->interface_provider);
6204 mojo::MakeRequest(
6205 &params->interface_bundle->document_interface_broker_content);
6206 mojo::MakeRequest(
6207 &params->interface_bundle->document_interface_broker_blink);
arthursonzogni1efc7b962019-03-14 09:36:336208 params->previous_routing_id = previous_routing_id;
rockot53be7caf2016-10-04 20:17:086209 params->opener_routing_id = IPC::mojom::kRoutingIdNone;
6210 params->parent_routing_id =
naskoeab5c5582015-12-15 05:20:006211 shell()->web_contents()->GetMainFrame()->GetRoutingID();
rockot53be7caf2016-10-04 20:17:086212 params->previous_sibling_routing_id = IPC::mojom::kRoutingIdNone;
6213 params->widget_params = mojom::CreateFrameWidgetParams::New();
6214 params->widget_params->routing_id = IPC::mojom::kRoutingIdNone;
6215 params->widget_params->hidden = true;
6216 params->replication_state.name = "name";
6217 params->replication_state.unique_name = "name";
Pavel Feldman25234722017-10-11 02:49:066218 params->devtools_frame_token = base::UnguessableToken::Create();
rockotee0368982016-10-16 22:44:326219 process->GetRendererInterface()->CreateFrame(std::move(params));
naskoeab5c5582015-12-15 05:20:006220 }
6221
6222 // The test must wait for the process to exit, but if there is no leak, the
6223 // RenderFrame will be properly created and there will be no crash.
6224 // Therefore, navigate the main frame to completely different site, which
6225 // will cause the original process to exit cleanly.
6226 EXPECT_TRUE(NavigateToURL(
6227 shell(), embedded_test_server()->GetURL("d.com", "/title3.html")));
6228 watcher.Wait();
6229 EXPECT_TRUE(watcher.did_exit_normally());
naskoeab5c5582015-12-15 05:20:006230}
6231
6232// This test ensures that the RenderFrame isn't leaked in the renderer process
6233// when a remote parent detaches a child frame. The test works by trying
6234// to create a new RenderFrame with the same routing id. If there is an
6235// entry with the same routing ID, a CHECK is hit and the process crashes.
6236IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, ParentDetachRemoteChild) {
6237 GURL main_url(embedded_test_server()->GetURL(
6238 "a.com", "/cross_site_iframe_factory.html?a(b,b)"));
davidsac6e6c35e42016-11-21 19:45:576239 EXPECT_TRUE(NavigateToURL(shell(), main_url));
naskoeab5c5582015-12-15 05:20:006240
ekaramadfd1b5cfa2016-04-19 00:35:006241 WebContentsImpl* contents = web_contents();
6242 EXPECT_EQ(2U, contents->GetFrameTree()->root()->child_count());
naskoeab5c5582015-12-15 05:20:006243
6244 // Capture the FrameTreeNode this test will be navigating.
ekaramadfd1b5cfa2016-04-19 00:35:006245 FrameTreeNode* node = contents->GetFrameTree()->root()->child_at(0);
naskoeab5c5582015-12-15 05:20:006246 EXPECT_TRUE(node);
6247 EXPECT_NE(node->current_frame_host()->GetSiteInstance(),
6248 node->parent()->current_frame_host()->GetSiteInstance());
6249
6250 // Grab the routing id of the first child RenderFrameHost and set up a process
6251 // observer to ensure there is no crash when a new RenderFrame creation is
6252 // attempted.
6253 RenderProcessHost* process = node->current_frame_host()->GetProcess();
6254 RenderProcessHostWatcher watcher(
6255 process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
6256 int frame_routing_id = node->current_frame_host()->GetRoutingID();
6257 int widget_routing_id =
6258 node->current_frame_host()->GetRenderWidgetHost()->GetRoutingID();
6259 int parent_routing_id =
6260 node->parent()->render_manager()->GetRoutingIdForSiteInstance(
6261 node->current_frame_host()->GetSiteInstance());
6262
6263 // Have the parent frame remove the child frame from its DOM. This should
6264 // result in the child RenderFrame being deleted in the remote process.
ekaramadfd1b5cfa2016-04-19 00:35:006265 EXPECT_TRUE(ExecuteScript(contents,
naskoeab5c5582015-12-15 05:20:006266 "document.body.removeChild("
6267 "document.querySelectorAll('iframe')[0])"));
ekaramadfd1b5cfa2016-04-19 00:35:006268 EXPECT_EQ(1U, contents->GetFrameTree()->root()->child_count());
naskoeab5c5582015-12-15 05:20:006269
6270 {
rockot53be7caf2016-10-04 20:17:086271 mojom::CreateFrameParamsPtr params = mojom::CreateFrameParams::New();
6272 params->routing_id = frame_routing_id;
Oksana Zhuravlova8b88e572019-01-07 21:54:006273 params->interface_bundle = mojom::DocumentScopedInterfaceBundle::New();
6274 mojo::MakeRequest(&params->interface_bundle->interface_provider);
6275 mojo::MakeRequest(
6276 &params->interface_bundle->document_interface_broker_content);
6277 mojo::MakeRequest(
6278 &params->interface_bundle->document_interface_broker_blink);
arthursonzogni1efc7b962019-03-14 09:36:336279 params->previous_routing_id = IPC::mojom::kRoutingIdNone;
rockot53be7caf2016-10-04 20:17:086280 params->opener_routing_id = IPC::mojom::kRoutingIdNone;
6281 params->parent_routing_id = parent_routing_id;
6282 params->previous_sibling_routing_id = IPC::mojom::kRoutingIdNone;
6283 params->widget_params = mojom::CreateFrameWidgetParams::New();
6284 params->widget_params->routing_id = widget_routing_id;
6285 params->widget_params->hidden = true;
6286 params->replication_state.name = "name";
6287 params->replication_state.unique_name = "name";
Pavel Feldman25234722017-10-11 02:49:066288 params->devtools_frame_token = base::UnguessableToken::Create();
rockotee0368982016-10-16 22:44:326289 process->GetRendererInterface()->CreateFrame(std::move(params));
naskoeab5c5582015-12-15 05:20:006290 }
6291
6292 // The test must wait for the process to exit, but if there is no leak, the
6293 // RenderFrame will be properly created and there will be no crash.
6294 // Therefore, navigate the remaining subframe to completely different site,
6295 // which will cause the original process to exit cleanly.
ekaramadfd1b5cfa2016-04-19 00:35:006296 NavigateFrameToURL(contents->GetFrameTree()->root()->child_at(0),
6297 embedded_test_server()->GetURL("d.com", "/title3.html"));
naskoeab5c5582015-12-15 05:20:006298 watcher.Wait();
6299 EXPECT_TRUE(watcher.did_exit_normally());
6300}
6301
EhsanK2075c7e2017-08-21 02:42:396302// TODO(ekaramad): Move this test out of this file when addressing
6303// https://crbug.com/754726.
6304// This test verifies that RFHImpl::ForEachImmediateLocalRoot works as expected.
6305// The frame tree used in the test is:
6306// A0
6307// / | \
6308// A1 B1 A2
6309// / \ | / \
6310// B2 A3 B3 A4 C2
6311// / / / \ \
6312// D1 D2 C3 C4 C5
6313//
6314// As an example, the expected set of immediate local roots for the root node A0
6315// should be {B1, B2, C2, D2, C5}. Note that the order is compatible with that
6316// of a BFS traversal from root node A0.
6317IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, FindImmediateLocalRoots) {
6318 GURL main_url(embedded_test_server()->GetURL(
6319 "a.com",
6320 "/cross_site_iframe_factory.html?a(a(b(d),a(d)),b(b(c,c)),a(a(c),c))"));
ekaramadbabb9bf2016-01-12 15:17:026321 EXPECT_TRUE(NavigateToURL(shell(), main_url));
ekaramadbabb9bf2016-01-12 15:17:026322
EhsanK2075c7e2017-08-21 02:42:396323 // Each entry is of the frame "LABEL:ILR1ILR2..." where ILR stands for
6324 // immediate local root.
6325 std::string immediate_local_roots[] = {
6326 "A0:B1B2C2D2C5", "A1:B2D2", "B1:C3C4", "A2:C2C5", "B2:D1",
6327 "A3:D2", "B3:C3C4", "A4:C5", "C2:", "D1:",
6328 "D2:", "C3:", "C4:", "C5:"};
ekaramadbabb9bf2016-01-12 15:17:026329
EhsanK2075c7e2017-08-21 02:42:396330 std::map<RenderFrameHostImpl*, std::string>
6331 frame_to_immediate_local_roots_map;
6332 std::map<RenderFrameHostImpl*, std::string> frame_to_label_map;
6333 size_t index = 0;
6334 // Map each RenderFrameHostImpl to its label and set of immediate local roots.
6335 for (auto* ftn : web_contents()->GetFrameTree()->Nodes()) {
6336 std::string roots = immediate_local_roots[index++];
6337 frame_to_immediate_local_roots_map[ftn->current_frame_host()] = roots;
6338 frame_to_label_map[ftn->current_frame_host()] = roots.substr(0, 2);
6339 }
ekaramadbabb9bf2016-01-12 15:17:026340
EhsanK2075c7e2017-08-21 02:42:396341 // For each frame in the tree, verify that ForEachImmediateLocalRoot properly
6342 // visits each and only each immediate local root in a BFS traversal order.
6343 for (auto* ftn : web_contents()->GetFrameTree()->Nodes()) {
6344 RenderFrameHostImpl* current_frame_host = ftn->current_frame_host();
6345 std::list<RenderFrameHostImpl*> frame_list;
6346 current_frame_host->ForEachImmediateLocalRoot(
6347 base::Bind([](std::list<RenderFrameHostImpl*>* ilr_list,
6348 RenderFrameHostImpl* rfh) { ilr_list->push_back(rfh); },
6349 &frame_list));
ekaramadbabb9bf2016-01-12 15:17:026350
EhsanK2075c7e2017-08-21 02:42:396351 std::string result = frame_to_label_map[current_frame_host];
6352 result.append(":");
6353 for (auto* ilr_ptr : frame_list)
6354 result.append(frame_to_label_map[ilr_ptr]);
6355 EXPECT_EQ(frame_to_immediate_local_roots_map[current_frame_host], result);
6356 }
6357}
ekaramadbabb9bf2016-01-12 15:17:026358
EhsanK2075c7e2017-08-21 02:42:396359// This test verifies that changing the CSS visibility of a cross-origin
6360// <iframe> is forwarded to its corresponding RenderWidgetHost and all other
6361// RenderWidgetHosts corresponding to the nested cross-origin frame.
6362IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CSSVisibilityChanged) {
6363 GURL main_url(embedded_test_server()->GetURL(
6364 "a.com", "/cross_site_iframe_factory.html?a(b(b(c(d(d(a))))))"));
6365 EXPECT_TRUE(NavigateToURL(shell(), main_url));
6366
6367 // Find all child RenderWidgetHosts.
6368 std::vector<RenderWidgetHostImpl*> child_widget_hosts;
6369 FrameTreeNode* first_cross_process_child =
6370 web_contents()->GetFrameTree()->root()->child_at(0);
6371 for (auto* ftn : web_contents()->GetFrameTree()->SubtreeNodes(
6372 first_cross_process_child)) {
6373 RenderFrameHostImpl* frame_host = ftn->current_frame_host();
6374 if (!frame_host->is_local_root())
6375 continue;
6376
6377 child_widget_hosts.push_back(frame_host->GetRenderWidgetHost());
6378 }
6379
6380 // Ignoring the root, there is exactly 4 local roots and hence 5
6381 // RenderWidgetHosts on the page.
6382 EXPECT_EQ(4U, child_widget_hosts.size());
6383
6384 // Initially all the RenderWidgetHosts should be visible.
6385 for (size_t index = 0; index < child_widget_hosts.size(); ++index) {
6386 EXPECT_FALSE(child_widget_hosts[index]->is_hidden())
6387 << "The RWH at distance " << index + 1U
6388 << " from root RWH should not be hidden.";
6389 }
ekaramadbabb9bf2016-01-12 15:17:026390
6391 std::string show_script =
6392 "document.querySelector('iframe').style.visibility = 'visible';";
6393 std::string hide_script =
6394 "document.querySelector('iframe').style.visibility = 'hidden';";
6395
EhsanK2075c7e2017-08-21 02:42:396396 // Define observers for notifications about hiding child RenderWidgetHosts.
6397 std::vector<std::unique_ptr<RenderWidgetHostVisibilityObserver>>
6398 hide_widget_host_observers(child_widget_hosts.size());
6399 for (size_t index = 0U; index < child_widget_hosts.size(); ++index) {
6400 hide_widget_host_observers[index].reset(
6401 new RenderWidgetHostVisibilityObserver(child_widget_hosts[index],
6402 false));
6403 }
ekaramadbabb9bf2016-01-12 15:17:026404
EhsanK2075c7e2017-08-21 02:42:396405 EXPECT_TRUE(ExecuteScript(shell(), hide_script));
6406 for (size_t index = 0U; index < child_widget_hosts.size(); ++index) {
6407 EXPECT_TRUE(hide_widget_host_observers[index]->WaitUntilSatisfied())
6408 << "Expected RenderWidgetHost at distance " << index + 1U
6409 << " from root RenderWidgetHost to become hidden.";
6410 }
6411
6412 // Define observers for notifications about showing child RenderWidgetHosts.
6413 std::vector<std::unique_ptr<RenderWidgetHostVisibilityObserver>>
6414 show_widget_host_observers(child_widget_hosts.size());
6415 for (size_t index = 0U; index < child_widget_hosts.size(); ++index) {
6416 show_widget_host_observers[index].reset(
6417 new RenderWidgetHostVisibilityObserver(child_widget_hosts[index],
6418 true));
6419 }
6420
nickadef4a52016-06-09 18:45:546421 EXPECT_TRUE(ExecuteScript(shell(), show_script));
EhsanK2075c7e2017-08-21 02:42:396422 for (size_t index = 0U; index < child_widget_hosts.size(); ++index) {
6423 EXPECT_TRUE(show_widget_host_observers[index]->WaitUntilSatisfied())
6424 << "Expected RenderWidgetHost at distance " << index + 1U
6425 << " from root RenderWidgetHost to become shown.";
6426 }
ekaramadbabb9bf2016-01-12 15:17:026427}
6428
Ehsan Karamadc2483882017-08-01 18:58:546429// This test verifies that hiding an OOPIF in CSS will stop generating
6430// compositor frames for the OOPIF and any nested OOPIFs inside it. This holds
6431// even when the whole page is shown.
6432IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
6433 HiddenOOPIFWillNotGenerateCompositorFrames) {
6434 GURL main_url(embedded_test_server()->GetURL(
6435 "a.com", "/frame_tree/page_with_two_frames.html"));
6436 ASSERT_TRUE(NavigateToURL(shell(), main_url));
6437 ASSERT_EQ(shell()->web_contents()->GetLastCommittedURL(), main_url);
6438
6439 GURL cross_site_url_b =
6440 embedded_test_server()->GetURL("b.com", "/counter.html");
6441
6442 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
6443
6444 NavigateFrameToURL(root->child_at(0), cross_site_url_b);
6445
6446 NavigateFrameToURL(root->child_at(1), cross_site_url_b);
6447
6448 // Now inject code in the first frame to create a nested OOPIF.
6449 RenderFrameHostCreatedObserver new_frame_created_observer(
6450 shell()->web_contents(), 1);
6451 ASSERT_TRUE(ExecuteScript(
6452 root->child_at(0)->current_frame_host(),
6453 "document.body.appendChild(document.createElement('iframe'));"));
6454 new_frame_created_observer.Wait();
6455
6456 GURL cross_site_url_a =
6457 embedded_test_server()->GetURL("a.com", "/counter.html");
6458
6459 // Navigate the nested frame.
6460 TestFrameNavigationObserver observer(root->child_at(0)->child_at(0));
danakj824a7ff2019-02-07 20:34:026461 ASSERT_TRUE(
6462 ExecuteScript(root->child_at(0)->current_frame_host(),
6463 JsReplace("document.querySelector('iframe').src = $1",
6464 cross_site_url_a)));
Ehsan Karamadc2483882017-08-01 18:58:546465 observer.Wait();
6466
6467 RenderWidgetHostViewChildFrame* first_child_view =
6468 static_cast<RenderWidgetHostViewChildFrame*>(
6469 root->child_at(0)->current_frame_host()->GetView());
6470 RenderWidgetHostViewChildFrame* second_child_view =
6471 static_cast<RenderWidgetHostViewChildFrame*>(
6472 root->child_at(1)->current_frame_host()->GetView());
6473 RenderWidgetHostViewChildFrame* nested_child_view =
6474 static_cast<RenderWidgetHostViewChildFrame*>(
6475 root->child_at(0)->child_at(0)->current_frame_host()->GetView());
6476
jonrossafe76382018-05-25 16:44:326477 RenderFrameSubmissionObserver first_frame_counter(
6478 first_child_view->host_->render_frame_metadata_provider());
6479 RenderFrameSubmissionObserver second_frame_counter(
6480 second_child_view->host_->render_frame_metadata_provider());
6481 RenderFrameSubmissionObserver third_frame_counter(
6482 nested_child_view->host_->render_frame_metadata_provider());
Ehsan Karamadc2483882017-08-01 18:58:546483
jonrossafe76382018-05-25 16:44:326484 const int kFrameCountLimit = 20;
Ehsan Karamadc2483882017-08-01 18:58:546485
6486 // Wait for a minimum number of compositor frames for the second frame.
jonrossafe76382018-05-25 16:44:326487 while (second_frame_counter.render_frame_count() < kFrameCountLimit)
6488 second_frame_counter.WaitForAnyFrameSubmission();
6489 ASSERT_LE(kFrameCountLimit, second_frame_counter.render_frame_count());
Ehsan Karamadc2483882017-08-01 18:58:546490
6491 // Now make sure all frames have roughly the counter value in the sense that
6492 // no counter value is more than twice any other.
jonrossafe76382018-05-25 16:44:326493 float ratio = static_cast<float>(first_frame_counter.render_frame_count()) /
6494 static_cast<float>(second_frame_counter.render_frame_count());
Ehsan Karamadc2483882017-08-01 18:58:546495 EXPECT_GT(2.5f, ratio + 1 / ratio) << "Ratio is: " << ratio;
6496
jonrossafe76382018-05-25 16:44:326497 ratio = static_cast<float>(first_frame_counter.render_frame_count()) /
6498 static_cast<float>(third_frame_counter.render_frame_count());
Ehsan Karamadc2483882017-08-01 18:58:546499 EXPECT_GT(2.5f, ratio + 1 / ratio) << "Ratio is: " << ratio;
6500
6501 // Make sure all views can become visible.
6502 EXPECT_TRUE(first_child_view->CanBecomeVisible());
6503 EXPECT_TRUE(second_child_view->CanBecomeVisible());
6504 EXPECT_TRUE(nested_child_view->CanBecomeVisible());
6505
6506 // Hide the first frame and wait for the notification to be posted by its
6507 // RenderWidgetHost.
6508 RenderWidgetHostVisibilityObserver hide_observer(
6509 root->child_at(0)->current_frame_host()->GetRenderWidgetHost(), false);
6510
6511 // Hide the first frame.
6512 ASSERT_TRUE(ExecuteScript(
6513 shell(),
6514 "document.getElementsByName('frame1')[0].style.visibility = 'hidden'"));
6515 ASSERT_TRUE(hide_observer.WaitUntilSatisfied());
Ken Buchanandaef006b2017-08-17 18:32:156516 EXPECT_TRUE(first_child_view->FrameConnectorForTesting()->IsHidden());
Ehsan Karamadc2483882017-08-01 18:58:546517
6518 // Verify that only the second view can become visible now.
6519 EXPECT_FALSE(first_child_view->CanBecomeVisible());
6520 EXPECT_TRUE(second_child_view->CanBecomeVisible());
6521 EXPECT_FALSE(nested_child_view->CanBecomeVisible());
6522
6523 // Now hide and show the WebContents (to simulate a tab switch).
6524 shell()->web_contents()->WasHidden();
6525 shell()->web_contents()->WasShown();
6526
jonrossafe76382018-05-25 16:44:326527 first_frame_counter.ResetCounter();
6528 second_frame_counter.ResetCounter();
6529 third_frame_counter.ResetCounter();
Ehsan Karamadc2483882017-08-01 18:58:546530
6531 // We expect the second counter to keep running.
jonrossafe76382018-05-25 16:44:326532 while (second_frame_counter.render_frame_count() < kFrameCountLimit)
6533 second_frame_counter.WaitForAnyFrameSubmission();
6534 ASSERT_LT(kFrameCountLimit, second_frame_counter.render_frame_count() + 1);
Ehsan Karamadc2483882017-08-01 18:58:546535
6536 // Verify that the counter for other two frames did not count much.
jonrossafe76382018-05-25 16:44:326537 ratio = static_cast<float>(first_frame_counter.render_frame_count()) /
6538 static_cast<float>(second_frame_counter.render_frame_count());
Ehsan Karamadc2483882017-08-01 18:58:546539 EXPECT_GT(0.5f, ratio) << "Ratio is: " << ratio;
6540
jonrossafe76382018-05-25 16:44:326541 ratio = static_cast<float>(third_frame_counter.render_frame_count()) /
6542 static_cast<float>(second_frame_counter.render_frame_count());
Ehsan Karamadc2483882017-08-01 18:58:546543 EXPECT_GT(0.5f, ratio) << "Ratio is: " << ratio;
6544}
6545
6546// This test verifies that navigating a hidden OOPIF to cross-origin will not
6547// lead to creating compositor frames for the new OOPIF renderer.
6548IN_PROC_BROWSER_TEST_F(
6549 SitePerProcessBrowserTest,
6550 HiddenOOPIFWillNotGenerateCompositorFramesAfterNavigation) {
6551 GURL main_url(embedded_test_server()->GetURL(
6552 "a.com", "/frame_tree/page_with_two_frames.html"));
6553 ASSERT_TRUE(NavigateToURL(shell(), main_url));
6554 ASSERT_EQ(shell()->web_contents()->GetLastCommittedURL(), main_url);
6555
6556 GURL cross_site_url_b =
6557 embedded_test_server()->GetURL("b.com", "/counter.html");
6558
6559 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
6560
6561 NavigateFrameToURL(root->child_at(0), cross_site_url_b);
6562
6563 NavigateFrameToURL(root->child_at(1), cross_site_url_b);
6564
6565 // Hide the first frame and wait for the notification to be posted by its
6566 // RenderWidgetHost.
6567 RenderWidgetHostVisibilityObserver hide_observer(
6568 root->child_at(0)->current_frame_host()->GetRenderWidgetHost(), false);
6569
6570 // Hide the first frame.
6571 ASSERT_TRUE(ExecuteScript(
6572 shell(),
6573 "document.getElementsByName('frame1')[0].style.visibility = 'hidden'"));
6574 ASSERT_TRUE(hide_observer.WaitUntilSatisfied());
6575
6576 // Now navigate the first frame to another OOPIF process.
6577 TestFrameNavigationObserver navigation_observer(
6578 root->child_at(0)->current_frame_host());
6579 GURL cross_site_url_c =
6580 embedded_test_server()->GetURL("c.com", "/counter.html");
6581 ASSERT_TRUE(ExecuteScript(
6582 web_contents(),
danakj824a7ff2019-02-07 20:34:026583 JsReplace("document.getElementsByName('frame1')[0].src = $1",
6584 cross_site_url_c)));
Ehsan Karamadc2483882017-08-01 18:58:546585 navigation_observer.Wait();
6586
6587 // Now investigate compositor frame creation.
6588 RenderWidgetHostViewChildFrame* first_child_view =
6589 static_cast<RenderWidgetHostViewChildFrame*>(
6590 root->child_at(0)->current_frame_host()->GetView());
6591
6592 RenderWidgetHostViewChildFrame* second_child_view =
6593 static_cast<RenderWidgetHostViewChildFrame*>(
6594 root->child_at(1)->current_frame_host()->GetView());
6595
6596 EXPECT_FALSE(first_child_view->CanBecomeVisible());
6597
jonrossafe76382018-05-25 16:44:326598 RenderFrameSubmissionObserver first_frame_counter(
6599 first_child_view->host_->render_frame_metadata_provider());
6600 RenderFrameSubmissionObserver second_frame_counter(
6601 second_child_view->host_->render_frame_metadata_provider());
Ehsan Karamadc2483882017-08-01 18:58:546602
jonrossafe76382018-05-25 16:44:326603 const int kFrameCountLimit = 20;
Ehsan Karamadc2483882017-08-01 18:58:546604
6605 // Wait for a certain number of swapped compositor frames generated for the
6606 // second child view. During the same interval the first frame should not have
6607 // swapped any compositor frames.
jonrossafe76382018-05-25 16:44:326608 while (second_frame_counter.render_frame_count() < kFrameCountLimit)
6609 second_frame_counter.WaitForAnyFrameSubmission();
6610 ASSERT_LT(kFrameCountLimit, second_frame_counter.render_frame_count() + 1);
Ehsan Karamadc2483882017-08-01 18:58:546611
jonrossafe76382018-05-25 16:44:326612 float ratio = static_cast<float>(first_frame_counter.render_frame_count()) /
6613 static_cast<float>(second_frame_counter.render_frame_count());
Ehsan Karamadc2483882017-08-01 18:58:546614 EXPECT_GT(0.5f, ratio) << "Ratio is: " << ratio;
6615}
6616
alexmos6e940102016-01-19 22:47:256617// Verify that sandbox flags inheritance works across multiple levels of
6618// frames. See https://crbug.com/576845.
6619IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, SandboxFlagsInheritance) {
6620 GURL main_url(embedded_test_server()->GetURL(
6621 "a.com", "/cross_site_iframe_factory.html?a(a)"));
davidsac6e6c35e42016-11-21 19:45:576622 EXPECT_TRUE(NavigateToURL(shell(), main_url));
alexmos6e940102016-01-19 22:47:256623
6624 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:006625 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmos6e940102016-01-19 22:47:256626
6627 // Set sandbox flags for child frame.
6628 EXPECT_TRUE(ExecuteScript(
nickadef4a52016-06-09 18:45:546629 root, "document.querySelector('iframe').sandbox = 'allow-scripts';"));
alexmos6e940102016-01-19 22:47:256630
6631 // Calculate expected flags. Note that "allow-scripts" resets both
6632 // WebSandboxFlags::Scripts and WebSandboxFlags::AutomaticFeatures bits per
6633 // blink::parseSandboxPolicy().
6634 blink::WebSandboxFlags expected_flags =
Blink Reformat1c4d759e2017-04-09 16:34:546635 blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kScripts &
6636 ~blink::WebSandboxFlags::kAutomaticFeatures;
Ian Clellandcdc4f312017-10-13 22:24:126637 EXPECT_EQ(expected_flags,
6638 root->child_at(0)->pending_frame_policy().sandbox_flags);
Blink Reformat1c4d759e2017-04-09 16:34:546639 EXPECT_EQ(blink::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:126640 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmos6e940102016-01-19 22:47:256641
6642 // Navigate child frame so that the sandbox flags take effect. Use a page
6643 // with three levels of frames and make sure all frames properly inherit
6644 // sandbox flags.
6645 GURL frame_url(embedded_test_server()->GetURL(
6646 "b.com", "/cross_site_iframe_factory.html?b(c(d))"));
alexmos6e940102016-01-19 22:47:256647 NavigateFrameToURL(root->child_at(0), frame_url);
nasko8206fa12016-03-22 02:24:136648
alexmos6e940102016-01-19 22:47:256649 // Wait for subframes to load as well.
6650 ASSERT_TRUE(WaitForLoadStop(shell()->web_contents()));
6651
6652 // Check each new frame's sandbox flags on the browser process side.
6653 FrameTreeNode* b_child = root->child_at(0);
6654 FrameTreeNode* c_child = b_child->child_at(0);
6655 FrameTreeNode* d_child = c_child->child_at(0);
Ian Clellandcdc4f312017-10-13 22:24:126656 EXPECT_EQ(expected_flags, b_child->effective_frame_policy().sandbox_flags);
6657 EXPECT_EQ(expected_flags, c_child->effective_frame_policy().sandbox_flags);
6658 EXPECT_EQ(expected_flags, d_child->effective_frame_policy().sandbox_flags);
alexmos6e940102016-01-19 22:47:256659
6660 // Check whether each frame is sandboxed on the renderer side, by seeing if
6661 // each frame's origin is unique ("null").
Philip Jägenstedt67302a22018-09-14 09:58:056662 EXPECT_EQ("null", GetOriginFromRenderer(b_child));
6663 EXPECT_EQ("null", GetOriginFromRenderer(c_child));
6664 EXPECT_EQ("null", GetOriginFromRenderer(d_child));
alexmos6e940102016-01-19 22:47:256665}
6666
6667// Check that sandbox flags are not inherited before they take effect. Create
6668// a child frame, update its sandbox flags but don't navigate the frame, and
6669// ensure that a new cross-site grandchild frame doesn't inherit the new flags
6670// (which shouldn't have taken effect).
6671IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
6672 SandboxFlagsNotInheritedBeforeNavigation) {
6673 GURL main_url(embedded_test_server()->GetURL(
6674 "a.com", "/cross_site_iframe_factory.html?a(a)"));
davidsac6e6c35e42016-11-21 19:45:576675 EXPECT_TRUE(NavigateToURL(shell(), main_url));
alexmos6e940102016-01-19 22:47:256676
6677 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:006678 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmos6e940102016-01-19 22:47:256679
6680 // Set sandbox flags for child frame.
6681 EXPECT_TRUE(ExecuteScript(
nickadef4a52016-06-09 18:45:546682 root, "document.querySelector('iframe').sandbox = 'allow-scripts';"));
alexmos6e940102016-01-19 22:47:256683
6684 // These flags should be pending but not take effect, since there's been no
6685 // navigation.
6686 blink::WebSandboxFlags expected_flags =
Blink Reformat1c4d759e2017-04-09 16:34:546687 blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kScripts &
6688 ~blink::WebSandboxFlags::kAutomaticFeatures;
alexmos6e940102016-01-19 22:47:256689 FrameTreeNode* child = root->child_at(0);
Ian Clellandcdc4f312017-10-13 22:24:126690 EXPECT_EQ(expected_flags, child->pending_frame_policy().sandbox_flags);
6691 EXPECT_EQ(blink::WebSandboxFlags::kNone,
6692 child->effective_frame_policy().sandbox_flags);
alexmos6e940102016-01-19 22:47:256693
6694 // Add a new grandchild frame and navigate it cross-site.
6695 RenderFrameHostCreatedObserver frame_observer(shell()->web_contents(), 1);
6696 EXPECT_TRUE(ExecuteScript(
nickadef4a52016-06-09 18:45:546697 child, "document.body.appendChild(document.createElement('iframe'));"));
alexmos6e940102016-01-19 22:47:256698 frame_observer.Wait();
6699
6700 FrameTreeNode* grandchild = child->child_at(0);
6701 GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
6702 TestFrameNavigationObserver navigation_observer(grandchild);
6703 NavigateFrameToURL(grandchild, frame_url);
6704 navigation_observer.Wait();
6705
6706 // Since the update flags haven't yet taken effect in its parent, this
6707 // grandchild frame should not be sandboxed.
Blink Reformat1c4d759e2017-04-09 16:34:546708 EXPECT_EQ(blink::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:126709 grandchild->pending_frame_policy().sandbox_flags);
6710 EXPECT_EQ(blink::WebSandboxFlags::kNone,
6711 grandchild->effective_frame_policy().sandbox_flags);
alexmos6e940102016-01-19 22:47:256712
6713 // Check that the grandchild frame isn't sandboxed on the renderer side. If
6714 // sandboxed, its origin would be unique ("null").
Nick Carterb7e71312018-08-03 23:36:136715 std::string expected_origin = url::Origin::Create(frame_url).Serialize();
Philip Jägenstedt67302a22018-09-14 09:58:056716 EXPECT_EQ(expected_origin, GetOriginFromRenderer(grandchild));
alexmos6e940102016-01-19 22:47:256717}
6718
alexmosaedfc6f2016-01-21 23:57:386719// Verify that popups opened from sandboxed frames inherit sandbox flags from
6720// their opener, and that they keep these inherited flags after being navigated
6721// cross-site. See https://crbug.com/483584.
6722IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
6723 NewPopupInheritsSandboxFlagsFromOpener) {
6724 GURL main_url(embedded_test_server()->GetURL(
6725 "a.com", "/cross_site_iframe_factory.html?a(a)"));
6726 EXPECT_TRUE(NavigateToURL(shell(), main_url));
6727
6728 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:006729 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmosaedfc6f2016-01-21 23:57:386730
6731 // Set sandbox flags for child frame.
Nasko Oskov03912102019-01-11 00:21:326732 EXPECT_TRUE(ExecJs(root,
6733 "document.querySelector('iframe').sandbox = "
6734 " 'allow-scripts allow-popups';"));
alexmosaedfc6f2016-01-21 23:57:386735
6736 // Calculate expected flags. Note that "allow-scripts" resets both
6737 // WebSandboxFlags::Scripts and WebSandboxFlags::AutomaticFeatures bits per
6738 // blink::parseSandboxPolicy().
6739 blink::WebSandboxFlags expected_flags =
Blink Reformat1c4d759e2017-04-09 16:34:546740 blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kScripts &
6741 ~blink::WebSandboxFlags::kAutomaticFeatures &
6742 ~blink::WebSandboxFlags::kPopups;
Ian Clellandcdc4f312017-10-13 22:24:126743 EXPECT_EQ(expected_flags,
6744 root->child_at(0)->pending_frame_policy().sandbox_flags);
alexmosaedfc6f2016-01-21 23:57:386745
6746 // Navigate child frame cross-site. The sandbox flags should take effect.
6747 GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
6748 TestFrameNavigationObserver frame_observer(root->child_at(0));
6749 NavigateFrameToURL(root->child_at(0), frame_url);
6750 frame_observer.Wait();
Ian Clellandcdc4f312017-10-13 22:24:126751 EXPECT_EQ(expected_flags,
6752 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmosaedfc6f2016-01-21 23:57:386753
6754 // Verify that they've also taken effect on the renderer side. The sandboxed
Nasko Oskov03912102019-01-11 00:21:326755 // frame's origin should be opaque.
Philip Jägenstedt67302a22018-09-14 09:58:056756 EXPECT_EQ("null", GetOriginFromRenderer(root->child_at(0)));
Nasko Oskov03912102019-01-11 00:21:326757 const url::SchemeHostPort tuple_b(frame_url);
6758 const url::Origin sandbox_origin_b = root->child_at(0)->current_origin();
6759 EXPECT_TRUE(sandbox_origin_b.opaque());
6760 EXPECT_EQ(tuple_b, sandbox_origin_b.GetTupleOrPrecursorTupleIfOpaque());
alexmosaedfc6f2016-01-21 23:57:386761
6762 // Open a popup named "foo" from the sandboxed child frame.
nickadef4a52016-06-09 18:45:546763 Shell* foo_shell =
6764 OpenPopup(root->child_at(0), GURL(url::kAboutBlankURL), "foo");
alexmosaedfc6f2016-01-21 23:57:386765 EXPECT_TRUE(foo_shell);
6766
6767 FrameTreeNode* foo_root =
6768 static_cast<WebContentsImpl*>(foo_shell->web_contents())
6769 ->GetFrameTree()
6770 ->root();
6771
6772 // Check that the sandbox flags for new popup are correct in the browser
6773 // process.
Ian Clellandcdc4f312017-10-13 22:24:126774 EXPECT_EQ(expected_flags, foo_root->effective_frame_policy().sandbox_flags);
alexmosaedfc6f2016-01-21 23:57:386775
Nasko Oskov03912102019-01-11 00:21:326776 // The popup's origin should be opaque, since it's sandboxed, but cross-origin
6777 // from its opener.
Philip Jägenstedt67302a22018-09-14 09:58:056778 EXPECT_EQ("null", GetOriginFromRenderer(foo_root));
Nasko Oskov03912102019-01-11 00:21:326779 url::Origin sandbox_origin_b2 = foo_root->current_origin();
6780 EXPECT_NE(sandbox_origin_b2, sandbox_origin_b);
6781 EXPECT_TRUE(sandbox_origin_b2.opaque());
6782 EXPECT_EQ(tuple_b, sandbox_origin_b2.GetTupleOrPrecursorTupleIfOpaque());
alexmosaedfc6f2016-01-21 23:57:386783
Nasko Oskov03912102019-01-11 00:21:326784 // Navigate the popup cross-site. This should be placed in an opaque origin
6785 // derived from c.com, and retain the inherited sandbox flags.
alexmosaedfc6f2016-01-21 23:57:386786 GURL c_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
Nasko Oskov03912102019-01-11 00:21:326787 const url::SchemeHostPort tuple_c(c_url);
Alex Moshchuk1ef8e9e2017-08-30 18:26:526788 {
6789 TestFrameNavigationObserver popup_observer(foo_root);
Nasko Oskov03912102019-01-11 00:21:326790 EXPECT_TRUE(ExecJs(foo_root, JsReplace("location.href = $1", c_url)));
Alex Moshchuk1ef8e9e2017-08-30 18:26:526791 popup_observer.Wait();
6792 EXPECT_EQ(c_url, foo_shell->web_contents()->GetLastCommittedURL());
6793 }
6794
6795 // Confirm that the popup is still sandboxed, both on browser and renderer
6796 // sides.
Ian Clellandcdc4f312017-10-13 22:24:126797 EXPECT_EQ(expected_flags, foo_root->effective_frame_policy().sandbox_flags);
Philip Jägenstedt67302a22018-09-14 09:58:056798 EXPECT_EQ("null", GetOriginFromRenderer(foo_root));
Nasko Oskov03912102019-01-11 00:21:326799 const url::Origin sandbox_origin_c = foo_root->current_origin();
6800 EXPECT_NE(sandbox_origin_b, sandbox_origin_c);
6801 EXPECT_TRUE(sandbox_origin_c.opaque());
6802 EXPECT_EQ(tuple_c, sandbox_origin_c.GetTupleOrPrecursorTupleIfOpaque());
Alex Moshchuk1ef8e9e2017-08-30 18:26:526803
6804 // Navigate the popup back to b.com. The popup should perform a
Nasko Oskov03912102019-01-11 00:21:326805 // remote-to-local navigation in the b.com process, and keep an opaque
Alex Moshchuk1ef8e9e2017-08-30 18:26:526806 // origin and the inherited sandbox flags.
6807 {
6808 TestFrameNavigationObserver popup_observer(foo_root);
Nasko Oskov03912102019-01-11 00:21:326809 EXPECT_TRUE(ExecJs(foo_root, JsReplace("location.href = $1", frame_url)));
Alex Moshchuk1ef8e9e2017-08-30 18:26:526810 popup_observer.Wait();
6811 EXPECT_EQ(frame_url, foo_shell->web_contents()->GetLastCommittedURL());
6812 }
alexmosaedfc6f2016-01-21 23:57:386813
6814 // Confirm that the popup is still sandboxed, both on browser and renderer
Nasko Oskov03912102019-01-11 00:21:326815 // sides. This navigation should result in a new opaque origin derived
6816 // from b.com.
Ian Clellandcdc4f312017-10-13 22:24:126817 EXPECT_EQ(expected_flags, foo_root->effective_frame_policy().sandbox_flags);
Philip Jägenstedt67302a22018-09-14 09:58:056818 EXPECT_EQ("null", GetOriginFromRenderer(foo_root));
Nasko Oskov03912102019-01-11 00:21:326819 url::Origin sandbox_origin_b3 = foo_root->current_origin();
6820 EXPECT_TRUE(sandbox_origin_b3.opaque());
6821 EXPECT_EQ(tuple_b, sandbox_origin_b3.GetTupleOrPrecursorTupleIfOpaque());
6822 EXPECT_NE(sandbox_origin_b, sandbox_origin_b3);
6823 EXPECT_NE(sandbox_origin_b2, sandbox_origin_b3);
alexmosaedfc6f2016-01-21 23:57:386824}
6825
6826// Verify that popups opened from frames sandboxed with the
6827// "allow-popups-to-escape-sandbox" directive do *not* inherit sandbox flags
6828// from their opener.
6829IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
6830 OpenUnsandboxedPopupFromSandboxedFrame) {
6831 GURL main_url(embedded_test_server()->GetURL(
6832 "a.com", "/cross_site_iframe_factory.html?a(a)"));
6833 EXPECT_TRUE(NavigateToURL(shell(), main_url));
6834
6835 // It is safe to obtain the root frame tree node here, as it doesn't change.
ekaramadfd1b5cfa2016-04-19 00:35:006836 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmosaedfc6f2016-01-21 23:57:386837
6838 // Set sandbox flags for child frame, specifying that popups opened from it
6839 // should not be sandboxed.
6840 EXPECT_TRUE(ExecuteScript(
nickadef4a52016-06-09 18:45:546841 root,
alexmosaedfc6f2016-01-21 23:57:386842 "document.querySelector('iframe').sandbox = "
6843 " 'allow-scripts allow-popups allow-popups-to-escape-sandbox';"));
6844
6845 // Set expected flags for the child frame. Note that "allow-scripts" resets
6846 // both WebSandboxFlags::Scripts and WebSandboxFlags::AutomaticFeatures bits
6847 // per blink::parseSandboxPolicy().
6848 blink::WebSandboxFlags expected_flags =
Blink Reformat1c4d759e2017-04-09 16:34:546849 blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kScripts &
6850 ~blink::WebSandboxFlags::kAutomaticFeatures &
6851 ~blink::WebSandboxFlags::kPopups &
6852 ~blink::WebSandboxFlags::kPropagatesToAuxiliaryBrowsingContexts;
Ian Clellandcdc4f312017-10-13 22:24:126853 EXPECT_EQ(expected_flags,
6854 root->child_at(0)->pending_frame_policy().sandbox_flags);
alexmosaedfc6f2016-01-21 23:57:386855
6856 // Navigate child frame cross-site. The sandbox flags should take effect.
6857 GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
6858 TestFrameNavigationObserver frame_observer(root->child_at(0));
6859 NavigateFrameToURL(root->child_at(0), frame_url);
6860 frame_observer.Wait();
Ian Clellandcdc4f312017-10-13 22:24:126861 EXPECT_EQ(expected_flags,
6862 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmosaedfc6f2016-01-21 23:57:386863
6864 // Open a cross-site popup named "foo" from the child frame.
6865 GURL b_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
nickadef4a52016-06-09 18:45:546866 Shell* foo_shell = OpenPopup(root->child_at(0), b_url, "foo");
alexmosaedfc6f2016-01-21 23:57:386867 EXPECT_TRUE(foo_shell);
6868
6869 FrameTreeNode* foo_root =
6870 static_cast<WebContentsImpl*>(foo_shell->web_contents())
6871 ->GetFrameTree()
6872 ->root();
6873
6874 // Check that the sandbox flags for new popup are correct in the browser
6875 // process. They should not have been inherited.
Ian Clellandcdc4f312017-10-13 22:24:126876 EXPECT_EQ(blink::WebSandboxFlags::kNone,
6877 foo_root->effective_frame_policy().sandbox_flags);
alexmosaedfc6f2016-01-21 23:57:386878
6879 // The popup's origin should match |b_url|, since it's not sandboxed.
Nick Carterb7e71312018-08-03 23:36:136880 EXPECT_EQ(url::Origin::Create(b_url).Serialize(),
Philip Jägenstedt67302a22018-09-14 09:58:056881 EvalJs(foo_root, "self.origin;"));
alexmosaedfc6f2016-01-21 23:57:386882}
6883
estark56dc8e22016-01-26 17:58:296884// Tests that the WebContents is notified when passive mixed content is
6885// displayed in an OOPIF. The test ignores cert errors so that an HTTPS
6886// iframe can be loaded from a site other than localhost (the
6887// EmbeddedTestServer serves a certificate that is valid for localhost).
gliderad70d3f62016-04-06 09:41:396888// This test crashes on Windows under Dr. Memory, see https://crbug.com/600942.
6889#if defined(OS_WIN)
6890#define MAYBE_PassiveMixedContentInIframe DISABLED_PassiveMixedContentInIframe
6891#else
6892#define MAYBE_PassiveMixedContentInIframe PassiveMixedContentInIframe
6893#endif
estark56dc8e22016-01-26 17:58:296894IN_PROC_BROWSER_TEST_F(SitePerProcessIgnoreCertErrorsBrowserTest,
gliderad70d3f62016-04-06 09:41:396895 MAYBE_PassiveMixedContentInIframe) {
estark56dc8e22016-01-26 17:58:296896 net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
Alan Cutterfb5689192019-03-28 08:39:196897 https_server.ServeFilesFromSourceDirectory(GetTestDataFilePath());
estark56dc8e22016-01-26 17:58:296898 SetupCrossSiteRedirector(&https_server);
martijna4dce162016-11-16 17:25:046899 ASSERT_TRUE(https_server.Start());
estark56dc8e22016-01-26 17:58:296900
estarkcd2e30c2016-08-12 06:51:156901 WebContentsImpl* web_contents =
6902 static_cast<WebContentsImpl*>(shell()->web_contents());
6903
estark56dc8e22016-01-26 17:58:296904 GURL iframe_url(
6905 https_server.GetURL("/mixed-content/basic-passive-in-iframe.html"));
6906 EXPECT_TRUE(NavigateToURL(shell(), iframe_url));
estarkc2273502016-10-12 22:03:026907 NavigationEntry* entry = web_contents->GetController().GetVisibleEntry();
6908 EXPECT_TRUE(!!(entry->GetSSL().content_status &
6909 SSLStatus::DISPLAYED_INSECURE_CONTENT));
estark56dc8e22016-01-26 17:58:296910
6911 // When the subframe navigates, the WebContents should still be marked
6912 // as having displayed insecure content.
6913 GURL navigate_url(https_server.GetURL("/title1.html"));
estarkcd2e30c2016-08-12 06:51:156914 FrameTreeNode* root = web_contents->GetFrameTree()->root();
estark56dc8e22016-01-26 17:58:296915 NavigateFrameToURL(root->child_at(0), navigate_url);
estarkc2273502016-10-12 22:03:026916 entry = web_contents->GetController().GetVisibleEntry();
6917 EXPECT_TRUE(!!(entry->GetSSL().content_status &
6918 SSLStatus::DISPLAYED_INSECURE_CONTENT));
estark56dc8e22016-01-26 17:58:296919
6920 // When the main frame navigates, it should no longer be marked as
6921 // displaying insecure content.
6922 EXPECT_TRUE(
6923 NavigateToURL(shell(), https_server.GetURL("b.com", "/title1.html")));
estarkc2273502016-10-12 22:03:026924 entry = web_contents->GetController().GetVisibleEntry();
6925 EXPECT_FALSE(!!(entry->GetSSL().content_status &
6926 SSLStatus::DISPLAYED_INSECURE_CONTENT));
estark56dc8e22016-01-26 17:58:296927}
6928
6929// Tests that, when a parent frame is set to strictly block mixed
6930// content via Content Security Policy, child OOPIFs cannot display
6931// mixed content.
6932IN_PROC_BROWSER_TEST_F(SitePerProcessIgnoreCertErrorsBrowserTest,
6933 PassiveMixedContentInIframeWithStrictBlocking) {
6934 net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
Alan Cutterfb5689192019-03-28 08:39:196935 https_server.ServeFilesFromSourceDirectory(GetTestDataFilePath());
estark56dc8e22016-01-26 17:58:296936 SetupCrossSiteRedirector(&https_server);
martijna4dce162016-11-16 17:25:046937 ASSERT_TRUE(https_server.Start());
estark56dc8e22016-01-26 17:58:296938
estarkcd2e30c2016-08-12 06:51:156939 WebContentsImpl* web_contents =
6940 static_cast<WebContentsImpl*>(shell()->web_contents());
6941
estark56dc8e22016-01-26 17:58:296942 GURL iframe_url_with_strict_blocking(https_server.GetURL(
6943 "/mixed-content/basic-passive-in-iframe-with-strict-blocking.html"));
6944 EXPECT_TRUE(NavigateToURL(shell(), iframe_url_with_strict_blocking));
estarkc2273502016-10-12 22:03:026945 NavigationEntry* entry = web_contents->GetController().GetVisibleEntry();
6946 EXPECT_FALSE(!!(entry->GetSSL().content_status &
6947 SSLStatus::DISPLAYED_INSECURE_CONTENT));
estark56dc8e22016-01-26 17:58:296948
estarkcd2e30c2016-08-12 06:51:156949 FrameTreeNode* root = web_contents->GetFrameTree()->root();
mkwstf672e7ef2016-06-09 20:51:076950 EXPECT_EQ(blink::kBlockAllMixedContent,
6951 root->current_replication_state().insecure_request_policy);
6952 EXPECT_EQ(
6953 blink::kBlockAllMixedContent,
6954 root->child_at(0)->current_replication_state().insecure_request_policy);
estark56dc8e22016-01-26 17:58:296955
6956 // When the subframe navigates, it should still be marked as enforcing
6957 // strict mixed content.
6958 GURL navigate_url(https_server.GetURL("/title1.html"));
6959 NavigateFrameToURL(root->child_at(0), navigate_url);
mkwstf672e7ef2016-06-09 20:51:076960 EXPECT_EQ(blink::kBlockAllMixedContent,
6961 root->current_replication_state().insecure_request_policy);
6962 EXPECT_EQ(
6963 blink::kBlockAllMixedContent,
6964 root->child_at(0)->current_replication_state().insecure_request_policy);
estark56dc8e22016-01-26 17:58:296965
6966 // When the main frame navigates, it should no longer be marked as
6967 // enforcing strict mixed content.
6968 EXPECT_TRUE(
6969 NavigateToURL(shell(), https_server.GetURL("b.com", "/title1.html")));
mkwstf672e7ef2016-06-09 20:51:076970 EXPECT_EQ(blink::kLeaveInsecureRequestsAlone,
6971 root->current_replication_state().insecure_request_policy);
6972}
6973
6974// Tests that, when a parent frame is set to upgrade insecure requests
6975// via Content Security Policy, child OOPIFs will upgrade as well.
6976IN_PROC_BROWSER_TEST_F(SitePerProcessIgnoreCertErrorsBrowserTest,
6977 PassiveMixedContentInIframeWithUpgrade) {
6978 net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
Alan Cutterfb5689192019-03-28 08:39:196979 https_server.ServeFilesFromSourceDirectory(GetTestDataFilePath());
mkwstf672e7ef2016-06-09 20:51:076980 SetupCrossSiteRedirector(&https_server);
martijna4dce162016-11-16 17:25:046981 ASSERT_TRUE(https_server.Start());
mkwstf672e7ef2016-06-09 20:51:076982
estarkcd2e30c2016-08-12 06:51:156983 WebContentsImpl* web_contents =
6984 static_cast<WebContentsImpl*>(shell()->web_contents());
6985
mkwstf672e7ef2016-06-09 20:51:076986 GURL iframe_url_with_upgrade(https_server.GetURL(
6987 "/mixed-content/basic-passive-in-iframe-with-upgrade.html"));
6988 EXPECT_TRUE(NavigateToURL(shell(), iframe_url_with_upgrade));
estarkc2273502016-10-12 22:03:026989 NavigationEntry* entry = web_contents->GetController().GetVisibleEntry();
6990 EXPECT_FALSE(!!(entry->GetSSL().content_status &
6991 SSLStatus::DISPLAYED_INSECURE_CONTENT));
mkwstf672e7ef2016-06-09 20:51:076992
estarkcd2e30c2016-08-12 06:51:156993 FrameTreeNode* root = web_contents->GetFrameTree()->root();
mkwstf672e7ef2016-06-09 20:51:076994 EXPECT_EQ(blink::kUpgradeInsecureRequests,
6995 root->current_replication_state().insecure_request_policy);
6996 EXPECT_EQ(
6997 blink::kUpgradeInsecureRequests,
6998 root->child_at(0)->current_replication_state().insecure_request_policy);
6999
7000 // When the subframe navigates, it should still be marked as upgrading
7001 // insecure requests.
7002 GURL navigate_url(https_server.GetURL("/title1.html"));
7003 NavigateFrameToURL(root->child_at(0), navigate_url);
7004 EXPECT_EQ(blink::kUpgradeInsecureRequests,
7005 root->current_replication_state().insecure_request_policy);
7006 EXPECT_EQ(
7007 blink::kUpgradeInsecureRequests,
7008 root->child_at(0)->current_replication_state().insecure_request_policy);
7009
7010 // When the main frame navigates, it should no longer be marked as
7011 // upgrading insecure requests.
7012 EXPECT_TRUE(
7013 NavigateToURL(shell(), https_server.GetURL("b.com", "/title1.html")));
7014 EXPECT_EQ(blink::kLeaveInsecureRequestsAlone,
7015 root->current_replication_state().insecure_request_policy);
estark56dc8e22016-01-26 17:58:297016}
7017
7018// Tests that active mixed content is blocked in an OOPIF. The test
7019// ignores cert errors so that an HTTPS iframe can be loaded from a site
7020// other than localhost (the EmbeddedTestServer serves a certificate
7021// that is valid for localhost).
7022IN_PROC_BROWSER_TEST_F(SitePerProcessIgnoreCertErrorsBrowserTest,
7023 ActiveMixedContentInIframe) {
7024 net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
Alan Cutterfb5689192019-03-28 08:39:197025 https_server.ServeFilesFromSourceDirectory(GetTestDataFilePath());
estark56dc8e22016-01-26 17:58:297026 SetupCrossSiteRedirector(&https_server);
martijna4dce162016-11-16 17:25:047027 ASSERT_TRUE(https_server.Start());
estark56dc8e22016-01-26 17:58:297028
7029 GURL iframe_url(
7030 https_server.GetURL("/mixed-content/basic-active-in-iframe.html"));
7031 EXPECT_TRUE(NavigateToURL(shell(), iframe_url));
ekaramadfd1b5cfa2016-04-19 00:35:007032 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
estark56dc8e22016-01-26 17:58:297033 ASSERT_EQ(1U, root->child_count());
7034 FrameTreeNode* mixed_child = root->child_at(0)->child_at(0);
7035 ASSERT_TRUE(mixed_child);
tommycli13632cf2016-06-13 21:54:197036 // The child iframe attempted to create a mixed iframe; this should
7037 // have been blocked, so the mixed iframe should not have committed a
7038 // load.
7039 EXPECT_FALSE(mixed_child->has_committed_real_load());
estark56dc8e22016-01-26 17:58:297040}
7041
estarkcd2e30c2016-08-12 06:51:157042// Test that subresources with certificate errors get reported to the
7043// browser. That is, if https://example.test frames https://a.com which
7044// loads an image with certificate errors, the browser should be
7045// notified about the subresource with certificate errors and downgrade
7046// the UI appropriately.
estarkaeda1422016-05-10 02:31:127047IN_PROC_BROWSER_TEST_F(SitePerProcessIgnoreCertErrorsBrowserTest,
estarkcd2e30c2016-08-12 06:51:157048 SubresourceWithCertificateErrors) {
estarkaeda1422016-05-10 02:31:127049 net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
Alan Cutterfb5689192019-03-28 08:39:197050 https_server.ServeFilesFromSourceDirectory(GetTestDataFilePath());
estarkaeda1422016-05-10 02:31:127051 SetupCrossSiteRedirector(&https_server);
martijna4dce162016-11-16 17:25:047052 ASSERT_TRUE(https_server.Start());
estarkaeda1422016-05-10 02:31:127053
7054 GURL url(https_server.GetURL(
estarkcd2e30c2016-08-12 06:51:157055 "example.test",
estarkaeda1422016-05-10 02:31:127056 "/mixed-content/non-redundant-cert-error-in-iframe.html"));
7057 EXPECT_TRUE(NavigateToURL(shell(), url));
7058
7059 NavigationEntry* entry =
7060 shell()->web_contents()->GetController().GetLastCommittedEntry();
7061 ASSERT_TRUE(entry);
7062
7063 // The main page was loaded with certificate errors.
estark47ba9c7d2016-10-11 15:40:117064 EXPECT_TRUE(net::IsCertStatusError(entry->GetSSL().cert_status));
estarkaeda1422016-05-10 02:31:127065
estarkcd2e30c2016-08-12 06:51:157066 // The image that the iframe loaded had certificate errors also, so
7067 // the page should be marked as having displayed subresources with
7068 // cert errors.
estarkc2273502016-10-12 22:03:027069 EXPECT_TRUE(!!(entry->GetSSL().content_status &
7070 SSLStatus::DISPLAYED_CONTENT_WITH_CERT_ERRORS));
estarkaeda1422016-05-10 02:31:127071}
7072
lfg43e08e62016-02-03 18:51:377073// Test setting a cross-origin iframe to display: none.
7074IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CrossSiteIframeDisplayNone) {
7075 GURL main_url(embedded_test_server()->GetURL(
7076 "a.com", "/cross_site_iframe_factory.html?a(b)"));
davidsac6e6c35e42016-11-21 19:45:577077 EXPECT_TRUE(NavigateToURL(shell(), main_url));
lfg43e08e62016-02-03 18:51:377078
ekaramadfd1b5cfa2016-04-19 00:35:007079 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
lfg43e08e62016-02-03 18:51:377080 RenderWidgetHost* root_render_widget_host =
7081 root->current_frame_host()->GetRenderWidgetHost();
7082
7083 // Set the iframe to display: none.
nickadef4a52016-06-09 18:45:547084 EXPECT_TRUE(ExecuteScript(
7085 shell(), "document.querySelector('iframe').style.display = 'none'"));
lfg43e08e62016-02-03 18:51:377086
7087 // Waits until pending frames are done.
dcheng59716272016-04-09 05:19:087088 std::unique_ptr<MainThreadFrameObserver> observer(
lfg43e08e62016-02-03 18:51:377089 new MainThreadFrameObserver(root_render_widget_host));
7090 observer->Wait();
7091
7092 // Force the renderer to generate a new frame.
nickadef4a52016-06-09 18:45:547093 EXPECT_TRUE(
7094 ExecuteScript(shell(), "document.body.style.background = 'black'"));
lfg43e08e62016-02-03 18:51:377095
7096 // Waits for the next frame.
7097 observer->Wait();
7098}
7099
alexmos4170f6e82016-03-03 22:42:047100// Test that a cross-origin iframe can be blocked by X-Frame-Options and CSP
7101// frame-ancestors.
7102IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
7103 CrossSiteIframeBlockedByXFrameOptionsOrCSP) {
7104 GURL main_url(embedded_test_server()->GetURL(
7105 "a.com", "/cross_site_iframe_factory.html?a(a)"));
davidsac6e6c35e42016-11-21 19:45:577106 EXPECT_TRUE(NavigateToURL(shell(), main_url));
alexmos4170f6e82016-03-03 22:42:047107
ekaramadfd1b5cfa2016-04-19 00:35:007108 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
alexmos4170f6e82016-03-03 22:42:047109
7110 // Add a load event handler for the iframe element.
nickadef4a52016-06-09 18:45:547111 EXPECT_TRUE(ExecuteScript(shell(),
alexmos4170f6e82016-03-03 22:42:047112 "document.querySelector('iframe').onload = "
7113 " function() { document.title = 'loaded'; };"));
7114
Arthur Sonzogni144eff602018-08-13 10:37:157115 const struct {
7116 const char* url;
7117 bool use_error_page;
7118 } kTestCases[] = {
7119 {"/frame-ancestors-none.html", false},
7120 {"/x-frame-options-deny.html", true},
alexmos4170f6e82016-03-03 22:42:047121 };
7122
Arthur Sonzogni144eff602018-08-13 10:37:157123 for (const auto& test : kTestCases) {
7124 GURL blocked_url = embedded_test_server()->GetURL("b.com", test.url);
nickadef4a52016-06-09 18:45:547125 EXPECT_TRUE(ExecuteScript(shell(), "document.title = 'not loaded';"));
alexmos4170f6e82016-03-03 22:42:047126 base::string16 expected_title(base::UTF8ToUTF16("loaded"));
7127 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
7128
7129 // Navigate the subframe to a blocked URL.
7130 TestNavigationObserver load_observer(shell()->web_contents());
Arthur Sonzogni144eff602018-08-13 10:37:157131 EXPECT_TRUE(ExecuteScript(
danakj824a7ff2019-02-07 20:34:027132 shell(), JsReplace("frames[0].location.href = $1", blocked_url)));
alexmos4170f6e82016-03-03 22:42:047133 load_observer.Wait();
7134
7135 // The blocked frame's origin should become unique.
7136 EXPECT_EQ("null", root->child_at(0)->current_origin().Serialize());
7137
Arthur Sonzogni144eff602018-08-13 10:37:157138 // X-Frame-Options and CSP frame-ancestors behave differently. XFO commits
7139 // an error page, while CSP commits a "data:," URL.
7140 // TODO(https://crbug.com/870815): Use an error page for both.
7141 if (test.use_error_page) {
7142 EXPECT_FALSE(load_observer.last_navigation_succeeded());
7143 EXPECT_EQ(net::ERR_BLOCKED_BY_RESPONSE,
7144 load_observer.last_net_error_code());
7145 EXPECT_EQ(root->child_at(0)->current_frame_host()->GetLastCommittedURL(),
7146 blocked_url);
7147 EXPECT_EQ("Error", EvalJs(root->child_at(0), "document.title"));
7148 } else {
7149 EXPECT_TRUE(load_observer.last_navigation_succeeded());
7150 EXPECT_EQ(net::OK, load_observer.last_net_error_code());
7151 // Ensure that we don't use the blocked URL as the blocked frame's last
7152 // committed URL (see https://crbug.com/622385).
7153 EXPECT_EQ(root->child_at(0)->current_frame_host()->GetLastCommittedURL(),
7154 GURL("data:,"));
7155
7156 // The blocked navigation should behave like an empty 200 response. Make
7157 // sure that the frame's document.title is empty: this double-checks both
7158 // that the blocked URL's contents wasn't loaded, and that the old page
7159 // isn't active anymore (both of these pages have non-empty titles).
7160 EXPECT_EQ("", EvalJs(root->child_at(0), "document.title"));
7161 }
alexmos30535f72016-06-23 18:48:237162
alexmos4170f6e82016-03-03 22:42:047163 // The blocked frame should still fire a load event in its parent's process.
7164 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
7165
7166 // Check that the current RenderFrameHost has stopped loading.
7167 EXPECT_FALSE(root->child_at(0)->current_frame_host()->is_loading());
7168
alexmos4170f6e82016-03-03 22:42:047169 // Navigate the subframe to another cross-origin page and ensure that this
7170 // navigation succeeds. Use a renderer-initiated navigation to test the
7171 // transfer logic, which used to have some issues with this.
7172 GURL c_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
7173 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "child-0", c_url));
7174 EXPECT_EQ(c_url, root->child_at(0)->current_url());
7175
7176 // When a page gets blocked due to XFO or CSP, it is sandboxed with the
7177 // SandboxOrigin flag (i.e., its origin is set to be unique) to ensure that
7178 // the blocked page is seen as cross-origin. However, those flags shouldn't
7179 // affect future navigations for a frame. Verify this for the above
7180 // navigation.
7181 EXPECT_EQ(c_url.GetOrigin().spec(),
7182 root->child_at(0)->current_origin().Serialize() + "/");
Blink Reformat1c4d759e2017-04-09 16:34:547183 EXPECT_EQ(blink::WebSandboxFlags::kNone,
Ian Clellandcdc4f312017-10-13 22:24:127184 root->child_at(0)->effective_frame_policy().sandbox_flags);
alexmos4170f6e82016-03-03 22:42:047185 }
7186}
7187
lukasza8e1c02e42016-05-17 20:05:107188// Test that a cross-origin frame's navigation can be blocked by CSP frame-src.
7189// In this version of a test, CSP comes from HTTP headers.
7190IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
7191 CrossSiteIframeBlockedByParentCSPFromHeaders) {
7192 GURL main_url(
7193 embedded_test_server()->GetURL("a.com", "/frame-src-self-and-b.html"));
7194 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7195
7196 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
7197
7198 // Sanity-check that the test page has the expected shape for testing.
7199 GURL old_subframe_url(
7200 embedded_test_server()->GetURL("b.com", "/title2.html"));
7201 EXPECT_FALSE(root->child_at(0)->HasSameOrigin(*root));
7202 EXPECT_EQ(old_subframe_url, root->child_at(0)->current_url());
7203 const std::vector<ContentSecurityPolicyHeader>& root_csp =
7204 root->current_replication_state().accumulated_csp_headers;
7205 EXPECT_EQ(1u, root_csp.size());
7206 EXPECT_EQ("frame-src 'self' http://b.com:*", root_csp[0].header_value);
7207
7208 // Monitor subframe's load events via main frame's title.
nickadef4a52016-06-09 18:45:547209 EXPECT_TRUE(ExecuteScript(shell(),
lukasza8e1c02e42016-05-17 20:05:107210 "document.querySelector('iframe').onload = "
7211 " function() { document.title = 'loaded'; };"));
nickadef4a52016-06-09 18:45:547212 EXPECT_TRUE(ExecuteScript(shell(), "document.title = 'not loaded';"));
lukasza8e1c02e42016-05-17 20:05:107213 base::string16 expected_title(base::UTF8ToUTF16("loaded"));
7214 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
7215
7216 // Try to navigate the subframe to a blocked URL.
7217 TestNavigationObserver load_observer(shell()->web_contents());
7218 GURL blocked_url = embedded_test_server()->GetURL("c.com", "/title3.html");
danakj824a7ff2019-02-07 20:34:027219 EXPECT_TRUE(ExecuteScript(
7220 root->child_at(0), JsReplace("window.location.href = $1", blocked_url)));
lukasza8e1c02e42016-05-17 20:05:107221
7222 // The blocked frame should still fire a load event in its parent's process.
7223 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
7224
7225 // Check that the current RenderFrameHost has stopped loading.
Nasko Oskov63dda2952018-02-09 19:35:427226 if (root->child_at(0)->current_frame_host()->is_loading())
lukasza8e1c02e42016-05-17 20:05:107227 load_observer.Wait();
lukasza8e1c02e42016-05-17 20:05:107228
arthursonzogni7fed384c2017-03-18 03:07:347229 // The last successful url shouldn't be the blocked url.
7230 EXPECT_EQ(old_subframe_url,
7231 root->child_at(0)->current_frame_host()->last_successful_url());
lukasza8e1c02e42016-05-17 20:05:107232
Nasko Oskov63dda2952018-02-09 19:35:427233 // The blocked frame should go to an error page. Errors currently commit
7234 // with the URL of the blocked page.
7235 EXPECT_EQ(blocked_url, root->child_at(0)->current_url());
arthursonzogni7fed384c2017-03-18 03:07:347236
Nasko Oskov63dda2952018-02-09 19:35:427237 // The page should get the title of an error page (i.e "Error") and not the
7238 // title of the blocked page.
Nick Carterb7e71312018-08-03 23:36:137239 EXPECT_EQ("Error", EvalJs(root->child_at(0), "document.title"));
lukasza8e1c02e42016-05-17 20:05:107240
7241 // Navigate to a URL without CSP.
7242 EXPECT_TRUE(NavigateToURL(
7243 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
lukasza8e1c02e42016-05-17 20:05:107244}
7245
7246// Test that a cross-origin frame's navigation can be blocked by CSP frame-src.
7247// In this version of a test, CSP comes from a <meta> element added after the
7248// page has already loaded.
7249IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
7250 CrossSiteIframeBlockedByParentCSPFromMeta) {
7251 GURL main_url(embedded_test_server()->GetURL(
7252 "a.com", "/cross_site_iframe_factory.html?a(a)"));
7253 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7254
7255 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
7256
7257 // Navigate the subframe to a location we will disallow in the future.
7258 GURL old_subframe_url(
7259 embedded_test_server()->GetURL("b.com", "/title2.html"));
7260 NavigateFrameToURL(root->child_at(0), old_subframe_url);
7261
7262 // Add frame-src CSP via a new <meta> element.
Nick Carterb7e71312018-08-03 23:36:137263 EXPECT_TRUE(
7264 ExecJs(shell(),
7265 "var meta = document.createElement('meta');"
7266 "meta.httpEquiv = 'Content-Security-Policy';"
7267 "meta.content = 'frame-src https://a.com:*';"
7268 "document.getElementsByTagName('head')[0].appendChild(meta);"));
lukasza8e1c02e42016-05-17 20:05:107269
7270 // Sanity-check that the test page has the expected shape for testing.
7271 // (the CSP should not have an effect on the already loaded frames).
7272 EXPECT_FALSE(root->child_at(0)->HasSameOrigin(*root));
7273 EXPECT_EQ(old_subframe_url, root->child_at(0)->current_url());
7274 const std::vector<ContentSecurityPolicyHeader>& root_csp =
7275 root->current_replication_state().accumulated_csp_headers;
7276 EXPECT_EQ(1u, root_csp.size());
7277 EXPECT_EQ("frame-src https://a.com:*", root_csp[0].header_value);
7278
7279 // Monitor subframe's load events via main frame's title.
Nick Carterb7e71312018-08-03 23:36:137280 EXPECT_TRUE(ExecJs(shell(),
7281 "document.querySelector('iframe').onload = "
7282 " function() { document.title = 'loaded'; };"));
7283 EXPECT_TRUE(ExecJs(shell(), "document.title = 'not loaded';"));
lukasza8e1c02e42016-05-17 20:05:107284 base::string16 expected_title(base::UTF8ToUTF16("loaded"));
7285 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
7286
7287 // Try to navigate the subframe to a blocked URL.
7288 TestNavigationObserver load_observer2(shell()->web_contents());
7289 GURL blocked_url = embedded_test_server()->GetURL("c.com", "/title3.html");
Nick Carterb7e71312018-08-03 23:36:137290 EXPECT_TRUE(ExecJs(root->child_at(0),
7291 JsReplace("window.location.href = $1;", blocked_url)));
lukasza8e1c02e42016-05-17 20:05:107292
7293 // The blocked frame should still fire a load event in its parent's process.
7294 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
7295
7296 // Check that the current RenderFrameHost has stopped loading.
Nasko Oskov63dda2952018-02-09 19:35:427297 if (root->child_at(0)->current_frame_host()->is_loading())
lukasza8e1c02e42016-05-17 20:05:107298 load_observer2.Wait();
lukasza8e1c02e42016-05-17 20:05:107299
arthursonzogni7fed384c2017-03-18 03:07:347300 // The last successful url shouldn't be the blocked url.
7301 EXPECT_EQ(old_subframe_url,
7302 root->child_at(0)->current_frame_host()->last_successful_url());
lukasza8e1c02e42016-05-17 20:05:107303
Nasko Oskov63dda2952018-02-09 19:35:427304 // The blocked frame should go to an error page. Errors currently commit
7305 // with the URL of the blocked page.
7306 EXPECT_EQ(blocked_url, root->child_at(0)->current_url());
arthursonzogni7fed384c2017-03-18 03:07:347307
Nasko Oskov63dda2952018-02-09 19:35:427308 // The page should get the title of an error page (i.e "Error") and not the
7309 // title of the blocked page.
Nick Carterb7e71312018-08-03 23:36:137310 EXPECT_EQ("Error", EvalJs(root->child_at(0), "document.title"));
lukasza8e1c02e42016-05-17 20:05:107311}
7312
7313// Test that a cross-origin frame's navigation can be blocked by CSP frame-src.
7314// In this version of a test, CSP is inherited by srcdoc iframe from a parent
7315// that declared CSP via HTTP headers. Cross-origin frame navigating to a
7316// blocked location is a child of the srcdoc iframe.
7317IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
7318 CrossSiteIframeBlockedByCSPInheritedBySrcDocParent) {
7319 GURL main_url(
7320 embedded_test_server()->GetURL("a.com", "/frame-src-self-and-b.html"));
7321 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7322
7323 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
7324 FrameTreeNode* srcdoc_frame = root->child_at(1);
7325 EXPECT_TRUE(srcdoc_frame != nullptr);
7326 FrameTreeNode* navigating_frame = srcdoc_frame->child_at(0);
7327 EXPECT_TRUE(navigating_frame != nullptr);
7328
7329 // Sanity-check that the test page has the expected shape for testing.
7330 // (the CSP should not have an effect on the already loaded frames).
7331 GURL old_subframe_url(
7332 embedded_test_server()->GetURL("b.com", "/title2.html"));
7333 EXPECT_TRUE(srcdoc_frame->HasSameOrigin(*root));
7334 EXPECT_FALSE(srcdoc_frame->HasSameOrigin(*navigating_frame));
7335 EXPECT_EQ(old_subframe_url, navigating_frame->current_url());
7336 const std::vector<ContentSecurityPolicyHeader>& srcdoc_csp =
7337 srcdoc_frame->current_replication_state().accumulated_csp_headers;
7338 EXPECT_EQ(1u, srcdoc_csp.size());
7339 EXPECT_EQ("frame-src 'self' http://b.com:*", srcdoc_csp[0].header_value);
7340
7341 // Monitor navigating_frame's load events via srcdoc_frame posting
7342 // a message to the parent frame.
Nick Carterb7e71312018-08-03 23:36:137343 EXPECT_TRUE(ExecJs(root,
7344 "window.addEventListener('message', function(event) {"
7345 " document.title = event.data;"
7346 "});"));
lukasza8e1c02e42016-05-17 20:05:107347 EXPECT_TRUE(
Nick Carterb7e71312018-08-03 23:36:137348 ExecJs(srcdoc_frame,
7349 "document.querySelector('iframe').onload = "
7350 " function() { window.top.postMessage('loaded', '*'); };"));
7351 EXPECT_TRUE(ExecJs(shell(), "document.title = 'not loaded';"));
lukasza8e1c02e42016-05-17 20:05:107352 base::string16 expected_title(base::UTF8ToUTF16("loaded"));
7353 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
7354
7355 // Try to navigate the subframe to a blocked URL.
7356 TestNavigationObserver load_observer2(shell()->web_contents());
7357 GURL blocked_url = embedded_test_server()->GetURL("c.com", "/title3.html");
Nick Carterb7e71312018-08-03 23:36:137358 EXPECT_TRUE(ExecJs(navigating_frame,
7359 JsReplace("window.location.href = $1;", blocked_url)));
lukasza8e1c02e42016-05-17 20:05:107360
7361 // The blocked frame should still fire a load event in its parent's process.
7362 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
7363
7364 // Check that the current RenderFrameHost has stopped loading.
Nasko Oskov63dda2952018-02-09 19:35:427365 if (navigating_frame->current_frame_host()->is_loading())
lukasza8e1c02e42016-05-17 20:05:107366 load_observer2.Wait();
lukasza8e1c02e42016-05-17 20:05:107367
arthursonzogni7fed384c2017-03-18 03:07:347368 // The last successful url shouldn't be the blocked url.
7369 EXPECT_EQ(old_subframe_url,
7370 navigating_frame->current_frame_host()->last_successful_url());
lukasza8e1c02e42016-05-17 20:05:107371
Nasko Oskov63dda2952018-02-09 19:35:427372 // The blocked frame should go to an error page. Errors currently commit
7373 // with the URL of the blocked page.
7374 EXPECT_EQ(blocked_url, navigating_frame->current_url());
arthursonzogni7fed384c2017-03-18 03:07:347375
Nasko Oskov63dda2952018-02-09 19:35:427376 // The page should get the title of an error page (i.e "Error") and not the
7377 // title of the blocked page.
Nick Carterb7e71312018-08-03 23:36:137378 EXPECT_EQ("Error", EvalJs(navigating_frame, "document.title"));
lukasza8e1c02e42016-05-17 20:05:107379
7380 // Navigate the subframe to a URL without CSP.
7381 NavigateFrameToURL(srcdoc_frame,
7382 embedded_test_server()->GetURL("a.com", "/title1.html"));
7383
7384 // Verify that the frame's CSP got correctly reset to an empty set.
7385 EXPECT_EQ(
7386 0u,
7387 srcdoc_frame->current_replication_state().accumulated_csp_headers.size());
7388}
7389
lfgdb5c4ed2016-03-04 23:09:077390IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, ScreenCoordinates) {
7391 GURL main_url(embedded_test_server()->GetURL(
7392 "a.com", "/cross_site_iframe_factory.html?a(b)"));
davidsac6e6c35e42016-11-21 19:45:577393 EXPECT_TRUE(NavigateToURL(shell(), main_url));
lfgdb5c4ed2016-03-04 23:09:077394
ekaramadfd1b5cfa2016-04-19 00:35:007395 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
lfgdb5c4ed2016-03-04 23:09:077396 FrameTreeNode* child = root->child_at(0);
7397
7398 const char* properties[] = {"screenX", "screenY", "outerWidth",
7399 "outerHeight"};
7400
7401 for (const char* property : properties) {
Nick Carterb7e71312018-08-03 23:36:137402 std::string script = base::StringPrintf("window.%s;", property);
7403 int root_value = EvalJs(root, script).ExtractInt();
7404 int child_value = EvalJs(child, script).ExtractInt();
lfgdb5c4ed2016-03-04 23:09:077405 EXPECT_EQ(root_value, child_value);
7406 }
7407}
7408
nasko7ecc9a62016-03-17 00:22:227409// Tests that the swapped out state on RenderViewHost is properly reset when
7410// the main frame is navigated to the same SiteInstance as one of its child
7411// frames.
7412IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
7413 NavigateMainFrameToChildSite) {
7414 GURL main_url(embedded_test_server()->GetURL(
7415 "a.com", "/cross_site_iframe_factory.html?a(b)"));
7416 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7417
ekaramadfd1b5cfa2016-04-19 00:35:007418 WebContentsImpl* contents = web_contents();
nasko7ecc9a62016-03-17 00:22:227419 FrameTreeNode* root = contents->GetFrameTree()->root();
7420 EXPECT_EQ(1U, root->child_count());
7421
7422 // Ensure the RenderViewHost for the SiteInstance of the child is considered
7423 // in swapped out state.
7424 RenderViewHostImpl* rvh = contents->GetFrameTree()->GetRenderViewHost(
7425 root->child_at(0)->current_frame_host()->GetSiteInstance());
7426 EXPECT_TRUE(rvh->is_swapped_out_);
7427
7428 // Have the child frame navigate its parent to its SiteInstance.
7429 GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
danakj824a7ff2019-02-07 20:34:027430 auto script = JsReplace("parent.location = $1", b_url);
nasko7ecc9a62016-03-17 00:22:227431
japhet5d8d5ce2016-10-26 17:38:147432 // Ensure the child has received a user gesture, so that it has permission
7433 // to framebust.
7434 SimulateMouseClick(
7435 root->child_at(0)->current_frame_host()->GetRenderWidgetHost(), 1, 1);
nasko7ecc9a62016-03-17 00:22:227436 TestFrameNavigationObserver frame_observer(root);
nickadef4a52016-06-09 18:45:547437 EXPECT_TRUE(ExecuteScript(root->child_at(0), script));
nasko7ecc9a62016-03-17 00:22:227438 frame_observer.Wait();
7439 EXPECT_EQ(b_url, root->current_url());
7440
7441 // Verify that the same RenderViewHost is preserved and that it is no longer
7442 // in swapped out state.
7443 EXPECT_EQ(rvh, contents->GetFrameTree()->GetRenderViewHost(
7444 root->current_frame_host()->GetSiteInstance()));
7445 EXPECT_FALSE(rvh->is_swapped_out_);
7446}
7447
alexmos9aa61232016-04-26 21:54:027448// Test for https://crbug.com/568836. From an A-embed-B page, navigate the
7449// subframe from B to A. This cleans up the process for B, but the test delays
7450// the browser side from killing the B process right away. This allows the
Albert J. Wong3c93c182018-09-27 17:29:437451// B process to process two WidgetMsg_Close messages sent to the subframe's
alexmos9aa61232016-04-26 21:54:027452// RenderWidget and to the RenderView, in that order. In the bug, the latter
7453// crashed while detaching the subframe's LocalFrame (triggered as part of
7454// closing the RenderView), because this tried to access the subframe's
7455// WebFrameWidget (from RenderFrameImpl::didChangeSelection), which had already
7456// been cleared by the former.
7457IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
7458 CloseSubframeWidgetAndViewOnProcessExit) {
7459 GURL main_url(embedded_test_server()->GetURL(
7460 "a.com", "/cross_site_iframe_factory.html?a(b)"));
7461 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7462
7463 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
7464 ->GetFrameTree()
7465 ->root();
7466
7467 // "Select all" in the subframe. The bug only happens if there's a selection
7468 // change, which triggers the path through didChangeSelection.
dtapuska9d46ef7d2017-05-26 19:06:067469 root->child_at(0)->current_frame_host()->GetFrameInputHandler()->SelectAll();
alexmos9aa61232016-04-26 21:54:027470
7471 // Prevent b.com process from terminating right away once the subframe
7472 // navigates away from b.com below. This is necessary so that the renderer
7473 // process has time to process the closings of RenderWidget and RenderView,
Yutaka Hirano09a9afb92017-08-16 09:11:457474 // which is where the original bug was triggered. Incrementing the keep alive
7475 // ref count will cause RenderProcessHostImpl::Cleanup to forego process
alexmos9aa61232016-04-26 21:54:027476 // termination.
7477 RenderProcessHost* subframe_process =
7478 root->child_at(0)->current_frame_host()->GetProcess();
Shubhie Panickerc226de342018-03-27 20:54:127479 subframe_process->IncrementKeepAliveRefCount(
7480 RenderProcessHostImpl::KeepAliveClientType::kFetch);
alexmos9aa61232016-04-26 21:54:027481
7482 // Navigate the subframe away from b.com. Since this is the last active
7483 // frame in the b.com process, this causes the RenderWidget and RenderView to
Arthur Sonzogni4973b392018-08-20 18:14:457484 // be closed.
alexmos9aa61232016-04-26 21:54:027485 NavigateFrameToURL(root->child_at(0),
7486 embedded_test_server()->GetURL("a.com", "/title1.html"));
alexmos9aa61232016-04-26 21:54:027487
Arthur Sonzogni4973b392018-08-20 18:14:457488 // Release the process.
7489 RenderProcessHostWatcher process_shutdown_observer(
7490 subframe_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Shubhie Panickerc226de342018-03-27 20:54:127491 subframe_process->DecrementKeepAliveRefCount(
7492 RenderProcessHostImpl::KeepAliveClientType::kFetch);
Arthur Sonzogni4973b392018-08-20 18:14:457493 process_shutdown_observer.Wait();
alexmos9aa61232016-04-26 21:54:027494}
7495
kenrb19221852016-04-29 17:21:407496// Tests that an input event targeted to a out-of-process iframe correctly
7497// triggers a user interaction notification for WebContentsObservers.
7498// This is used for browser features such as download request limiting and
7499// launching multiple external protocol handlers, which can block repeated
7500// actions from a page when a user is not interacting with the page.
7501IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
7502 UserInteractionForChildFrameTest) {
7503 GURL main_url(embedded_test_server()->GetURL(
7504 "a.com", "/cross_site_iframe_factory.html?a(b)"));
7505 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7506
7507 UserInteractionObserver observer(web_contents());
7508
7509 // Target an event to the child frame's RenderWidgetHostView.
7510 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
7511 SimulateMouseClick(
7512 root->child_at(0)->current_frame_host()->GetRenderWidgetHost(), 5, 5);
7513
7514 EXPECT_TRUE(observer.WasUserInteractionReceived());
7515
7516 // Target an event to the main frame.
7517 observer.Reset();
7518 SimulateMouseClick(root->current_frame_host()->GetRenderWidgetHost(), 1, 1);
7519
7520 EXPECT_TRUE(observer.WasUserInteractionReceived());
7521}
7522
nasko58b07f52016-05-09 22:38:357523// Ensures that navigating to data: URLs present in session history will
Charlie Reis0bb3f5c72018-08-06 22:46:017524// correctly commit the navigation in the same process as the one used for the
7525// original navigation. See https://crbug.com/606996.
nasko58b07f52016-05-09 22:38:357526IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
7527 NavigateSubframeToDataUrlInSessionHistory) {
7528 GURL main_url(embedded_test_server()->GetURL(
7529 "a.com", "/cross_site_iframe_factory.html?a(b,b)"));
7530 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7531
7532 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
7533 EXPECT_EQ(2U, root->child_count());
7534 EXPECT_EQ(
7535 " Site A ------------ proxies for B\n"
7536 " |--Site B ------- proxies for A\n"
7537 " +--Site B ------- proxies for A\n"
7538 "Where A = http://a.com/\n"
7539 " B = http://b.com/",
7540 DepictFrameTree(root));
7541
7542 TestNavigationObserver observer(shell()->web_contents());
7543 FrameTreeNode* child = root->child_at(0);
7544
7545 // Navigate iframe to a data URL, which will commit in a new SiteInstance.
7546 GURL data_url("data:text/html,dataurl");
7547 NavigateFrameToURL(child, data_url);
7548 EXPECT_TRUE(observer.last_navigation_succeeded());
7549 EXPECT_EQ(data_url, observer.last_navigation_url());
7550 scoped_refptr<SiteInstanceImpl> orig_site_instance =
Charlie Reis0bb3f5c72018-08-06 22:46:017551 child->current_frame_host()->GetSiteInstance();
nasko58b07f52016-05-09 22:38:357552 EXPECT_NE(root->current_frame_host()->GetSiteInstance(), orig_site_instance);
7553
7554 // Navigate it to another cross-site url.
7555 GURL cross_site_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
7556 NavigateFrameToURL(child, cross_site_url);
7557 EXPECT_TRUE(observer.last_navigation_succeeded());
7558 EXPECT_EQ(cross_site_url, observer.last_navigation_url());
7559 EXPECT_EQ(3, web_contents()->GetController().GetEntryCount());
7560 EXPECT_NE(orig_site_instance, child->current_frame_host()->GetSiteInstance());
7561
7562 // Go back and ensure the data: URL committed in the same SiteInstance as the
7563 // original navigation.
7564 EXPECT_TRUE(web_contents()->GetController().CanGoBack());
7565 TestFrameNavigationObserver frame_observer(child);
7566 web_contents()->GetController().GoBack();
7567 frame_observer.WaitForCommit();
7568 EXPECT_EQ(orig_site_instance, child->current_frame_host()->GetSiteInstance());
7569}
7570
Charlie Reis0bb3f5c72018-08-06 22:46:017571// Ensures that subframes navigated to data: URLs start in a process based on
7572// their creator, but end up in unique processes after a restore (since
7573// SiteInstance relationships are not preserved on restore, until
7574// https://crbug.com/14987 is fixed). This is better than restoring into the
7575// parent process, per https://crbug.com/863069.
7576IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
7577 SubframeDataUrlsAfterRestore) {
7578 // We must use a page that has iframes in the HTML here, unlike
7579 // cross_site_iframe_factory.html which loads them dynamically. In the latter
7580 // case, Chrome will not restore subframe URLs from history, which is needed
7581 // for this test.
7582 GURL main_url(embedded_test_server()->GetURL(
7583 "a.com", "/frame_tree/page_with_two_iframes.html"));
7584 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7585
7586 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
7587 EXPECT_EQ(2U, root->child_count());
7588 EXPECT_EQ(
7589 " Site A ------------ proxies for B C\n"
7590 " |--Site B ------- proxies for A C\n"
7591 " +--Site C ------- proxies for A B\n"
7592 "Where A = http://a.com/\n"
7593 " B = http://bar.com/\n"
7594 " C = http://baz.com/",
7595 DepictFrameTree(root));
7596
7597 FrameTreeNode* child_0 = root->child_at(0);
7598 FrameTreeNode* child_1 = root->child_at(1);
7599 scoped_refptr<SiteInstanceImpl> child_site_instance_0 =
7600 child_0->current_frame_host()->GetSiteInstance();
7601 scoped_refptr<SiteInstanceImpl> child_site_instance_1 =
7602 child_1->current_frame_host()->GetSiteInstance();
7603
7604 // Navigate the iframes to data URLs via renderer initiated navigations, which
7605 // will commit in the existing SiteInstances.
7606 TestNavigationObserver observer(shell()->web_contents());
7607 GURL data_url_0("data:text/html,dataurl_0");
7608 {
7609 TestFrameNavigationObserver commit_observer(child_0);
7610 EXPECT_TRUE(
danakj824a7ff2019-02-07 20:34:027611 ExecuteScript(child_0, JsReplace("location.href = $1", data_url_0)));
Charlie Reis0bb3f5c72018-08-06 22:46:017612 commit_observer.WaitForCommit();
7613 }
7614 EXPECT_TRUE(observer.last_navigation_succeeded());
7615 EXPECT_EQ(data_url_0, observer.last_navigation_url());
7616 EXPECT_EQ(child_site_instance_0,
7617 child_0->current_frame_host()->GetSiteInstance());
7618
7619 GURL data_url_1("data:text/html,dataurl_1");
7620 {
7621 TestFrameNavigationObserver commit_observer(child_1);
7622 EXPECT_TRUE(
danakj824a7ff2019-02-07 20:34:027623 ExecuteScript(child_1, JsReplace("location.href = $1", data_url_1)));
Charlie Reis0bb3f5c72018-08-06 22:46:017624 commit_observer.WaitForCommit();
7625 }
7626 EXPECT_TRUE(observer.last_navigation_succeeded());
7627 EXPECT_EQ(data_url_1, observer.last_navigation_url());
7628 EXPECT_EQ(child_site_instance_1,
7629 child_1->current_frame_host()->GetSiteInstance());
7630
7631 // Grab the NavigationEntry and clone its PageState into a new entry for
7632 // restoring into a new tab.
Lucas Furukawa Gadani5553a152019-01-08 18:55:577633 NavigationControllerImpl& controller = static_cast<NavigationControllerImpl&>(
7634 shell()->web_contents()->GetController());
Charlie Reis0bb3f5c72018-08-06 22:46:017635 NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
7636 std::unique_ptr<NavigationEntryImpl> restored_entry =
7637 NavigationEntryImpl::FromNavigationEntry(
7638 NavigationController::CreateNavigationEntry(
7639 main_url, Referrer(), ui::PAGE_TRANSITION_RELOAD, false,
7640 std::string(), controller.GetBrowserContext(),
7641 nullptr /* blob_url_loader_factory */));
7642 EXPECT_EQ(0U, restored_entry->root_node()->children.size());
7643 restored_entry->SetPageState(entry->GetPageState());
7644 ASSERT_EQ(2U, restored_entry->root_node()->children.size());
7645
7646 // Restore the NavigationEntry into a new tab and check that the data URLs are
7647 // not loaded into the parent's SiteInstance.
7648 std::vector<std::unique_ptr<NavigationEntry>> entries;
7649 entries.push_back(std::move(restored_entry));
7650 Shell* new_shell = Shell::CreateNewWindow(
7651 controller.GetBrowserContext(), GURL::EmptyGURL(), nullptr, gfx::Size());
7652 FrameTreeNode* new_root =
7653 static_cast<WebContentsImpl*>(new_shell->web_contents())
7654 ->GetFrameTree()
7655 ->root();
7656 NavigationControllerImpl& new_controller =
7657 static_cast<NavigationControllerImpl&>(
7658 new_shell->web_contents()->GetController());
7659 new_controller.Restore(entries.size() - 1,
7660 RestoreType::LAST_SESSION_EXITED_CLEANLY, &entries);
7661 ASSERT_EQ(0u, entries.size());
7662 {
7663 TestNavigationObserver restore_observer(new_shell->web_contents());
7664 new_controller.LoadIfNecessary();
7665 restore_observer.Wait();
7666 }
7667 ASSERT_EQ(2U, new_root->child_count());
7668 EXPECT_EQ(main_url, new_root->current_url());
7669 EXPECT_EQ("data", new_root->child_at(0)->current_url().scheme());
7670 EXPECT_EQ("data", new_root->child_at(1)->current_url().scheme());
7671
7672 EXPECT_NE(new_root->current_frame_host()->GetSiteInstance(),
7673 new_root->child_at(0)->current_frame_host()->GetSiteInstance());
7674 EXPECT_NE(new_root->current_frame_host()->GetSiteInstance(),
7675 new_root->child_at(1)->current_frame_host()->GetSiteInstance());
7676 EXPECT_NE(new_root->child_at(0)->current_frame_host()->GetSiteInstance(),
7677 new_root->child_at(1)->current_frame_host()->GetSiteInstance());
7678}
7679
7680// Similar to SubframeDataUrlsAfterRestore, but ensures that about:blank frames
7681// do get put into their parent process after restore, even if they weren't
7682// originally. This is safe because they do not contain active content (even
7683// when there's a fragment in the URL), and it avoids unnecessary OOPIFs.
7684IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
7685 SubframeBlankUrlsAfterRestore) {
7686 // We must use a page that has iframes in the HTML here, unlike
7687 // cross_site_iframe_factory.html which loads them dynamically. In the latter
7688 // case, Chrome will not restore subframe URLs from history, which is needed
7689 // for this test.
7690 GURL main_url(embedded_test_server()->GetURL(
7691 "a.com", "/frame_tree/page_with_two_iframes.html"));
7692 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7693
7694 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
7695 EXPECT_EQ(2U, root->child_count());
7696 EXPECT_EQ(
7697 " Site A ------------ proxies for B C\n"
7698 " |--Site B ------- proxies for A C\n"
7699 " +--Site C ------- proxies for A B\n"
7700 "Where A = http://a.com/\n"
7701 " B = http://bar.com/\n"
7702 " C = http://baz.com/",
7703 DepictFrameTree(root));
7704
7705 FrameTreeNode* child_0 = root->child_at(0);
7706 FrameTreeNode* child_1 = root->child_at(1);
7707 scoped_refptr<SiteInstanceImpl> child_site_instance_0 =
7708 child_0->current_frame_host()->GetSiteInstance();
7709 scoped_refptr<SiteInstanceImpl> child_site_instance_1 =
7710 child_1->current_frame_host()->GetSiteInstance();
7711
7712 // Navigate the iframes to about:blank URLs via renderer initiated
7713 // navigations, which will commit in the existing SiteInstances.
7714 TestNavigationObserver observer(shell()->web_contents());
7715 GURL blank_url("about:blank");
7716 {
7717 TestFrameNavigationObserver commit_observer(child_0);
7718 EXPECT_TRUE(
danakj824a7ff2019-02-07 20:34:027719 ExecuteScript(child_0, JsReplace("location.href = $1", blank_url)));
Charlie Reis0bb3f5c72018-08-06 22:46:017720 commit_observer.WaitForCommit();
7721 }
7722 EXPECT_TRUE(observer.last_navigation_succeeded());
7723 EXPECT_EQ(blank_url, observer.last_navigation_url());
7724 EXPECT_EQ(child_site_instance_0,
7725 child_0->current_frame_host()->GetSiteInstance());
7726
7727 GURL blank_url_ref("about:blank#1");
7728 {
7729 TestFrameNavigationObserver commit_observer(child_1);
danakj824a7ff2019-02-07 20:34:027730 EXPECT_TRUE(
7731 ExecuteScript(child_1, JsReplace("location.href = $1", blank_url_ref)));
Charlie Reis0bb3f5c72018-08-06 22:46:017732 commit_observer.WaitForCommit();
7733 }
7734 EXPECT_TRUE(observer.last_navigation_succeeded());
7735 EXPECT_EQ(blank_url_ref, observer.last_navigation_url());
7736 EXPECT_EQ(child_site_instance_1,
7737 child_1->current_frame_host()->GetSiteInstance());
7738
7739 // Grab the NavigationEntry and clone its PageState into a new entry for
7740 // restoring into a new tab.
Lucas Furukawa Gadani5553a152019-01-08 18:55:577741 NavigationControllerImpl& controller = static_cast<NavigationControllerImpl&>(
7742 shell()->web_contents()->GetController());
Charlie Reis0bb3f5c72018-08-06 22:46:017743 NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
7744 std::unique_ptr<NavigationEntryImpl> restored_entry =
7745 NavigationEntryImpl::FromNavigationEntry(
7746 NavigationController::CreateNavigationEntry(
7747 main_url, Referrer(), ui::PAGE_TRANSITION_RELOAD, false,
7748 std::string(), controller.GetBrowserContext(),
7749 nullptr /* blob_url_loader_factory */));
7750 EXPECT_EQ(0U, restored_entry->root_node()->children.size());
7751 restored_entry->SetPageState(entry->GetPageState());
7752 ASSERT_EQ(2U, restored_entry->root_node()->children.size());
7753
7754 // Restore the NavigationEntry into a new tab and check that the data URLs are
7755 // not loaded into the parent's SiteInstance.
7756 std::vector<std::unique_ptr<NavigationEntry>> entries;
7757 entries.push_back(std::move(restored_entry));
7758 Shell* new_shell = Shell::CreateNewWindow(
7759 controller.GetBrowserContext(), GURL::EmptyGURL(), nullptr, gfx::Size());
7760 FrameTreeNode* new_root =
7761 static_cast<WebContentsImpl*>(new_shell->web_contents())
7762 ->GetFrameTree()
7763 ->root();
7764 NavigationControllerImpl& new_controller =
7765 static_cast<NavigationControllerImpl&>(
7766 new_shell->web_contents()->GetController());
7767 new_controller.Restore(entries.size() - 1,
7768 RestoreType::LAST_SESSION_EXITED_CLEANLY, &entries);
7769 ASSERT_EQ(0u, entries.size());
7770 {
7771 TestNavigationObserver restore_observer(new_shell->web_contents());
7772 new_controller.LoadIfNecessary();
7773 restore_observer.Wait();
7774 }
7775 ASSERT_EQ(2U, new_root->child_count());
7776 EXPECT_EQ(main_url, new_root->current_url());
7777 EXPECT_TRUE(new_root->child_at(0)->current_url().IsAboutBlank());
7778 EXPECT_TRUE(new_root->child_at(1)->current_url().IsAboutBlank());
7779
7780 EXPECT_EQ(new_root->current_frame_host()->GetSiteInstance(),
7781 new_root->child_at(0)->current_frame_host()->GetSiteInstance());
7782 EXPECT_EQ(new_root->current_frame_host()->GetSiteInstance(),
7783 new_root->child_at(1)->current_frame_host()->GetSiteInstance());
7784}
7785
7786// Similar to SubframeBlankUrlsAfterRestore, but ensures that about:srcdoc ends
7787// up in its parent's process after restore, since that's where its content
7788// comes from.
7789IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
7790 SubframeSrcdocUrlAfterRestore) {
7791 // Load a page that uses iframe srcdoc.
7792 GURL main_url(embedded_test_server()->GetURL(
7793 "a.com", "/frame_tree/page_with_srcdoc_frame.html"));
7794 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7795
7796 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
7797 EXPECT_EQ(1U, root->child_count());
7798 FrameTreeNode* child = root->child_at(0);
7799 scoped_refptr<SiteInstanceImpl> child_site_instance =
7800 child->current_frame_host()->GetSiteInstance();
7801 EXPECT_EQ(child_site_instance, root->current_frame_host()->GetSiteInstance());
7802
7803 // Grab the NavigationEntry and clone its PageState into a new entry for
7804 // restoring into a new tab.
Lucas Furukawa Gadani5553a152019-01-08 18:55:577805 NavigationControllerImpl& controller = static_cast<NavigationControllerImpl&>(
7806 shell()->web_contents()->GetController());
Charlie Reis0bb3f5c72018-08-06 22:46:017807 NavigationEntryImpl* entry = controller.GetLastCommittedEntry();
7808 std::unique_ptr<NavigationEntryImpl> restored_entry =
7809 NavigationEntryImpl::FromNavigationEntry(
7810 NavigationController::CreateNavigationEntry(
7811 main_url, Referrer(), ui::PAGE_TRANSITION_RELOAD, false,
7812 std::string(), controller.GetBrowserContext(),
7813 nullptr /* blob_url_loader_factory */));
7814 EXPECT_EQ(0U, restored_entry->root_node()->children.size());
7815 restored_entry->SetPageState(entry->GetPageState());
7816 ASSERT_EQ(1U, restored_entry->root_node()->children.size());
7817
7818 // Restore the NavigationEntry into a new tab and check that the srcdoc URLs
7819 // are still loaded into the parent's SiteInstance.
7820 std::vector<std::unique_ptr<NavigationEntry>> entries;
7821 entries.push_back(std::move(restored_entry));
7822 Shell* new_shell = Shell::CreateNewWindow(
7823 controller.GetBrowserContext(), GURL::EmptyGURL(), nullptr, gfx::Size());
7824 FrameTreeNode* new_root =
7825 static_cast<WebContentsImpl*>(new_shell->web_contents())
7826 ->GetFrameTree()
7827 ->root();
7828 NavigationControllerImpl& new_controller =
7829 static_cast<NavigationControllerImpl&>(
7830 new_shell->web_contents()->GetController());
7831 new_controller.Restore(entries.size() - 1,
7832 RestoreType::LAST_SESSION_EXITED_CLEANLY, &entries);
7833 ASSERT_EQ(0u, entries.size());
7834 {
7835 TestNavigationObserver restore_observer(new_shell->web_contents());
7836 new_controller.LoadIfNecessary();
7837 restore_observer.Wait();
7838 }
7839 ASSERT_EQ(1U, new_root->child_count());
7840 EXPECT_EQ(main_url, new_root->current_url());
7841 EXPECT_EQ(GURL(content::kAboutSrcDocURL),
7842 new_root->child_at(0)->current_url());
7843
7844 EXPECT_EQ(new_root->current_frame_host()->GetSiteInstance(),
7845 new_root->child_at(0)->current_frame_host()->GetSiteInstance());
7846}
7847
nasko58b07f52016-05-09 22:38:357848// Ensures that navigating to about:blank URLs present in session history will
7849// correctly commit the navigation in the same process as the one used for
7850// the original navigation.
7851IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
7852 NavigateSubframeToAboutBlankInSessionHistory) {
7853 GURL main_url(embedded_test_server()->GetURL(
7854 "a.com", "/cross_site_iframe_factory.html?a(b,b)"));
7855 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7856
7857 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
7858 EXPECT_EQ(2U, root->child_count());
7859 EXPECT_EQ(
7860 " Site A ------------ proxies for B\n"
7861 " |--Site B ------- proxies for A\n"
7862 " +--Site B ------- proxies for A\n"
7863 "Where A = http://a.com/\n"
7864 " B = http://b.com/",
7865 DepictFrameTree(root));
7866
7867 TestNavigationObserver observer(shell()->web_contents());
7868 FrameTreeNode* child = root->child_at(0);
7869
7870 // Navigate iframe to about:blank, which will commit in a new SiteInstance.
7871 GURL about_blank_url("about:blank");
7872 NavigateFrameToURL(child, about_blank_url);
7873 EXPECT_TRUE(observer.last_navigation_succeeded());
7874 EXPECT_EQ(about_blank_url, observer.last_navigation_url());
7875 scoped_refptr<SiteInstanceImpl> orig_site_instance =
7876 child->current_frame_host()->GetSiteInstance();
7877 EXPECT_NE(root->current_frame_host()->GetSiteInstance(), orig_site_instance);
7878
7879 // Navigate it to another cross-site url.
7880 GURL cross_site_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
7881 NavigateFrameToURL(child, cross_site_url);
7882 EXPECT_TRUE(observer.last_navigation_succeeded());
7883 EXPECT_EQ(cross_site_url, observer.last_navigation_url());
7884 EXPECT_EQ(3, web_contents()->GetController().GetEntryCount());
7885 EXPECT_NE(orig_site_instance, child->current_frame_host()->GetSiteInstance());
7886
7887 // Go back and ensure the about:blank URL committed in the same SiteInstance
7888 // as the original navigation.
7889 EXPECT_TRUE(web_contents()->GetController().CanGoBack());
7890 TestFrameNavigationObserver frame_observer(child);
7891 web_contents()->GetController().GoBack();
7892 frame_observer.WaitForCommit();
7893 EXPECT_EQ(orig_site_instance, child->current_frame_host()->GetSiteInstance());
7894}
7895
nick5ae4d2d2017-01-06 01:18:357896// Helper filter class to wait for a ShowCreatedWindow or ShowWidget message,
7897// record the routing ID from the message, and then drop the message.
7898const uint32_t kMessageClasses[] = {ViewMsgStart, FrameMsgStart};
alexmosc2a8cec2016-05-23 22:19:537899class PendingWidgetMessageFilter : public BrowserMessageFilter {
7900 public:
7901 PendingWidgetMessageFilter()
Avi Drissman9d3ded92018-12-25 20:50:217902 : BrowserMessageFilter(kMessageClasses, base::size(kMessageClasses)),
Lukasz Anforowicz5a92f052018-08-15 20:38:477903 routing_id_(MSG_ROUTING_NONE) {}
alexmosc2a8cec2016-05-23 22:19:537904
7905 bool OnMessageReceived(const IPC::Message& message) override {
7906 bool handled = true;
7907 IPC_BEGIN_MESSAGE_MAP(PendingWidgetMessageFilter, message)
nick5ae4d2d2017-01-06 01:18:357908 IPC_MESSAGE_HANDLER(FrameHostMsg_ShowCreatedWindow, OnShowCreatedWindow)
alexmosc2a8cec2016-05-23 22:19:537909 IPC_MESSAGE_HANDLER(ViewHostMsg_ShowWidget, OnShowWidget)
7910 IPC_MESSAGE_UNHANDLED(handled = false)
7911 IPC_END_MESSAGE_MAP()
7912 return handled;
7913 }
7914
Lukasz Anforowicz5a92f052018-08-15 20:38:477915 void Wait() { run_loop_.Run(); }
alexmosc2a8cec2016-05-23 22:19:537916
7917 int routing_id() { return routing_id_; }
7918
7919 private:
7920 ~PendingWidgetMessageFilter() override {}
7921
nick5ae4d2d2017-01-06 01:18:357922 void OnShowCreatedWindow(int pending_widget_routing_id,
7923 WindowOpenDisposition disposition,
7924 const gfx::Rect& initial_rect,
7925 bool user_gesture) {
Eric Seckler8652dcd52018-09-20 10:42:287926 base::PostTaskWithTraits(
7927 FROM_HERE, {content::BrowserThread::UI},
tzike2aca992017-09-05 08:50:547928 base::BindOnce(&PendingWidgetMessageFilter::OnReceivedRoutingIDOnUI,
7929 this, pending_widget_routing_id));
alexmosc2a8cec2016-05-23 22:19:537930 }
7931
7932 void OnShowWidget(int routing_id, const gfx::Rect& initial_rect) {
Eric Seckler8652dcd52018-09-20 10:42:287933 base::PostTaskWithTraits(
7934 FROM_HERE, {content::BrowserThread::UI},
tzike2aca992017-09-05 08:50:547935 base::BindOnce(&PendingWidgetMessageFilter::OnReceivedRoutingIDOnUI,
7936 this, routing_id));
alexmosc2a8cec2016-05-23 22:19:537937 }
7938
nick5ae4d2d2017-01-06 01:18:357939 void OnReceivedRoutingIDOnUI(int widget_routing_id) {
7940 routing_id_ = widget_routing_id;
Lukasz Anforowicz5a92f052018-08-15 20:38:477941 run_loop_.Quit();
alexmosc2a8cec2016-05-23 22:19:537942 }
7943
7944 int routing_id_;
Lukasz Anforowicz5a92f052018-08-15 20:38:477945 base::RunLoop run_loop_;
alexmosc2a8cec2016-05-23 22:19:537946
7947 DISALLOW_COPY_AND_ASSIGN(PendingWidgetMessageFilter);
7948};
7949
7950// Test for https://crbug.com/612276. Simultaneously open two new windows from
7951// two subframes in different processes, where each subframe process's next
7952// routing ID is the same. Make sure that both windows are created properly.
7953//
7954// Each new window requires two IPCs to first create it (handled by
7955// CreateNewWindow) and then show it (ShowCreatedWindow). In the bug, both
7956// CreateNewWindow calls arrived before the ShowCreatedWindow calls, resulting
7957// in the two pending windows colliding in the pending WebContents map, which
7958// used to be keyed only by routing_id.
7959IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
7960 TwoSubframesCreatePopupsSimultaneously) {
7961 GURL main_url(embedded_test_server()->GetURL(
7962 "a.com", "/cross_site_iframe_factory.html?a(b,c)"));
7963 EXPECT_TRUE(NavigateToURL(shell(), main_url));
7964
7965 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
7966 FrameTreeNode* child1 = root->child_at(0);
7967 FrameTreeNode* child2 = root->child_at(1);
7968 RenderProcessHost* process1 = child1->current_frame_host()->GetProcess();
7969 RenderProcessHost* process2 = child2->current_frame_host()->GetProcess();
7970
7971 // Call window.open simultaneously in both subframes to create two popups.
nick5ae4d2d2017-01-06 01:18:357972 // Wait for and then drop both FrameHostMsg_ShowCreatedWindow messages. This
7973 // will ensure that both CreateNewWindow calls happen before either
alexmosc2a8cec2016-05-23 22:19:537974 // ShowCreatedWindow call.
7975 scoped_refptr<PendingWidgetMessageFilter> filter1 =
7976 new PendingWidgetMessageFilter();
7977 process1->AddFilter(filter1.get());
nickadef4a52016-06-09 18:45:547978 EXPECT_TRUE(ExecuteScript(child1, "window.open();"));
alexmosc2a8cec2016-05-23 22:19:537979 filter1->Wait();
7980
7981 scoped_refptr<PendingWidgetMessageFilter> filter2 =
7982 new PendingWidgetMessageFilter();
7983 process2->AddFilter(filter2.get());
nickadef4a52016-06-09 18:45:547984 EXPECT_TRUE(ExecuteScript(child2, "window.open();"));
alexmosc2a8cec2016-05-23 22:19:537985 filter2->Wait();
7986
7987 // At this point, we should have two pending WebContents.
Jan Wilken Dörrie77c581a2019-06-07 16:25:067988 EXPECT_TRUE(base::Contains(
skyostil66bd67912016-08-12 12:33:117989 web_contents()->pending_contents_,
Lukasz Anforowicz09060bdf72018-08-23 15:53:177990 GlobalRoutingID(process1->GetID(), filter1->routing_id())));
Jan Wilken Dörrie77c581a2019-06-07 16:25:067991 EXPECT_TRUE(base::Contains(
skyostil66bd67912016-08-12 12:33:117992 web_contents()->pending_contents_,
Lukasz Anforowicz09060bdf72018-08-23 15:53:177993 GlobalRoutingID(process2->GetID(), filter2->routing_id())));
alexmosc2a8cec2016-05-23 22:19:537994
7995 // Both subframes were set up in the same way, so the next routing ID for the
7996 // new popup windows should match up (this led to the collision in the
7997 // pending contents map in the original bug).
7998 EXPECT_EQ(filter1->routing_id(), filter2->routing_id());
7999
nick5ae4d2d2017-01-06 01:18:358000 // Now, simulate that both FrameHostMsg_ShowCreatedWindow messages arrive by
8001 // showing both of the pending WebContents.
alexmosc2a8cec2016-05-23 22:19:538002 web_contents()->ShowCreatedWindow(process1->GetID(), filter1->routing_id(),
nick3b04f322016-08-31 19:29:198003 WindowOpenDisposition::NEW_FOREGROUND_TAB,
8004 gfx::Rect(), true);
alexmosc2a8cec2016-05-23 22:19:538005 web_contents()->ShowCreatedWindow(process2->GetID(), filter2->routing_id(),
nick3b04f322016-08-31 19:29:198006 WindowOpenDisposition::NEW_FOREGROUND_TAB,
8007 gfx::Rect(), true);
alexmosc2a8cec2016-05-23 22:19:538008
8009 // Verify that both shells were properly created.
8010 EXPECT_EQ(3u, Shell::windows().size());
8011}
8012
8013// Test for https://crbug.com/612276. Similar to
8014// TwoSubframesOpenWindowsSimultaneously, but use popup menu widgets instead of
8015// windows.
8016//
8017// The plumbing that this test is verifying is not utilized on Mac/Android,
8018// where popup menus don't create a popup RenderWidget, but rather they trigger
8019// a FrameHostMsg_ShowPopup to ask the browser to build and display the actual
8020// popup using native controls.
8021#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
8022IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
8023 TwoSubframesCreatePopupMenuWidgetsSimultaneously) {
8024 GURL main_url(embedded_test_server()->GetURL(
8025 "a.com", "/cross_site_iframe_factory.html?a(b,c)"));
8026 EXPECT_TRUE(NavigateToURL(shell(), main_url));
8027
8028 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
8029 FrameTreeNode* child1 = root->child_at(0);
8030 FrameTreeNode* child2 = root->child_at(1);
8031 RenderProcessHost* process1 = child1->current_frame_host()->GetProcess();
8032 RenderProcessHost* process2 = child2->current_frame_host()->GetProcess();
8033
8034 // Navigate both subframes to a page with a <select> element.
8035 NavigateFrameToURL(child1, embedded_test_server()->GetURL(
8036 "b.com", "/site_isolation/page-with-select.html"));
8037 NavigateFrameToURL(child2, embedded_test_server()->GetURL(
8038 "c.com", "/site_isolation/page-with-select.html"));
8039
dtapuskae0f8ed72016-06-17 08:23:418040 // Open both <select> menus by focusing each item and sending a space key
8041 // at the focused node. This creates a popup widget in both processes.
alexmosc2a8cec2016-05-23 22:19:538042 // Wait for and then drop the ViewHostMsg_ShowWidget messages, so that both
8043 // widgets are left in pending-but-not-shown state.
Daniel Cheng93c80a92018-02-14 19:02:438044 NativeWebKeyboardEvent event(
8045 blink::WebKeyboardEvent::kChar, blink::WebInputEvent::kNoModifiers,
8046 blink::WebInputEvent::GetStaticTimeStampForTests());
dtapuskae0f8ed72016-06-17 08:23:418047 event.text[0] = ' ';
dtapuskae0f8ed72016-06-17 08:23:418048
alexmosc2a8cec2016-05-23 22:19:538049 scoped_refptr<PendingWidgetMessageFilter> filter1 =
8050 new PendingWidgetMessageFilter();
8051 process1->AddFilter(filter1.get());
dtapuskae0f8ed72016-06-17 08:23:418052 EXPECT_TRUE(ExecuteScript(child1, "focusSelectMenu();"));
8053 child1->current_frame_host()->GetRenderWidgetHost()->ForwardKeyboardEvent(
8054 event);
alexmosc2a8cec2016-05-23 22:19:538055 filter1->Wait();
8056
8057 scoped_refptr<PendingWidgetMessageFilter> filter2 =
8058 new PendingWidgetMessageFilter();
8059 process2->AddFilter(filter2.get());
dtapuskae0f8ed72016-06-17 08:23:418060 EXPECT_TRUE(ExecuteScript(child2, "focusSelectMenu();"));
8061 child2->current_frame_host()->GetRenderWidgetHost()->ForwardKeyboardEvent(
8062 event);
alexmosc2a8cec2016-05-23 22:19:538063 filter2->Wait();
8064
8065 // At this point, we should have two pending widgets.
Jan Wilken Dörrie77c581a2019-06-07 16:25:068066 EXPECT_TRUE(base::Contains(
skyostil66bd67912016-08-12 12:33:118067 web_contents()->pending_widget_views_,
Lukasz Anforowicz09060bdf72018-08-23 15:53:178068 GlobalRoutingID(process1->GetID(), filter1->routing_id())));
Jan Wilken Dörrie77c581a2019-06-07 16:25:068069 EXPECT_TRUE(base::Contains(
skyostil66bd67912016-08-12 12:33:118070 web_contents()->pending_widget_views_,
Lukasz Anforowicz09060bdf72018-08-23 15:53:178071 GlobalRoutingID(process2->GetID(), filter2->routing_id())));
alexmosc2a8cec2016-05-23 22:19:538072
8073 // Both subframes were set up in the same way, so the next routing ID for the
8074 // new popup widgets should match up (this led to the collision in the
8075 // pending widgets map in the original bug).
8076 EXPECT_EQ(filter1->routing_id(), filter2->routing_id());
8077
8078 // Now simulate both widgets being shown.
8079 web_contents()->ShowCreatedWidget(process1->GetID(), filter1->routing_id(),
8080 false, gfx::Rect());
8081 web_contents()->ShowCreatedWidget(process2->GetID(), filter2->routing_id(),
8082 false, gfx::Rect());
Jan Wilken Dörrie77c581a2019-06-07 16:25:068083 EXPECT_FALSE(base::Contains(
skyostil66bd67912016-08-12 12:33:118084 web_contents()->pending_widget_views_,
Lukasz Anforowicz09060bdf72018-08-23 15:53:178085 GlobalRoutingID(process1->GetID(), filter1->routing_id())));
Jan Wilken Dörrie77c581a2019-06-07 16:25:068086 EXPECT_FALSE(base::Contains(
skyostil66bd67912016-08-12 12:33:118087 web_contents()->pending_widget_views_,
Lukasz Anforowicz09060bdf72018-08-23 15:53:178088 GlobalRoutingID(process2->GetID(), filter2->routing_id())));
alexmosc2a8cec2016-05-23 22:19:538089}
8090#endif
8091
nasko39e3eb72016-06-24 23:15:448092// Test for https://crbug.com/615575. It ensures that file chooser triggered
8093// by a document in an out-of-process subframe works properly.
8094IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, FileChooserInSubframe) {
8095 EXPECT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
8096 "a.com", "/cross_site_iframe_factory.html?a(b)")));
8097 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
8098
8099 GURL url(embedded_test_server()->GetURL("b.com", "/file_input.html"));
8100 NavigateFrameToURL(root->child_at(0), url);
8101
8102 // Use FileChooserDelegate to avoid showing the actual dialog and to respond
8103 // back to the renderer process with predefined file.
Kent Tamura8c9e0562018-11-06 07:02:198104 base::RunLoop run_loop;
nasko39e3eb72016-06-24 23:15:448105 base::FilePath file;
Avi Drissman1cb5e9f2018-05-01 15:53:288106 EXPECT_TRUE(base::PathService::Get(base::DIR_TEMP, &file));
nasko39e3eb72016-06-24 23:15:448107 file = file.AppendASCII("bar");
Kent Tamura8c9e0562018-11-06 07:02:198108 std::unique_ptr<FileChooserDelegate> delegate(
8109 new FileChooserDelegate(file, run_loop.QuitClosure()));
nasko39e3eb72016-06-24 23:15:448110 shell()->web_contents()->SetDelegate(delegate.get());
8111 EXPECT_TRUE(ExecuteScript(root->child_at(0),
8112 "document.getElementById('fileinput').click();"));
Kent Tamura8c9e0562018-11-06 07:02:198113 run_loop.Run();
nasko39e3eb72016-06-24 23:15:448114
8115 // Also, extract the file from the renderer process to ensure that the
8116 // response made it over successfully and the proper filename is set.
Nick Carterb7e71312018-08-03 23:36:138117 EXPECT_EQ("bar",
8118 EvalJs(root->child_at(0),
8119 "document.getElementById('fileinput').files[0].name;"));
nasko39e3eb72016-06-24 23:15:448120}
8121
lfg717154072016-06-30 15:04:168122// Tests that an out-of-process iframe receives the visibilitychange event.
8123IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, VisibilityChange) {
8124 GURL main_url(embedded_test_server()->GetURL(
8125 "a.com", "/cross_site_iframe_factory.html?a(b)"));
davidsac6e6c35e42016-11-21 19:45:578126 EXPECT_TRUE(NavigateToURL(shell(), main_url));
lfg717154072016-06-30 15:04:168127
8128 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
8129 ->GetFrameTree()
8130 ->root();
8131
8132 EXPECT_EQ(
8133 " Site A ------------ proxies for B\n"
8134 " +--Site B ------- proxies for A\n"
8135 "Where A = http://a.com/\n"
8136 " B = http://b.com/",
8137 DepictFrameTree(root));
8138
Nick Carterb7e71312018-08-03 23:36:138139 EXPECT_TRUE(
8140 ExecJs(root->child_at(0),
8141 "var event_fired = 0;\n"
8142 "document.addEventListener('visibilitychange',\n"
8143 " function() { event_fired++; });\n"));
lfg717154072016-06-30 15:04:168144
8145 shell()->web_contents()->WasHidden();
8146
Nick Carterb7e71312018-08-03 23:36:138147 EXPECT_EQ(1, EvalJs(root->child_at(0), "event_fired"));
lfg717154072016-06-30 15:04:168148
8149 shell()->web_contents()->WasShown();
8150
Nick Carterb7e71312018-08-03 23:36:138151 EXPECT_EQ(2, EvalJs(root->child_at(0), "event_fired"));
lfg717154072016-06-30 15:04:168152}
8153
alexmos20b99f02016-08-10 02:20:218154// Test that the pending RenderFrameHost is canceled and destroyed when its
8155// process dies. Previously, reusing a top-level pending RFH which
8156// is not live was hitting a CHECK in CreateRenderView due to having neither a
8157// main frame routing ID nor a proxy routing ID. See https://crbug.com/627400
8158// for more details.
8159IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
8160 PendingRFHIsCanceledWhenItsProcessDies) {
8161 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
8162 EXPECT_TRUE(NavigateToURL(shell(), main_url));
8163 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
8164
8165 // Open a popup at b.com.
8166 GURL popup_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
8167 Shell* popup_shell = OpenPopup(root, popup_url, "foo");
8168 EXPECT_TRUE(popup_shell);
8169
8170 // The RenderViewHost for b.com in the main tab should not be active.
8171 SiteInstance* b_instance = popup_shell->web_contents()->GetSiteInstance();
8172 RenderViewHostImpl* rvh =
8173 web_contents()->GetFrameTree()->GetRenderViewHost(b_instance);
8174 EXPECT_FALSE(rvh->is_active());
8175
8176 // Navigate main tab to a b.com URL that will not commit.
8177 GURL stall_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
8178 TestNavigationManager delayer(shell()->web_contents(), stall_url);
danakj824a7ff2019-02-07 20:34:028179 EXPECT_TRUE(ExecuteScript(shell(), JsReplace("location = $1", stall_url)));
clamy19f0d492016-10-13 16:53:288180 EXPECT_TRUE(delayer.WaitForRequestStart());
alexmos20b99f02016-08-10 02:20:218181
8182 // The pending RFH should be in the same process as the popup.
8183 RenderFrameHostImpl* pending_rfh =
clamy610c63b32017-12-22 15:05:188184 root->render_manager()->speculative_frame_host();
alexmos20b99f02016-08-10 02:20:218185 RenderProcessHost* pending_process = pending_rfh->GetProcess();
8186 EXPECT_EQ(pending_process,
8187 popup_shell->web_contents()->GetMainFrame()->GetProcess());
8188
8189 // Kill the b.com process, currently in use by the pending RenderFrameHost
8190 // and the popup.
8191 RenderProcessHostWatcher crash_observer(
8192 pending_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:458193 EXPECT_TRUE(pending_process->Shutdown(0));
alexmos20b99f02016-08-10 02:20:218194 crash_observer.Wait();
8195
8196 // The pending RFH should have been canceled and destroyed, so that it won't
8197 // be reused while it's not live in the next navigation.
8198 {
8199 RenderFrameHostImpl* pending_rfh =
clamy610c63b32017-12-22 15:05:188200 root->render_manager()->speculative_frame_host();
alexmos20b99f02016-08-10 02:20:218201 EXPECT_FALSE(pending_rfh);
8202 }
8203
8204 // Navigate main tab to b.com again. This should not crash.
8205 GURL b_url(embedded_test_server()->GetURL("b.com", "/title3.html"));
Alex Moshchuk7e26eca2018-03-03 01:34:298206 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), b_url));
alexmos20b99f02016-08-10 02:20:218207
8208 // The b.com RVH in the main tab should become active.
8209 EXPECT_TRUE(rvh->is_active());
8210}
8211
8212// Test that killing a pending RenderFrameHost's process doesn't leave its
8213// RenderViewHost confused whether it's active or not for future navigations
8214// that try to reuse it. See https://crbug.com/627893 for more details.
8215// Similar to the test above for https://crbug.com/627400, except the popup is
8216// navigated after pending RFH's process is killed, rather than the main tab.
8217IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
8218 RenderViewHostKeepsSwappedOutStateIfPendingRFHDies) {
8219 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
8220 EXPECT_TRUE(NavigateToURL(shell(), main_url));
8221 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
8222
8223 // Open a popup at b.com.
8224 GURL popup_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
8225 Shell* popup_shell = OpenPopup(root, popup_url, "foo");
8226 EXPECT_TRUE(popup_shell);
8227
8228 // The RenderViewHost for b.com in the main tab should not be active.
8229 SiteInstance* b_instance = popup_shell->web_contents()->GetSiteInstance();
8230 RenderViewHostImpl* rvh =
8231 web_contents()->GetFrameTree()->GetRenderViewHost(b_instance);
8232 EXPECT_FALSE(rvh->is_active());
8233
8234 // Navigate main tab to a b.com URL that will not commit.
8235 GURL stall_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
jamcb4ae152017-05-19 01:35:518236 NavigationHandleObserver handle_observer(shell()->web_contents(), stall_url);
alexmos20b99f02016-08-10 02:20:218237 TestNavigationManager delayer(shell()->web_contents(), stall_url);
danakj824a7ff2019-02-07 20:34:028238 EXPECT_TRUE(ExecuteScript(shell(), JsReplace("location = $1", stall_url)));
clamy19f0d492016-10-13 16:53:288239 EXPECT_TRUE(delayer.WaitForRequestStart());
alexmos20b99f02016-08-10 02:20:218240
8241 // Kill the b.com process, currently in use by the pending RenderFrameHost
8242 // and the popup.
8243 RenderProcessHost* pending_process =
8244 popup_shell->web_contents()->GetMainFrame()->GetProcess();
8245 RenderProcessHostWatcher crash_observer(
8246 pending_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:458247 EXPECT_TRUE(pending_process->Shutdown(0));
alexmos20b99f02016-08-10 02:20:218248 crash_observer.Wait();
8249
8250 // Since the navigation above didn't commit, the b.com RenderViewHost in the
8251 // main tab should still not be active.
8252 EXPECT_FALSE(rvh->is_active());
jamcb4ae152017-05-19 01:35:518253 EXPECT_EQ(net::ERR_ABORTED, handle_observer.net_error_code());
alexmos20b99f02016-08-10 02:20:218254
8255 // Navigate popup to b.com to recreate the b.com process. When creating
8256 // opener proxies, |rvh| should be reused as a swapped out RVH. In
8257 // https://crbug.com/627893, recreating the opener RenderView was hitting a
8258 // CHECK(params.swapped_out) in the renderer process, since its
8259 // RenderViewHost was brought into an active state by the navigation to
8260 // |stall_url| above, even though it never committed.
8261 GURL b_url(embedded_test_server()->GetURL("b.com", "/title3.html"));
Alex Moshchuk7e26eca2018-03-03 01:34:298262 EXPECT_TRUE(NavigateToURLInSameBrowsingInstance(popup_shell, b_url));
alexmos20b99f02016-08-10 02:20:218263 EXPECT_FALSE(rvh->is_active());
8264}
8265
alexmosc15fab92016-08-12 00:30:418266// Test that a crashed subframe can be successfully navigated to the site it
8267// was on before crashing. See https://crbug.com/634368.
8268IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
8269 NavigateCrashedSubframeToSameSite) {
8270 GURL main_url(embedded_test_server()->GetURL(
8271 "a.com", "/cross_site_iframe_factory.html?a(b)"));
8272 EXPECT_TRUE(NavigateToURL(shell(), main_url));
8273 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
8274 FrameTreeNode* child = root->child_at(0);
8275
8276 // Set up a postMessage handler in the main frame for later use.
8277 EXPECT_TRUE(ExecuteScript(
8278 root->current_frame_host(),
8279 "window.addEventListener('message',"
8280 " function(e) { document.title = e.data; });"));
8281
8282 // Crash the subframe process.
8283 RenderProcessHost* child_process = child->current_frame_host()->GetProcess();
8284 RenderProcessHostWatcher crash_observer(
8285 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:458286 child_process->Shutdown(0);
alexmosc15fab92016-08-12 00:30:418287 crash_observer.Wait();
8288 EXPECT_FALSE(child->current_frame_host()->IsRenderFrameLive());
8289
8290 // When the subframe dies, its RenderWidgetHostView should be cleared and
8291 // reset in the CrossProcessFrameConnector.
8292 EXPECT_FALSE(child->current_frame_host()->GetView());
8293 RenderFrameProxyHost* proxy_to_parent =
8294 child->render_manager()->GetProxyToParent();
8295 EXPECT_FALSE(
8296 proxy_to_parent->cross_process_frame_connector()->get_view_for_testing());
8297
8298 // Navigate the subframe to the same site it was on before crashing. This
8299 // should reuse the subframe's current RenderFrameHost and reinitialize the
8300 // RenderFrame in a new process.
8301 NavigateFrameToURL(child,
8302 embedded_test_server()->GetURL("b.com", "/title1.html"));
8303 EXPECT_TRUE(child->current_frame_host()->IsRenderFrameLive());
8304
8305 // The RenderWidgetHostView for the child should be recreated and set to be
8306 // used in the CrossProcessFrameConnector. Without this, the frame won't be
8307 // rendered properly.
8308 EXPECT_TRUE(child->current_frame_host()->GetView());
8309 EXPECT_EQ(
8310 child->current_frame_host()->GetView(),
8311 proxy_to_parent->cross_process_frame_connector()->get_view_for_testing());
8312
jonross068adbc2018-05-30 18:04:478313 // Make sure that the child frame has submitted a compositor frame
8314 RenderFrameSubmissionObserver frame_observer(child);
8315 frame_observer.WaitForMetadataChange();
lfgb592bfa2017-05-08 20:47:008316
alexmosc15fab92016-08-12 00:30:418317 // Send a postMessage from the child to its parent. This verifies that the
8318 // parent's proxy in the child's SiteInstance was also restored.
8319 base::string16 expected_title(base::UTF8ToUTF16("I am alive!"));
8320 TitleWatcher title_watcher(shell()->web_contents(), expected_title);
8321 EXPECT_TRUE(ExecuteScript(child->current_frame_host(),
8322 "parent.postMessage('I am alive!', '*');"));
8323 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
8324}
8325
alexmos136fd6e62016-08-15 20:58:418326// Test that session history length and offset are replicated to all renderer
8327// processes in a FrameTree. This allows each renderer to see correct values
8328// for history.length, and to check the offset validity properly for
8329// navigations initiated via history.go(). See https:/crbug.com/501116.
8330IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, SessionHistoryReplication) {
8331 GURL main_url(embedded_test_server()->GetURL(
8332 "a.com", "/cross_site_iframe_factory.html?a(a,a)"));
8333 EXPECT_TRUE(NavigateToURL(shell(), main_url));
8334 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
8335 FrameTreeNode* child1 = root->child_at(0);
8336 FrameTreeNode* child2 = root->child_at(1);
8337 GURL child_first_url(child1->current_url());
8338 EXPECT_EQ(child1->current_url(), child2->current_url());
8339
8340 // Helper to retrieve the history length from a given frame.
8341 auto history_length = [](FrameTreeNode* ftn) {
Nick Carterb7e71312018-08-03 23:36:138342 return EvalJs(ftn->current_frame_host(), "history.length;");
alexmos136fd6e62016-08-15 20:58:418343 };
8344
8345 // All frames should see a history length of 1 to start with.
8346 EXPECT_EQ(1, history_length(root));
8347 EXPECT_EQ(1, history_length(child1));
8348 EXPECT_EQ(1, history_length(child2));
8349
8350 // Navigate first child cross-site. This increases history length to 2.
8351 NavigateFrameToURL(child1,
8352 embedded_test_server()->GetURL("b.com", "/title1.html"));
8353 EXPECT_EQ(2, history_length(root));
8354 EXPECT_EQ(2, history_length(child1));
8355 EXPECT_EQ(2, history_length(child2));
8356
8357 // Navigate second child same-site.
8358 GURL child2_last_url(embedded_test_server()->GetURL("a.com", "/title2.html"));
8359 NavigateFrameToURL(child2, child2_last_url);
8360 EXPECT_EQ(3, history_length(root));
8361 EXPECT_EQ(3, history_length(child1));
8362 EXPECT_EQ(3, history_length(child2));
8363
8364 // Navigate first child same-site to another b.com URL.
8365 GURL child1_last_url(embedded_test_server()->GetURL("b.com", "/title3.html"));
8366 NavigateFrameToURL(child1, child1_last_url);
8367 EXPECT_EQ(4, history_length(root));
8368 EXPECT_EQ(4, history_length(child1));
8369 EXPECT_EQ(4, history_length(child2));
8370
8371 // Go back three entries using the history API from the main frame. This
8372 // checks that both history length and offset are not stale in a.com, as
8373 // otherwise this navigation might be dropped by Blink.
8374 EXPECT_TRUE(ExecuteScript(root, "history.go(-3);"));
8375 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
8376 EXPECT_EQ(main_url, root->current_url());
8377 EXPECT_EQ(child_first_url, child1->current_url());
8378 EXPECT_EQ(child_first_url, child2->current_url());
8379
8380 // Now go forward three entries from the child1 frame and check that the
8381 // history length and offset are not stale in b.com.
8382 EXPECT_TRUE(ExecuteScript(child1, "history.go(3);"));
8383 EXPECT_TRUE(WaitForLoadStop(shell()->web_contents()));
8384 EXPECT_EQ(main_url, root->current_url());
8385 EXPECT_EQ(child1_last_url, child1->current_url());
8386 EXPECT_EQ(child2_last_url, child2->current_url());
8387}
8388
alexmos7e03e5a92016-08-30 19:18:138389// A BrowserMessageFilter that drops FrameHostMsg_OnDispatchLoad messages.
8390class DispatchLoadMessageFilter : public BrowserMessageFilter {
8391 public:
8392 DispatchLoadMessageFilter() : BrowserMessageFilter(FrameMsgStart) {}
8393
8394 protected:
8395 ~DispatchLoadMessageFilter() override {}
8396
8397 private:
8398 // BrowserMessageFilter:
8399 bool OnMessageReceived(const IPC::Message& message) override {
8400 return message.type() == FrameHostMsg_DispatchLoad::ID;
8401 }
8402
8403 DISALLOW_COPY_AND_ASSIGN(DispatchLoadMessageFilter);
8404};
8405
8406// Test that the renderer isn't killed when a frame generates a load event just
8407// after becoming pending deletion. See https://crbug.com/636513.
8408IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
8409 LoadEventForwardingWhilePendingDeletion) {
8410 GURL main_url(embedded_test_server()->GetURL(
8411 "a.com", "/cross_site_iframe_factory.html?a(a)"));
8412 EXPECT_TRUE(NavigateToURL(shell(), main_url));
8413 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
8414 FrameTreeNode* child = root->child_at(0);
8415
8416 // Open a popup in the b.com process for later use.
8417 GURL popup_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
8418 Shell* popup_shell = OpenPopup(root, popup_url, "foo");
8419 EXPECT_TRUE(popup_shell);
8420
8421 // Install a filter to drop DispatchLoad messages from b.com.
8422 scoped_refptr<DispatchLoadMessageFilter> filter =
8423 new DispatchLoadMessageFilter();
8424 RenderProcessHost* b_process =
8425 popup_shell->web_contents()->GetMainFrame()->GetProcess();
8426 b_process->AddFilter(filter.get());
8427
8428 // Navigate subframe to b.com. Wait for commit but not full load.
8429 GURL b_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
8430 {
8431 TestFrameNavigationObserver commit_observer(child);
danakj824a7ff2019-02-07 20:34:028432 EXPECT_TRUE(ExecuteScript(child, JsReplace("location.href = $1", b_url)));
alexmos7e03e5a92016-08-30 19:18:138433 commit_observer.WaitForCommit();
8434 }
8435 RenderFrameHostImpl* child_rfh = child->current_frame_host();
8436 child_rfh->DisableSwapOutTimerForTesting();
8437
8438 // At this point, the subframe should have a proxy in its parent's
8439 // SiteInstance, a.com.
8440 EXPECT_TRUE(child->render_manager()->GetProxyToParent());
8441
8442 // Now, go back to a.com in the subframe and wait for commit.
8443 {
8444 TestFrameNavigationObserver commit_observer(child);
8445 web_contents()->GetController().GoBack();
8446 commit_observer.WaitForCommit();
8447 }
8448
8449 // At this point, the subframe's old RFH for b.com should be pending
8450 // deletion, and the subframe's proxy in a.com should've been cleared.
8451 EXPECT_FALSE(child_rfh->is_active());
8452 EXPECT_FALSE(child->render_manager()->GetProxyToParent());
8453
8454 // Simulate that the load event is dispatched from |child_rfh| just after
8455 // it's become pending deletion.
8456 child_rfh->OnDispatchLoad();
8457
8458 // In the bug, OnDispatchLoad killed the b.com renderer. Ensure that this is
8459 // not the case. Note that the process kill doesn't happen immediately, so
8460 // IsRenderFrameLive() can't be checked here (yet). Instead, check that
8461 // JavaScript can still execute in b.com using the popup.
8462 EXPECT_TRUE(ExecuteScript(popup_shell->web_contents(), "true"));
8463}
8464
clamyd69748c2016-10-07 22:09:448465IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
8466 RFHTransfersWhilePendingDeletion) {
8467 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
davidsac6e6c35e42016-11-21 19:45:578468 EXPECT_TRUE(NavigateToURL(shell(), main_url));
clamyd69748c2016-10-07 22:09:448469
8470 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
8471 ->GetFrameTree()
8472 ->root();
8473
8474 // Start a cross-process navigation and wait until the response is received.
8475 GURL cross_site_url_1 =
8476 embedded_test_server()->GetURL("b.com", "/title1.html");
8477 TestNavigationManager cross_site_manager(shell()->web_contents(),
8478 cross_site_url_1);
8479 shell()->web_contents()->GetController().LoadURL(
8480 cross_site_url_1, Referrer(), ui::PAGE_TRANSITION_LINK, std::string());
clamy19f0d492016-10-13 16:53:288481 EXPECT_TRUE(cross_site_manager.WaitForResponse());
clamyd69748c2016-10-07 22:09:448482
8483 // Start a renderer-initiated navigation to a cross-process url and make sure
8484 // the navigation will be blocked before being transferred.
8485 GURL cross_site_url_2 =
8486 embedded_test_server()->GetURL("c.com", "/title1.html");
8487 TestNavigationManager transfer_manager(shell()->web_contents(),
8488 cross_site_url_2);
danakj824a7ff2019-02-07 20:34:028489 EXPECT_TRUE(
8490 ExecuteScript(root, JsReplace("location.href = $1", cross_site_url_2)));
clamy19f0d492016-10-13 16:53:288491 EXPECT_TRUE(transfer_manager.WaitForResponse());
clamyd69748c2016-10-07 22:09:448492
8493 // Now have the cross-process navigation commit and mark the current RFH as
8494 // pending deletion.
8495 cross_site_manager.WaitForNavigationFinished();
8496
8497 // Resume the navigation in the previous RFH that has just been marked as
8498 // pending deletion. We should not crash.
8499 transfer_manager.WaitForNavigationFinished();
8500}
8501
jam419c0f12016-10-13 02:09:168502class NavigationHandleWatcher : public WebContentsObserver {
8503 public:
Lukasz Anforowicz9e0ce4e2017-09-28 19:09:158504 explicit NavigationHandleWatcher(WebContents* web_contents)
jam419c0f12016-10-13 02:09:168505 : WebContentsObserver(web_contents) {}
8506 void DidStartNavigation(NavigationHandle* navigation_handle) override {
8507 DCHECK_EQ(GURL("http://b.com/"),
8508 navigation_handle->GetStartingSiteInstance()->GetSiteURL());
8509 }
8510};
8511
8512// Verifies that the SiteInstance of a NavigationHandle correctly identifies the
8513// RenderFrameHost that started the navigation (and not the destination RFH).
8514IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
8515 NavigationHandleSiteInstance) {
8516 // Navigate to a page with a cross-site iframe.
8517 GURL main_url(embedded_test_server()->GetURL(
8518 "a.com", "/cross_site_iframe_factory.html?a(b)"));
8519 EXPECT_TRUE(NavigateToURL(shell(), main_url));
8520
8521 // Navigate the iframe cross-site.
8522 NavigationHandleWatcher watcher(shell()->web_contents());
8523 TestNavigationObserver load_observer(shell()->web_contents());
8524 GURL frame_url = embedded_test_server()->GetURL("c.com", "/title1.html");
danakj824a7ff2019-02-07 20:34:028525 EXPECT_TRUE(
8526 ExecuteScript(shell()->web_contents(),
8527 JsReplace("window.frames[0].location = $1", frame_url)));
jam419c0f12016-10-13 02:09:168528 load_observer.Wait();
8529}
8530
alexmos78c9c0d2016-10-14 18:57:038531// Test that when canceling a pending RenderFrameHost in the middle of a
8532// redirect, and then killing the corresponding RenderView's renderer process,
8533// the RenderViewHost isn't reused in an improper state later. Previously this
8534// led to a crash in CreateRenderView when recreating the RenderView due to a
8535// stale main frame routing ID. See https://crbug.com/627400.
8536IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
8537 ReuseNonLiveRenderViewHostAfterCancelPending) {
8538 GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
8539 GURL b_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
8540 GURL c_url(embedded_test_server()->GetURL("c.com", "/title3.html"));
8541
8542 EXPECT_TRUE(NavigateToURL(shell(), a_url));
8543
8544 // Open a popup and navigate it to b.com.
8545 Shell* popup = OpenPopup(shell(), a_url, "popup");
Alex Moshchuk7e26eca2018-03-03 01:34:298546 EXPECT_TRUE(NavigateToURLFromRenderer(popup, b_url));
alexmos78c9c0d2016-10-14 18:57:038547
8548 // Open a second popup and navigate it to b.com, which redirects to c.com.
8549 // The navigation to b.com will create a pending RenderFrameHost, which will
Alex Moshchuk7e26eca2018-03-03 01:34:298550 // be canceled during the redirect to c.com. Note that
8551 // NavigateToURLFromRenderer will return false because the committed URL
8552 // won't match the requested URL due to the redirect.
alexmos78c9c0d2016-10-14 18:57:038553 Shell* popup2 = OpenPopup(shell(), a_url, "popup2");
8554 TestNavigationObserver observer(popup2->web_contents());
8555 GURL redirect_url(embedded_test_server()->GetURL(
8556 "b.com", "/server-redirect?" + c_url.spec()));
Alex Moshchuk7e26eca2018-03-03 01:34:298557 EXPECT_FALSE(NavigateToURLFromRenderer(popup2, redirect_url));
alexmos78c9c0d2016-10-14 18:57:038558 EXPECT_EQ(c_url, observer.last_navigation_url());
8559 EXPECT_TRUE(observer.last_navigation_succeeded());
8560
8561 // Kill the b.com process (which currently hosts a RenderFrameProxy that
8562 // replaced the pending RenderFrame in |popup2|, as well as the RenderFrame
8563 // for |popup|).
8564 RenderProcessHost* b_process =
8565 popup->web_contents()->GetMainFrame()->GetProcess();
8566 RenderProcessHostWatcher crash_observer(
8567 b_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:458568 b_process->Shutdown(0);
alexmos78c9c0d2016-10-14 18:57:038569 crash_observer.Wait();
8570
8571 // Navigate the second popup to b.com. This used to crash when creating the
8572 // RenderView, because it reused the RenderViewHost created by the canceled
8573 // navigation to b.com, and that RenderViewHost had a stale main frame
8574 // routing ID and active state.
Alex Moshchuk7e26eca2018-03-03 01:34:298575 EXPECT_TRUE(NavigateToURLInSameBrowsingInstance(popup2, b_url));
alexmos78c9c0d2016-10-14 18:57:038576}
8577
8578// Check that after a pending RFH is canceled and replaced with a proxy (which
8579// reuses the canceled RFH's RenderViewHost), navigating to a main frame in the
8580// same site as the canceled RFH doesn't lead to a renderer crash. The steps
8581// here are similar to ReuseNonLiveRenderViewHostAfterCancelPending, but don't
8582// involve crashing the renderer. See https://crbug.com/651980.
8583IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
8584 RecreateMainFrameAfterCancelPending) {
8585 GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
8586 GURL b_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
8587 GURL c_url(embedded_test_server()->GetURL("c.com", "/title3.html"));
8588
8589 EXPECT_TRUE(NavigateToURL(shell(), a_url));
8590
8591 // Open a popup and navigate it to b.com.
8592 Shell* popup = OpenPopup(shell(), a_url, "popup");
Alex Moshchuk7e26eca2018-03-03 01:34:298593 EXPECT_TRUE(NavigateToURLFromRenderer(popup, b_url));
alexmos78c9c0d2016-10-14 18:57:038594
8595 // Open a second popup and navigate it to b.com, which redirects to c.com.
8596 // The navigation to b.com will create a pending RenderFrameHost, which will
8597 // be canceled during the redirect to c.com. Note that NavigateToURL will
8598 // return false because the committed URL won't match the requested URL due
8599 // to the redirect.
8600 Shell* popup2 = OpenPopup(shell(), a_url, "popup2");
8601 TestNavigationObserver observer(popup2->web_contents());
8602 GURL redirect_url(embedded_test_server()->GetURL(
8603 "b.com", "/server-redirect?" + c_url.spec()));
Alex Moshchuk7e26eca2018-03-03 01:34:298604 EXPECT_FALSE(NavigateToURLFromRenderer(popup2, redirect_url));
alexmos78c9c0d2016-10-14 18:57:038605 EXPECT_EQ(c_url, observer.last_navigation_url());
8606 EXPECT_TRUE(observer.last_navigation_succeeded());
8607
8608 // Navigate the second popup to b.com. This used to crash the b.com renderer
8609 // because it failed to delete the canceled RFH's RenderFrame, so this caused
8610 // it to try to create a frame widget which already existed.
Alex Moshchuk7e26eca2018-03-03 01:34:298611 EXPECT_TRUE(NavigateToURLFromRenderer(popup2, b_url));
alexmos78c9c0d2016-10-14 18:57:038612}
8613
8614// Check that when a pending RFH is canceled and a proxy needs to be created in
8615// its place, the proxy is properly initialized on the renderer side. See
8616// https://crbug.com/653746.
8617IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
8618 CommunicateWithProxyAfterCancelPending) {
8619 GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
8620 GURL b_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
8621 GURL c_url(embedded_test_server()->GetURL("c.com", "/title3.html"));
8622
8623 EXPECT_TRUE(NavigateToURL(shell(), a_url));
8624
8625 // Open a popup and navigate it to b.com.
8626 Shell* popup = OpenPopup(shell(), a_url, "popup");
Alex Moshchuk7e26eca2018-03-03 01:34:298627 EXPECT_TRUE(NavigateToURLFromRenderer(popup, b_url));
alexmos78c9c0d2016-10-14 18:57:038628
8629 // Open a second popup and navigate it to b.com, which redirects to c.com.
8630 // The navigation to b.com will create a pending RenderFrameHost, which will
8631 // be canceled during the redirect to c.com. Note that NavigateToURL will
8632 // return false because the committed URL won't match the requested URL due
8633 // to the redirect.
8634 Shell* popup2 = OpenPopup(shell(), a_url, "popup2");
8635 TestNavigationObserver observer(popup2->web_contents());
8636 GURL redirect_url(embedded_test_server()->GetURL(
8637 "b.com", "/server-redirect?" + c_url.spec()));
Alex Moshchuk7e26eca2018-03-03 01:34:298638 EXPECT_FALSE(NavigateToURLFromRenderer(popup2, redirect_url));
alexmos78c9c0d2016-10-14 18:57:038639 EXPECT_EQ(c_url, observer.last_navigation_url());
8640 EXPECT_TRUE(observer.last_navigation_succeeded());
8641
8642 // Because b.com has other active frames (namely, the frame in |popup|),
8643 // there should be a proxy created for the canceled RFH, and it should be
8644 // live.
8645 SiteInstance* b_instance = popup->web_contents()->GetSiteInstance();
8646 FrameTreeNode* popup2_root =
8647 static_cast<WebContentsImpl*>(popup2->web_contents())
8648 ->GetFrameTree()
8649 ->root();
8650 RenderFrameProxyHost* proxy =
8651 popup2_root->render_manager()->GetRenderFrameProxyHost(b_instance);
8652 EXPECT_TRUE(proxy);
8653 EXPECT_TRUE(proxy->is_render_frame_proxy_live());
8654
8655 // Add a postMessage listener in |popup2| (currently at a c.com URL).
8656 EXPECT_TRUE(
8657 ExecuteScript(popup2,
8658 "window.addEventListener('message', function(event) {\n"
8659 " document.title=event.data;\n"
8660 "});"));
8661
8662 // Check that a postMessage can be sent via |proxy| above. This needs to be
8663 // done from the b.com process. |popup| is currently in b.com, but it can't
8664 // reach the window reference for |popup2| due to a security restriction in
8665 // Blink. So, navigate the main tab to b.com and then send a postMessage to
8666 // |popup2|. This is allowed since the main tab is |popup2|'s opener.
Alex Moshchuk7e26eca2018-03-03 01:34:298667 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), b_url));
alexmos78c9c0d2016-10-14 18:57:038668
8669 base::string16 expected_title(base::UTF8ToUTF16("foo"));
8670 TitleWatcher title_watcher(popup2->web_contents(), expected_title);
8671 EXPECT_TRUE(ExecuteScript(
8672 shell(), "window.open('','popup2').postMessage('foo', '*');"));
8673 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
8674}
8675
Luna Lu58c77b02019-03-05 20:13:468676IN_PROC_BROWSER_TEST_F(SitePerProcessFeaturePolicyBrowserTest,
8677 TestPolicyReplicationOnSameOriginNavigation) {
iclellandab749ec92016-11-23 02:00:438678 GURL start_url(
8679 embedded_test_server()->GetURL("a.com", "/feature-policy1.html"));
8680 GURL first_nav_url(
8681 embedded_test_server()->GetURL("a.com", "/feature-policy2.html"));
8682 GURL second_nav_url(embedded_test_server()->GetURL("a.com", "/title2.html"));
8683
8684 EXPECT_TRUE(NavigateToURL(shell(), start_url));
8685
8686 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
Ian Clelland6684fb772018-02-15 16:46:138687 EXPECT_EQ(CreateFPHeader(blink::mojom::FeaturePolicyFeature::kGeolocation,
Luna Lu58c77b02019-03-05 20:13:468688 blink::mojom::FeaturePolicyFeature::kOversizedImages,
lunalu5fb4be22017-03-17 20:57:178689 {start_url.GetOrigin()}),
iclellandab749ec92016-11-23 02:00:438690 root->current_replication_state().feature_policy_header);
8691
8692 // When the main frame navigates to a page with a new policy, it should
8693 // overwrite the old one.
8694 EXPECT_TRUE(NavigateToURL(shell(), first_nav_url));
Ian Clelland6684fb772018-02-15 16:46:138695 EXPECT_EQ(CreateFPHeaderMatchesAll(
Luna Lu58c77b02019-03-05 20:13:468696 blink::mojom::FeaturePolicyFeature::kGeolocation,
8697 blink::mojom::FeaturePolicyFeature::kOversizedImages),
Ian Clelland6684fb772018-02-15 16:46:138698 root->current_replication_state().feature_policy_header);
iclellandab749ec92016-11-23 02:00:438699
8700 // When the main frame navigates to a page without a policy, the replicated
8701 // policy header should be cleared.
8702 EXPECT_TRUE(NavigateToURL(shell(), second_nav_url));
raymesd405a052016-12-05 23:41:348703 EXPECT_TRUE(root->current_replication_state().feature_policy_header.empty());
iclellandab749ec92016-11-23 02:00:438704}
8705
Luna Lu58c77b02019-03-05 20:13:468706IN_PROC_BROWSER_TEST_F(SitePerProcessFeaturePolicyBrowserTest,
8707 TestPolicyReplicationOnCrossOriginNavigation) {
iclellandab749ec92016-11-23 02:00:438708 GURL start_url(
8709 embedded_test_server()->GetURL("a.com", "/feature-policy1.html"));
8710 GURL first_nav_url(
8711 embedded_test_server()->GetURL("b.com", "/feature-policy2.html"));
8712 GURL second_nav_url(embedded_test_server()->GetURL("c.com", "/title2.html"));
8713
8714 EXPECT_TRUE(NavigateToURL(shell(), start_url));
8715
8716 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
Ian Clelland6684fb772018-02-15 16:46:138717 EXPECT_EQ(CreateFPHeader(blink::mojom::FeaturePolicyFeature::kGeolocation,
Luna Lu58c77b02019-03-05 20:13:468718 blink::mojom::FeaturePolicyFeature::kOversizedImages,
lunalu5fb4be22017-03-17 20:57:178719 {start_url.GetOrigin()}),
iclellandab749ec92016-11-23 02:00:438720 root->current_replication_state().feature_policy_header);
8721
8722 // When the main frame navigates to a page with a new policy, it should
8723 // overwrite the old one.
8724 EXPECT_TRUE(NavigateToURL(shell(), first_nav_url));
Ian Clelland6684fb772018-02-15 16:46:138725 EXPECT_EQ(CreateFPHeaderMatchesAll(
Luna Lu58c77b02019-03-05 20:13:468726 blink::mojom::FeaturePolicyFeature::kGeolocation,
8727 blink::mojom::FeaturePolicyFeature::kOversizedImages),
Ian Clelland6684fb772018-02-15 16:46:138728 root->current_replication_state().feature_policy_header);
iclellandab749ec92016-11-23 02:00:438729
8730 // When the main frame navigates to a page without a policy, the replicated
8731 // policy header should be cleared.
8732 EXPECT_TRUE(NavigateToURL(shell(), second_nav_url));
raymesd405a052016-12-05 23:41:348733 EXPECT_TRUE(root->current_replication_state().feature_policy_header.empty());
iclellandab749ec92016-11-23 02:00:438734}
8735
8736// Test that the replicated feature policy header is correct in subframes as
8737// they navigate.
Luna Lu58c77b02019-03-05 20:13:468738IN_PROC_BROWSER_TEST_F(SitePerProcessFeaturePolicyBrowserTest,
8739 TestPolicyReplicationFromRemoteFrames) {
iclellandab749ec92016-11-23 02:00:438740 GURL main_url(
8741 embedded_test_server()->GetURL("a.com", "/feature-policy-main.html"));
8742 GURL first_nav_url(
8743 embedded_test_server()->GetURL("b.com", "/feature-policy2.html"));
8744 GURL second_nav_url(embedded_test_server()->GetURL("c.com", "/title2.html"));
8745
8746 EXPECT_TRUE(NavigateToURL(shell(), main_url));
8747
8748 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
Ian Clelland6684fb772018-02-15 16:46:138749 EXPECT_EQ(CreateFPHeader(blink::mojom::FeaturePolicyFeature::kGeolocation,
Luna Lu58c77b02019-03-05 20:13:468750 blink::mojom::FeaturePolicyFeature::kOversizedImages,
raymesd405a052016-12-05 23:41:348751 {main_url.GetOrigin(), GURL("http://example.com/")}),
iclellandab749ec92016-11-23 02:00:438752 root->current_replication_state().feature_policy_header);
8753 EXPECT_EQ(1UL, root->child_count());
8754 EXPECT_EQ(
Ian Clelland6684fb772018-02-15 16:46:138755 CreateFPHeader(blink::mojom::FeaturePolicyFeature::kGeolocation,
Luna Lu58c77b02019-03-05 20:13:468756 blink::mojom::FeaturePolicyFeature::kOversizedImages,
lunalu5fb4be22017-03-17 20:57:178757 {main_url.GetOrigin()}),
iclellandab749ec92016-11-23 02:00:438758 root->child_at(0)->current_replication_state().feature_policy_header);
8759
8760 // Navigate the iframe cross-site.
8761 NavigateFrameToURL(root->child_at(0), first_nav_url);
8762 EXPECT_EQ(
Ian Clelland6684fb772018-02-15 16:46:138763 CreateFPHeaderMatchesAll(
Luna Lu58c77b02019-03-05 20:13:468764 blink::mojom::FeaturePolicyFeature::kGeolocation,
8765 blink::mojom::FeaturePolicyFeature::kOversizedImages),
iclellandab749ec92016-11-23 02:00:438766 root->child_at(0)->current_replication_state().feature_policy_header);
8767
8768 // Navigate the iframe to another location, this one with no policy header
8769 NavigateFrameToURL(root->child_at(0), second_nav_url);
raymesd405a052016-12-05 23:41:348770 EXPECT_TRUE(root->child_at(0)
8771 ->current_replication_state()
8772 .feature_policy_header.empty());
iclellandab749ec92016-11-23 02:00:438773
8774 // Navigate the iframe back to a page with a policy
8775 NavigateFrameToURL(root->child_at(0), first_nav_url);
8776 EXPECT_EQ(
Ian Clelland6684fb772018-02-15 16:46:138777 CreateFPHeaderMatchesAll(
Luna Lu58c77b02019-03-05 20:13:468778 blink::mojom::FeaturePolicyFeature::kGeolocation,
8779 blink::mojom::FeaturePolicyFeature::kOversizedImages),
iclellandab749ec92016-11-23 02:00:438780 root->child_at(0)->current_replication_state().feature_policy_header);
8781}
arthursonzogni85ef7712016-11-23 10:05:228782
Ian Clellandedb8c5dd2018-03-01 17:01:378783// Test that the replicated feature policy header is correct in remote proxies
8784// after the local frame has navigated.
8785IN_PROC_BROWSER_TEST_F(SitePerProcessFeaturePolicyJavaScriptBrowserTest,
8786 TestFeaturePolicyReplicationToProxyOnNavigation) {
8787 GURL main_url(embedded_test_server()->GetURL(
8788 "a.com", "/frame_tree/page_with_two_frames.html"));
8789 GURL first_nav_url(
8790 embedded_test_server()->GetURL("a.com", "/feature-policy3.html"));
8791 GURL second_nav_url(
8792 embedded_test_server()->GetURL("a.com", "/feature-policy4.html"));
8793
8794 EXPECT_TRUE(NavigateToURL(shell(), main_url));
8795
8796 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
8797 EXPECT_TRUE(root->current_replication_state().feature_policy_header.empty());
8798 EXPECT_EQ(2UL, root->child_count());
8799 EXPECT_TRUE(root->child_at(1)
8800 ->current_replication_state()
8801 .feature_policy_header.empty());
8802
8803 // Navigate the iframe to a page with a policy, and a nested cross-site iframe
8804 // (to the same site as a root->child_at(1) so that the render process already
8805 // exists.)
8806 NavigateFrameToURL(root->child_at(1), first_nav_url);
8807 EXPECT_EQ(
8808 CreateFPHeaderMatchesAll(
Luna Lu58c77b02019-03-05 20:13:468809 blink::mojom::FeaturePolicyFeature::kGeolocation,
8810 blink::mojom::FeaturePolicyFeature::kOversizedImages),
Ian Clellandedb8c5dd2018-03-01 17:01:378811 root->child_at(1)->current_replication_state().feature_policy_header);
8812
8813 EXPECT_EQ(1UL, root->child_at(1)->child_count());
8814
8815 // Ask the deepest iframe to report the enabled state of the geolocation
8816 // feature. If its parent frame's policy was replicated correctly to the
8817 // proxy, then this will be enabled. Otherwise, it will be disabled, as
8818 // geolocation is disabled by default in cross-origin frames.
Ian Clelland905cde302019-01-04 20:33:298819 EXPECT_EQ(true,
8820 EvalJs(root->child_at(1)->child_at(0),
8821 "document.featurePolicy.allowsFeature('geolocation')"));
Luna Lu58c77b02019-03-05 20:13:468822 // TODO(loonybear): Add JS test for parameterized features.
Ian Clellandedb8c5dd2018-03-01 17:01:378823
8824 // Now navigate the iframe to a page with no policy, and the same nested
8825 // cross-site iframe. The policy should be cleared in the proxy.
8826 NavigateFrameToURL(root->child_at(1), second_nav_url);
8827 EXPECT_TRUE(root->child_at(1)
8828 ->current_replication_state()
8829 .feature_policy_header.empty());
8830 EXPECT_EQ(1UL, root->child_at(1)->child_count());
8831
8832 // Ask the deepest iframe to report the enabled state of the geolocation
8833 // feature. If its parent frame's policy was replicated correctly to the
8834 // proxy, then this will now be disabled.
Ian Clelland905cde302019-01-04 20:33:298835 EXPECT_EQ(false,
8836 EvalJs(root->child_at(1)->child_at(0),
8837 "document.featurePolicy.allowsFeature('geolocation')"));
Ian Clellandedb8c5dd2018-03-01 17:01:378838}
8839
Ian Clelland2c0351ed2018-04-13 01:44:388840// Test that the constructed feature policy is correct in sandboxed
8841// frames. Sandboxed frames have an opaque origin, and if the frame policy,
8842// which is constructed in the parent frame, cannot send that origin through
8843// the browser process to the sandboxed frame, then the sandboxed frame's
8844// policy will be incorrect.
8845//
8846// This is a regression test for https://crbug.com/690520
8847IN_PROC_BROWSER_TEST_F(SitePerProcessFeaturePolicyJavaScriptBrowserTest,
8848 TestAllowAttributeInSandboxedFrame) {
8849 GURL main_url(embedded_test_server()->GetURL(
8850 "a.com",
8851 "/cross_site_iframe_factory.html?"
8852 "a(b{allow-geolocation,sandbox-allow-scripts})"));
8853 GURL nav_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
8854
8855 EXPECT_TRUE(NavigateToURL(shell(), main_url));
8856
8857 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
8858 EXPECT_TRUE(root->current_replication_state().feature_policy_header.empty());
8859 EXPECT_EQ(1UL, root->child_count());
8860 // Verify that the child frame is sandboxed with an opaque origin.
8861 EXPECT_TRUE(root->child_at(0)
8862 ->current_frame_host()
8863 ->GetLastCommittedOrigin()
Chris Palmerab5e5b52018-09-28 19:19:308864 .opaque());
Ian Clelland2c0351ed2018-04-13 01:44:388865 // And verify that the origin in the replication state is also opaque.
Chris Palmerab5e5b52018-09-28 19:19:308866 EXPECT_TRUE(root->child_at(0)->current_origin().opaque());
Ian Clelland2c0351ed2018-04-13 01:44:388867
8868 // Ask the sandboxed iframe to report the enabled state of the geolocation
8869 // feature. If the declared policy was correctly flagged as referring to the
8870 // opaque origin, then the policy in the sandboxed renderer will be
8871 // constructed correctly, and geolocation will be enabled in the sandbox.
8872 // Otherwise, it will be disabled, as geolocation is disabled by default in
8873 // cross-origin frames.
8874 bool success = false;
8875 EXPECT_TRUE(ExecuteScriptAndExtractBool(
8876 root->child_at(0),
8877 "window.domAutomationController.send("
Ian Clelland905cde302019-01-04 20:33:298878 "document.featurePolicy.allowsFeature('geolocation'));",
Ian Clelland2c0351ed2018-04-13 01:44:388879 &success));
8880 EXPECT_TRUE(success);
8881
8882 TestNavigationObserver load_observer(shell()->web_contents());
danakj824a7ff2019-02-07 20:34:028883 EXPECT_TRUE(ExecuteScript(root->child_at(0),
8884 JsReplace("document.location.href=$1", nav_url)));
Ian Clelland2c0351ed2018-04-13 01:44:388885 load_observer.Wait();
8886
8887 // Verify that the child frame is sandboxed with an opaque origin.
8888 EXPECT_TRUE(root->child_at(0)
8889 ->current_frame_host()
8890 ->GetLastCommittedOrigin()
Chris Palmerab5e5b52018-09-28 19:19:308891 .opaque());
Ian Clelland2c0351ed2018-04-13 01:44:388892 // And verify that the origin in the replication state is also opaque.
Chris Palmerab5e5b52018-09-28 19:19:308893 EXPECT_TRUE(root->child_at(0)->current_origin().opaque());
Ian Clelland2c0351ed2018-04-13 01:44:388894
8895 EXPECT_TRUE(ExecuteScriptAndExtractBool(
8896 root->child_at(0),
8897 "window.domAutomationController.send("
Ian Clelland905cde302019-01-04 20:33:298898 "document.featurePolicy.allowsFeature('geolocation'));",
Ian Clelland2c0351ed2018-04-13 01:44:388899 &success));
8900 EXPECT_TRUE(success);
8901}
8902
8903// Test that the constructed feature policy is correct in sandboxed
8904// frames. Sandboxed frames have an opaque origin, and if the frame policy,
8905// which is constructed in the parent frame, cannot send that origin through
8906// the browser process to the sandboxed frame, then the sandboxed frame's
8907// policy will be incorrect.
8908//
8909// This is a regression test for https://crbug.com/690520
8910IN_PROC_BROWSER_TEST_F(SitePerProcessFeaturePolicyJavaScriptBrowserTest,
8911 TestAllowAttributeInOpaqueOriginAfterNavigation) {
8912 GURL main_url(embedded_test_server()->GetURL(
8913 "a.com", "/page_with_data_iframe_and_allow.html"));
8914 GURL nav_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
8915
8916 EXPECT_TRUE(NavigateToURL(shell(), main_url));
8917
8918 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
8919 EXPECT_TRUE(root->current_replication_state().feature_policy_header.empty());
8920 EXPECT_EQ(1UL, root->child_count());
8921 // Verify that the child frame has an opaque origin.
8922 EXPECT_TRUE(root->child_at(0)
8923 ->current_frame_host()
8924 ->GetLastCommittedOrigin()
Chris Palmerab5e5b52018-09-28 19:19:308925 .opaque());
Ian Clelland2c0351ed2018-04-13 01:44:388926 // And verify that the origin in the replication state is also opaque.
Chris Palmerab5e5b52018-09-28 19:19:308927 EXPECT_TRUE(root->child_at(0)->current_origin().opaque());
Ian Clelland2c0351ed2018-04-13 01:44:388928
8929 // Verify that geolocation is enabled in the document.
8930 bool success = false;
8931 EXPECT_TRUE(ExecuteScriptAndExtractBool(
8932 root->child_at(0),
8933 "window.domAutomationController.send("
Ian Clelland905cde302019-01-04 20:33:298934 "document.featurePolicy.allowsFeature('geolocation'));",
Ian Clelland2c0351ed2018-04-13 01:44:388935 &success));
8936 EXPECT_TRUE(success);
8937
8938 TestNavigationObserver load_observer(shell()->web_contents());
danakj824a7ff2019-02-07 20:34:028939 EXPECT_TRUE(ExecuteScript(root->child_at(0),
8940 JsReplace("document.location.href=$1", nav_url)));
Ian Clelland2c0351ed2018-04-13 01:44:388941 load_observer.Wait();
8942
8943 // Verify that the child frame no longer has an opaque origin.
8944 EXPECT_FALSE(root->child_at(0)
8945 ->current_frame_host()
8946 ->GetLastCommittedOrigin()
Chris Palmerab5e5b52018-09-28 19:19:308947 .opaque());
Ian Clelland2c0351ed2018-04-13 01:44:388948 // Verify that the origin in the replication state is also no longer opaque.
Chris Palmerab5e5b52018-09-28 19:19:308949 EXPECT_FALSE(root->child_at(0)->current_origin().opaque());
Ian Clelland2c0351ed2018-04-13 01:44:388950
8951 // Verify that the new document does not have geolocation enabled.
8952 EXPECT_TRUE(ExecuteScriptAndExtractBool(
8953 root->child_at(0),
8954 "window.domAutomationController.send("
Ian Clelland905cde302019-01-04 20:33:298955 "document.featurePolicy.allowsFeature('geolocation'));",
Ian Clelland2c0351ed2018-04-13 01:44:388956 &success));
8957 EXPECT_FALSE(success);
8958}
8959
arthursonzogni85ef7712016-11-23 10:05:228960// Ensure that an iframe that navigates cross-site doesn't use the same process
8961// as its parent. Then when its parent navigates it via the "srcdoc" attribute,
8962// it must reuse its parent's process.
8963IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
8964 IframeSrcdocAfterCrossSiteNavigation) {
8965 GURL parent_url(embedded_test_server()->GetURL(
8966 "a.com", "/cross_site_iframe_factory.html?a(b)"));
8967 GURL child_url(embedded_test_server()->GetURL(
8968 "b.com", "/cross_site_iframe_factory.html?b()"));
arthursonzogniee7f43b2016-12-06 10:52:298969 GURL srcdoc_url(kAboutSrcDocURL);
arthursonzogni85ef7712016-11-23 10:05:228970
8971 // #1 Navigate to a page with a cross-site iframe.
8972 EXPECT_TRUE(NavigateToURL(shell(), parent_url));
8973
8974 // Ensure that the iframe uses its own process.
8975 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
8976 ASSERT_EQ(1u, root->child_count());
8977 FrameTreeNode* child = root->child_at(0);
8978 EXPECT_EQ(parent_url, root->current_url());
8979 EXPECT_EQ(child_url, child->current_url());
8980 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
8981 child->current_frame_host()->GetSiteInstance());
8982 EXPECT_NE(root->current_frame_host()->GetProcess(),
8983 child->current_frame_host()->GetProcess());
8984
8985 // #2 Navigate the iframe to its srcdoc attribute.
8986 TestNavigationObserver load_observer(shell()->web_contents());
8987 EXPECT_TRUE(ExecuteScript(
8988 root, "document.getElementById('child-0').srcdoc = 'srcdoc content';"));
8989 load_observer.Wait();
8990
8991 // Ensure that the iframe reuses its parent's process.
arthursonzogniee7f43b2016-12-06 10:52:298992 EXPECT_EQ(srcdoc_url, child->current_url());
arthursonzogni85ef7712016-11-23 10:05:228993 EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
8994 child->current_frame_host()->GetSiteInstance());
8995 EXPECT_EQ(root->current_frame_host()->GetProcess(),
8996 child->current_frame_host()->GetProcess());
8997}
8998
alexmosf65a795a2017-01-12 22:04:008999// Verify that a remote-to-local navigation in a crashed subframe works. See
9000// https://crbug.com/487872.
9001IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
9002 RemoteToLocalNavigationInCrashedSubframe) {
9003 GURL main_url(embedded_test_server()->GetURL(
9004 "a.com", "/cross_site_iframe_factory.html?a(b)"));
9005 EXPECT_TRUE(NavigateToURL(shell(), main_url));
9006
9007 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
9008 FrameTreeNode* child = root->child_at(0);
9009
9010 // Crash the subframe process.
9011 RenderProcessHost* child_process = child->current_frame_host()->GetProcess();
9012 {
9013 RenderProcessHostWatcher crash_observer(
9014 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
Wez0abfbf512018-03-03 01:54:459015 child_process->Shutdown(0);
alexmosf65a795a2017-01-12 22:04:009016 crash_observer.Wait();
9017 }
9018 EXPECT_FALSE(child->current_frame_host()->IsRenderFrameLive());
9019
9020 // Do a remote-to-local navigation of the child frame from the parent frame.
9021 TestFrameNavigationObserver frame_observer(child);
9022 GURL frame_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
danakj824a7ff2019-02-07 20:34:029023 EXPECT_TRUE(ExecuteScript(
9024 root, JsReplace("document.querySelector('iframe').src = $1", frame_url)));
alexmosf65a795a2017-01-12 22:04:009025 frame_observer.Wait();
9026
9027 EXPECT_TRUE(child->current_frame_host()->IsRenderFrameLive());
9028 EXPECT_FALSE(child->IsLoading());
9029 EXPECT_EQ(child->current_frame_host()->GetSiteInstance(),
9030 root->current_frame_host()->GetSiteInstance());
9031
9032 // Ensure the subframe is correctly attached in the frame tree, and that it
9033 // has correct content.
Nick Carterb7e71312018-08-03 23:36:139034 EXPECT_EQ(1, EvalJs(root, "frames.length;"));
alexmosf65a795a2017-01-12 22:04:009035
Nick Carterb7e71312018-08-03 23:36:139036 EXPECT_EQ("This page has no title.",
9037 EvalJs(root, "frames[0].document.body.innerText;"));
alexmosf65a795a2017-01-12 22:04:009038}
9039
lfg6ab84d522017-01-16 18:04:009040// Tests that trying to open a context menu in the old RFH after commiting a
9041// navigation doesn't crash the browser. https://crbug.com/677266.
9042IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
9043 ContextMenuAfterCrossProcessNavigation) {
9044 // Navigate to a.com.
9045 EXPECT_TRUE(NavigateToURL(
9046 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
9047
9048 // Disable the swapout ACK and the swapout timer.
9049 RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(
9050 shell()->web_contents()->GetMainFrame());
danakjcdee06c2019-02-08 18:04:079051 auto filter = base::MakeRefCounted<DropMessageFilter>(
9052 FrameMsgStart, FrameHostMsg_SwapOut_ACK::ID);
lfg6ab84d522017-01-16 18:04:009053 rfh->GetProcess()->AddFilter(filter.get());
9054 rfh->DisableSwapOutTimerForTesting();
9055
9056 // Open a popup on a.com to keep the process alive.
9057 OpenPopup(shell(), embedded_test_server()->GetURL("a.com", "/title2.html"),
9058 "foo");
9059
9060 // Cross-process navigation to b.com.
9061 EXPECT_TRUE(NavigateToURL(
9062 shell(), embedded_test_server()->GetURL("b.com", "/title3.html")));
9063
9064 // Pretend that a.com just requested a context menu. This used to cause a
9065 // because the RenderWidgetHostView is destroyed when the frame is swapped and
9066 // added to pending delete list.
9067 rfh->OnMessageReceived(
9068 FrameHostMsg_ContextMenu(rfh->GetRoutingID(), ContextMenuParams()));
9069}
9070
iclelland92f8c0b2017-04-19 12:43:059071// Test iframe container policy is replicated properly to the browser.
Ian Clelland91cff7ae2018-02-15 16:51:379072IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, ContainerPolicy) {
iclelland92f8c0b2017-04-19 12:43:059073 GURL url(embedded_test_server()->GetURL("/allowed_frames.html"));
9074 EXPECT_TRUE(NavigateToURL(shell(), url));
9075
9076 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
9077
Ian Clellandcdc4f312017-10-13 22:24:129078 EXPECT_EQ(0UL, root->effective_frame_policy().container_policy.size());
9079 EXPECT_EQ(
9080 0UL, root->child_at(0)->effective_frame_policy().container_policy.size());
9081 EXPECT_EQ(
9082 0UL, root->child_at(1)->effective_frame_policy().container_policy.size());
9083 EXPECT_EQ(
9084 2UL, root->child_at(2)->effective_frame_policy().container_policy.size());
9085 EXPECT_EQ(
9086 2UL, root->child_at(3)->effective_frame_policy().container_policy.size());
iclelland92f8c0b2017-04-19 12:43:059087}
9088
9089// Test dynamic updates to iframe "allow" attribute are propagated correctly.
Ian Clelland91cff7ae2018-02-15 16:51:379090IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, ContainerPolicyDynamic) {
iclelland92f8c0b2017-04-19 12:43:059091 GURL main_url(embedded_test_server()->GetURL("/allowed_frames.html"));
9092 GURL nav_url(
9093 embedded_test_server()->GetURL("b.com", "/feature-policy2.html"));
9094 EXPECT_TRUE(NavigateToURL(shell(), main_url));
9095
9096 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
9097
Ian Clellandcdc4f312017-10-13 22:24:129098 EXPECT_EQ(
9099 2UL, root->child_at(2)->effective_frame_policy().container_policy.size());
iclelland92f8c0b2017-04-19 12:43:059100
9101 // Removing the "allow" attribute; pending policy should update, but effective
9102 // policy remains unchanged.
9103 EXPECT_TRUE(ExecuteScript(
9104 root, "document.getElementById('child-2').setAttribute('allow','')"));
Ian Clellandcdc4f312017-10-13 22:24:129105 EXPECT_EQ(
9106 2UL, root->child_at(2)->effective_frame_policy().container_policy.size());
9107 EXPECT_EQ(0UL,
9108 root->child_at(2)->pending_frame_policy().container_policy.size());
iclelland92f8c0b2017-04-19 12:43:059109
9110 // Navigate the frame; pending policy should be committed.
9111 NavigateFrameToURL(root->child_at(2), nav_url);
Ian Clellandcdc4f312017-10-13 22:24:129112 EXPECT_EQ(
9113 0UL, root->child_at(2)->effective_frame_policy().container_policy.size());
iclelland92f8c0b2017-04-19 12:43:059114}
9115
Ian Clelland0e8654a2017-04-28 15:06:299116// Check that out-of-process frames correctly calculate the container policy in
9117// the renderer when navigating cross-origin. The policy should be unchanged
9118// when modified dynamically in the parent frame. When the frame is navigated,
9119// the new renderer should have the correct container policy.
9120//
9121// TODO(iclelland): Once there is a proper JS inspection API from the renderer,
9122// use that to check the policy. Until then, we test webkitFullscreenEnabled,
9123// which conveniently just returns the result of calling isFeatureEnabled on
9124// the fullscreen feature. Since there are no HTTP header policies involved,
9125// this verifies the presence of the container policy in the iframe.
9126// https://crbug.com/703703
Ian Clelland91cff7ae2018-02-15 16:51:379127IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
9128 ContainerPolicyCrossOriginNavigation) {
Ian Clelland0e8654a2017-04-28 15:06:299129 WebContentsImpl* contents = web_contents();
9130 FrameTreeNode* root = contents->GetFrameTree()->root();
9131
9132 // Helper to check if a frame is allowed to go fullscreen on the renderer
9133 // side.
9134 auto is_fullscreen_allowed = [](FrameTreeNode* ftn) {
Nick Carterb7e71312018-08-03 23:36:139135 return EvalJs(ftn, "document.webkitFullscreenEnabled;");
Ian Clelland0e8654a2017-04-28 15:06:299136 };
9137
9138 // Load a page with an <iframe> without allowFullscreen.
9139 EXPECT_TRUE(NavigateToURL(
9140 shell(), embedded_test_server()->GetURL(
9141 "a.com", "/cross_site_iframe_factory.html?a(b)")));
9142
9143 // Dynamically enable fullscreen for the subframe and check that the
9144 // fullscreen property was updated on the FrameTreeNode.
9145 EXPECT_TRUE(ExecuteScript(
9146 root, "document.getElementById('child-0').allowFullscreen='true'"));
9147
9148 // No change is expected to the container policy for dynamic modification of
9149 // a loaded frame.
Nick Carterb7e71312018-08-03 23:36:139150 EXPECT_EQ(false, is_fullscreen_allowed(root->child_at(0)));
Ian Clelland0e8654a2017-04-28 15:06:299151
9152 // Cross-site navigation should update the container policy in the new render
9153 // frame.
9154 NavigateFrameToURL(root->child_at(0),
9155 embedded_test_server()->GetURL("c.com", "/title1.html"));
Nick Carterb7e71312018-08-03 23:36:139156 EXPECT_EQ(true, is_fullscreen_allowed(root->child_at(0)));
Ian Clelland0e8654a2017-04-28 15:06:299157}
9158
iclelland92f8c0b2017-04-19 12:43:059159// Test that dynamic updates to iframe sandbox attribute correctly set the
9160// replicated container policy.
Ian Clelland91cff7ae2018-02-15 16:51:379161IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
iclelland92f8c0b2017-04-19 12:43:059162 ContainerPolicySandboxDynamic) {
9163 GURL main_url(embedded_test_server()->GetURL("/allowed_frames.html"));
9164 GURL nav_url(
9165 embedded_test_server()->GetURL("b.com", "/feature-policy2.html"));
9166 EXPECT_TRUE(NavigateToURL(shell(), main_url));
9167
9168 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
9169
9170 // Validate that the effective container policy contains a single non-unique
9171 // origin.
Luna Lu2e713992017-11-07 01:45:589172 const blink::ParsedFeaturePolicy initial_effective_policy =
Ian Clellandcdc4f312017-10-13 22:24:129173 root->child_at(2)->effective_frame_policy().container_policy;
Luna Lu0c2cd5a2019-02-12 16:24:599174 EXPECT_EQ(1UL, initial_effective_policy[0].values.size());
9175 EXPECT_FALSE(initial_effective_policy[0].values.begin()->first.opaque());
iclelland92f8c0b2017-04-19 12:43:059176
9177 // Set the "sandbox" attribute; pending policy should update, and should now
Ian Clelland2c0351ed2018-04-13 01:44:389178 // be flagged as matching the opaque origin of the frame (without containing
9179 // an actual opaque origin, since the parent frame doesn't actually have that
9180 // origin yet) but the effective policy should remain unchanged.
iclelland92f8c0b2017-04-19 12:43:059181 EXPECT_TRUE(ExecuteScript(
9182 root, "document.getElementById('child-2').setAttribute('sandbox','')"));
Luna Lu2e713992017-11-07 01:45:589183 const blink::ParsedFeaturePolicy updated_effective_policy =
Ian Clellandcdc4f312017-10-13 22:24:129184 root->child_at(2)->effective_frame_policy().container_policy;
Luna Lu2e713992017-11-07 01:45:589185 const blink::ParsedFeaturePolicy updated_pending_policy =
Ian Clellandcdc4f312017-10-13 22:24:129186 root->child_at(2)->pending_frame_policy().container_policy;
Luna Lu0c2cd5a2019-02-12 16:24:599187 EXPECT_EQ(1UL, updated_effective_policy[0].values.size());
9188 EXPECT_FALSE(updated_effective_policy[0].values.begin()->first.opaque());
9189 EXPECT_GE(updated_pending_policy[0].opaque_value, blink::PolicyValue(true));
9190 EXPECT_EQ(0UL, updated_pending_policy[0].values.size());
iclelland92f8c0b2017-04-19 12:43:059191
9192 // Navigate the frame; pending policy should now be committed.
9193 NavigateFrameToURL(root->child_at(2), nav_url);
Luna Lu2e713992017-11-07 01:45:589194 const blink::ParsedFeaturePolicy final_effective_policy =
Ian Clellandcdc4f312017-10-13 22:24:129195 root->child_at(2)->effective_frame_policy().container_policy;
Luna Lu0c2cd5a2019-02-12 16:24:599196 EXPECT_GE(final_effective_policy[0].opaque_value, blink::PolicyValue(true));
9197 EXPECT_EQ(0UL, final_effective_policy[0].values.size());
iclelland92f8c0b2017-04-19 12:43:059198}
9199
Ian Clellandbd2a91a2018-07-07 05:13:579200// Test that creating a new remote frame at the same origin as its parent
9201// results in the correct feature policy in the RemoteSecurityContext.
9202// https://crbug.com/852102
9203IN_PROC_BROWSER_TEST_F(SitePerProcessFeaturePolicyJavaScriptBrowserTest,
9204 FeaturePolicyConstructionInExistingProxy) {
9205 WebContentsImpl* contents = web_contents();
9206 FrameTreeNode* root = contents->GetFrameTree()->root();
9207
9208 // Navigate to a page (1) with a cross-origin iframe (2). After load, the
9209 // frame tree should look like:
9210 //
9211 // a.com(1)
9212 // /
9213 // b.com(2)
9214 EXPECT_TRUE(NavigateToURL(
9215 shell(), embedded_test_server()->GetURL(
9216 "a.com", "/cross_site_iframe_factory.html?a(b)")));
9217
9218 // Programmatically create a new same-origin frame (3) under the root, with a
9219 // cross-origin child (4). Since two SiteInstances already exist at this
9220 // point, a proxy for frame 3 will be created in the renderer for frames 2 and
9221 // 4. The frame tree should look like:
9222 //
9223 // a.com(1)
9224 // / \
9225 // b.com(2) a.com(3)
9226 // \
9227 // b.com(4)
danakj824a7ff2019-02-07 20:34:029228 auto create_subframe_script = JsReplace(
9229 "var f = document.createElement('iframe'); f.src=$1; "
9230 "document.body.appendChild(f);",
9231 embedded_test_server()->GetURL(
9232 "a.com", "/cross_site_iframe_factory.html?a(b{allow-autoplay})"));
Ian Clellandbd2a91a2018-07-07 05:13:579233 EXPECT_TRUE(ExecuteScript(root, create_subframe_script));
9234 EXPECT_TRUE(WaitForLoadStop(contents));
9235
9236 // Verify the shape of the frame tree
9237 EXPECT_EQ(2UL, root->child_count());
9238 EXPECT_EQ(1UL, root->child_at(1)->child_count());
9239
9240 // Ask frame 4 to report the enabled state of the autoplay feature. Frame 3's
9241 // policy should allow autoplay if created correctly, as it is same-origin
9242 // with the root, where the feature is enabled by default, and therefore
9243 // should be able to delegate it to frame 4.
9244 // This indirectly tests the replicated policy in frame 3: Because frame 4 is
9245 // cross-origin to frame 3, it will use the proxy's replicated policy as the
9246 // parent policy; otherwise we would just ask frame 3 to report its own state.
9247 bool success = false;
Ian Clelland905cde302019-01-04 20:33:299248 EXPECT_TRUE(ExecuteScriptAndExtractBool(
9249 root->child_at(1)->child_at(0),
9250 "window.domAutomationController.send("
9251 "document.featurePolicy.allowsFeature('autoplay'));",
9252 &success));
Ian Clellandbd2a91a2018-07-07 05:13:579253 EXPECT_TRUE(success);
9254}
9255
csharrisond86c35bc2017-02-02 17:41:269256// Test harness that allows for "barrier" style delaying of requests matching
9257// certain paths. Call SetDelayedRequestsForPath to delay requests, then
9258// SetUpEmbeddedTestServer to register handlers and start the server.
9259class RequestDelayingSitePerProcessBrowserTest
9260 : public SitePerProcessBrowserTest {
9261 public:
9262 RequestDelayingSitePerProcessBrowserTest()
Jeremy Roman04f27c372017-10-27 15:20:559263 : test_server_(std::make_unique<net::EmbeddedTestServer>()) {}
csharrisond86c35bc2017-02-02 17:41:269264
9265 // Must be called after any calls to SetDelayedRequestsForPath.
9266 void SetUpEmbeddedTestServer() {
csharrisond86c35bc2017-02-02 17:41:269267 SetupCrossSiteRedirector(test_server_.get());
9268 test_server_->RegisterRequestHandler(base::Bind(
9269 &RequestDelayingSitePerProcessBrowserTest::HandleMockResource,
9270 base::Unretained(this)));
9271 ASSERT_TRUE(test_server_->Start());
9272 }
9273
9274 // Delays |num_delayed| requests with URLs whose path parts match |path|. When
9275 // the |num_delayed| + 1 request matching the path comes in, the rest are
9276 // unblocked.
9277 // Note: must be called on the UI thread before |test_server_| is started.
9278 void SetDelayedRequestsForPath(const std::string& path, int num_delayed) {
9279 DCHECK_CURRENTLY_ON(BrowserThread::UI);
9280 DCHECK(!test_server_->Started());
9281 num_remaining_requests_to_delay_for_path_[path] = num_delayed;
9282 }
9283
9284 private:
9285 // Called on the test server's thread.
9286 void AddDelayedResponse(const net::test_server::SendBytesCallback& send,
9287 const net::test_server::SendCompleteCallback& done) {
9288 // Just create a closure that closes the socket without sending a response.
9289 // This will propagate an error to the underlying request.
9290 send_response_closures_.push_back(base::Bind(send, "", done));
9291 }
9292
9293 // Custom embedded test server handler. Looks for requests matching
9294 // num_remaining_requests_to_delay_for_path_, and delays them if necessary. As
9295 // soon as a single request comes in and:
9296 // 1) It matches a delayed path
9297 // 2) No path has any more requests to delay
9298 // Then we release the barrier and finish all delayed requests.
9299 std::unique_ptr<net::test_server::HttpResponse> HandleMockResource(
9300 const net::test_server::HttpRequest& request) {
9301 auto it =
9302 num_remaining_requests_to_delay_for_path_.find(request.GetURL().path());
9303 if (it == num_remaining_requests_to_delay_for_path_.end())
9304 return nullptr;
9305
9306 // If there are requests to delay for this path, make a delayed request
9307 // which will be finished later. Otherwise fall through to the bottom and
9308 // send an empty response.
9309 if (it->second > 0) {
9310 --it->second;
Jeremy Roman04f27c372017-10-27 15:20:559311 return std::make_unique<DelayedResponse>(this);
csharrisond86c35bc2017-02-02 17:41:269312 }
9313 MaybeStartRequests();
9314 return std::unique_ptr<net::test_server::BasicHttpResponse>();
9315 }
9316
9317 // If there are no more requests to delay, post a series of tasks finishing
9318 // all the delayed tasks. This will be called on the test server's thread.
9319 void MaybeStartRequests() {
9320 for (auto it : num_remaining_requests_to_delay_for_path_) {
9321 if (it.second > 0)
9322 return;
9323 }
9324 for (const auto it : send_response_closures_) {
9325 it.Run();
9326 }
9327 }
9328
9329 // This class passes the callbacks needed to respond to a request to the
9330 // underlying test fixture.
9331 class DelayedResponse : public net::test_server::BasicHttpResponse {
9332 public:
9333 explicit DelayedResponse(
9334 RequestDelayingSitePerProcessBrowserTest* test_harness)
9335 : test_harness_(test_harness) {}
9336 void SendResponse(
9337 const net::test_server::SendBytesCallback& send,
9338 const net::test_server::SendCompleteCallback& done) override {
9339 test_harness_->AddDelayedResponse(send, done);
9340 }
9341
9342 private:
9343 RequestDelayingSitePerProcessBrowserTest* test_harness_;
9344
9345 DISALLOW_COPY_AND_ASSIGN(DelayedResponse);
9346 };
9347
9348 // Set of closures to call which will complete delayed requests. May only be
9349 // modified on the test_server_'s thread.
9350 std::vector<base::Closure> send_response_closures_;
9351
9352 // Map from URL paths to the number of requests to delay for that particular
9353 // path. Initialized on the UI thread but modified and read on the test
9354 // server's thread after the |test_server_| is started.
9355 std::map<std::string, int> num_remaining_requests_to_delay_for_path_;
9356
9357 // Don't use embedded_test_server() because this one requires custom
9358 // initialization.
9359 std::unique_ptr<net::EmbeddedTestServer> test_server_;
9360};
9361
9362// Regression tests for https://crbug.com/678206, where the request throttling
9363// in ResourceScheduler was not updated for OOPIFs. This resulted in a single
9364// hung delayable request (e.g. video) starving all other delayable requests.
9365// The tests work by delaying n requests in a cross-domain iframe. Once the n +
9366// 1st request goes through to the network stack (ensuring it was not starved),
9367// the delayed request completed.
9368//
9369// If the logic is not correct, these tests will time out, as the n + 1st
9370// request will never start.
9371IN_PROC_BROWSER_TEST_F(RequestDelayingSitePerProcessBrowserTest,
9372 DelayableSubframeRequestsOneFrame) {
9373 std::string path = "/mock-video.mp4";
9374 SetDelayedRequestsForPath(path, 2);
9375 SetUpEmbeddedTestServer();
9376 GURL url(embedded_test_server()->GetURL(
9377 "a.com", base::StringPrintf("/site_isolation/"
9378 "subframes_with_resources.html?urls=%s&"
9379 "numSubresources=3",
9380 path.c_str())));
9381 EXPECT_TRUE(NavigateToURL(shell(), url));
9382 bool result;
9383 EXPECT_TRUE(ExecuteScriptAndExtractBool(shell(), "createFrames()", &result));
9384 EXPECT_TRUE(result);
9385}
9386
9387IN_PROC_BROWSER_TEST_F(RequestDelayingSitePerProcessBrowserTest,
9388 DelayableSubframeRequestsTwoFrames) {
9389 std::string path0 = "/mock-video0.mp4";
9390 std::string path1 = "/mock-video1.mp4";
9391 SetDelayedRequestsForPath(path0, 2);
9392 SetDelayedRequestsForPath(path1, 2);
9393 SetUpEmbeddedTestServer();
9394 GURL url(embedded_test_server()->GetURL(
9395 "a.com", base::StringPrintf("/site_isolation/"
9396 "subframes_with_resources.html?urls=%s,%s&"
9397 "numSubresources=3",
9398 path0.c_str(), path1.c_str())));
9399 EXPECT_TRUE(NavigateToURL(shell(), url));
9400 bool result;
9401 EXPECT_TRUE(ExecuteScriptAndExtractBool(shell(), "createFrames()", &result));
9402 EXPECT_TRUE(result);
9403}
9404
ekaramada06dc6c2017-02-22 20:07:439405#if defined(OS_ANDROID)
9406class TextSelectionObserver : public TextInputManager::Observer {
9407 public:
9408 explicit TextSelectionObserver(TextInputManager* text_input_manager)
9409 : text_input_manager_(text_input_manager) {
9410 text_input_manager->AddObserver(this);
9411 }
9412
9413 ~TextSelectionObserver() { text_input_manager_->RemoveObserver(this); }
9414
9415 void WaitForSelectedText(const std::string& expected_text) {
9416 if (last_selected_text_ == expected_text)
9417 return;
9418 expected_text_ = expected_text;
9419 loop_runner_ = new MessageLoopRunner();
9420 loop_runner_->Run();
9421 }
9422
9423 private:
9424 void OnTextSelectionChanged(TextInputManager* text_input_manager,
9425 RenderWidgetHostViewBase* updated_view) override {
ekaramad374b2662017-03-02 20:44:529426 last_selected_text_ = base::UTF16ToUTF8(
9427 text_input_manager->GetTextSelection(updated_view)->selected_text());
9428 if (last_selected_text_ == expected_text_ && loop_runner_)
9429 loop_runner_->Quit();
ekaramada06dc6c2017-02-22 20:07:439430 }
ekaramad374b2662017-03-02 20:44:529431
ekaramada06dc6c2017-02-22 20:07:439432 TextInputManager* const text_input_manager_;
9433 std::string last_selected_text_;
9434 std::string expected_text_;
9435 scoped_refptr<MessageLoopRunner> loop_runner_;
9436
9437 DISALLOW_COPY_AND_ASSIGN(TextSelectionObserver);
9438};
9439
9440class SitePerProcessAndroidImeTest : public SitePerProcessBrowserTest {
9441 public:
9442 SitePerProcessAndroidImeTest() : SitePerProcessBrowserTest() {}
9443 ~SitePerProcessAndroidImeTest() override {}
9444
9445 protected:
9446 ImeAdapterAndroid* ime_adapter() {
9447 return static_cast<RenderWidgetHostViewAndroid*>(
9448 web_contents()->GetRenderWidgetHostView())
9449 ->ime_adapter_for_testing();
9450 }
9451
ekaramada06dc6c2017-02-22 20:07:439452 void FocusInputInFrame(RenderFrameHostImpl* frame) {
9453 ASSERT_TRUE(ExecuteScript(frame, "window.focus(); input.focus();"));
9454 }
9455
9456 // Creates a page with multiple (nested) OOPIFs and populates all of them
9457 // with an <input> element along with the required handlers for the test.
9458 void LoadPage() {
9459 ASSERT_TRUE(NavigateToURL(
9460 shell(),
9461 GURL(embedded_test_server()->GetURL(
9462 "a.com", "/cross_site_iframe_factory.html?a(b,c(a(b)))"))));
9463 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
9464 frames_.push_back(root->current_frame_host());
9465 frames_.push_back(root->child_at(0)->current_frame_host());
9466 frames_.push_back(root->child_at(1)->current_frame_host());
9467 frames_.push_back(root->child_at(1)->child_at(0)->current_frame_host());
9468 frames_.push_back(
9469 root->child_at(1)->child_at(0)->child_at(0)->current_frame_host());
9470
9471 // Adds an <input> to frame and sets up a handler for |window.oninput|. When
9472 // the input event is fired (by changing the value of <input> element), the
9473 // handler will select all the text so that the corresponding text selection
9474 // update on the browser side notifies the test about input insertion.
9475 std::string add_input_script =
9476 "var input = document.createElement('input');"
9477 "document.body.appendChild(input);"
9478 "window.oninput = function() {"
9479 " input.select();"
9480 "};";
9481
9482 for (auto* frame : frames_)
9483 ASSERT_TRUE(ExecuteScript(frame, add_input_script));
9484 }
9485
9486 // This methods tries to commit |text| by simulating a native call from Java.
9487 void CommitText(const char* text) {
9488 JNIEnv* env = base::android::AttachCurrentThread();
9489
9490 // A valid caller is needed for ImeAdapterAndroid::GetUnderlinesFromSpans.
9491 base::android::ScopedJavaLocalRef<jobject> caller =
9492 ime_adapter()->java_ime_adapter_for_testing(env);
9493
9494 // Input string from Java side.
9495 base::android::ScopedJavaLocalRef<jstring> jtext =
9496 base::android::ConvertUTF8ToJavaString(env, text);
9497
9498 // Simulating a native call from Java side.
9499 ime_adapter()->CommitText(
9500 env, base::android::JavaParamRef<jobject>(env, caller.obj()),
9501 base::android::JavaParamRef<jobject>(env, jtext.obj()),
9502 base::android::JavaParamRef<jstring>(env, jtext.obj()), 0);
9503 }
9504
9505 std::vector<RenderFrameHostImpl*> frames_;
9506
9507 private:
9508 DISALLOW_COPY_AND_ASSIGN(SitePerProcessAndroidImeTest);
9509};
9510
9511// This test verifies that committing text will be applied on the focused
9512// RenderWidgetHost.
9513IN_PROC_BROWSER_TEST_F(SitePerProcessAndroidImeTest,
9514 CommitTextForFocusedWidget) {
9515 LoadPage();
9516 TextSelectionObserver selection_observer(
9517 web_contents()->GetTextInputManager());
9518 for (size_t index = 0; index < frames_.size(); ++index) {
9519 std::string text = base::StringPrintf("text%zu", index);
9520 FocusInputInFrame(frames_[index]);
9521 CommitText(text.c_str());
9522 selection_observer.WaitForSelectedText(text);
9523 }
9524}
9525#endif // OS_ANDROID
9526
alexmos0431b6b2017-03-01 02:52:439527// Test that an OOPIF at b.com can navigate to a cross-site a.com URL that
9528// transfers back to b.com. See https://crbug.com/681077#c10 and
9529// https://crbug.com/660407.
9530IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
9531 SubframeTransfersToCurrentRFH) {
9532 GURL main_url(embedded_test_server()->GetURL(
9533 "a.com", "/cross_site_iframe_factory.html?a(b)"));
9534 ASSERT_TRUE(NavigateToURL(shell(), main_url));
9535
9536 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
9537 ->GetFrameTree()
9538 ->root();
9539 scoped_refptr<SiteInstanceImpl> b_site_instance =
9540 root->child_at(0)->current_frame_host()->GetSiteInstance();
9541
9542 // Navigate subframe to a URL that will redirect from a.com back to b.com.
9543 // This navigation shouldn't time out. Also ensure that the pending RFH
9544 // that was created for a.com is destroyed.
9545 GURL frame_url(
9546 embedded_test_server()->GetURL("a.com", "/cross-site/b.com/title2.html"));
9547 NavigateIframeToURL(shell()->web_contents(), "child-0", frame_url);
clamy610c63b32017-12-22 15:05:189548 EXPECT_FALSE(root->child_at(0)->render_manager()->speculative_frame_host());
alexmos0431b6b2017-03-01 02:52:439549 GURL redirected_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
9550 EXPECT_EQ(root->child_at(0)->current_url(), redirected_url);
9551 EXPECT_EQ(b_site_instance,
9552 root->child_at(0)->current_frame_host()->GetSiteInstance());
9553
9554 // Try the same navigation, but use the browser-initiated path.
9555 NavigateFrameToURL(root->child_at(0), frame_url);
clamy610c63b32017-12-22 15:05:189556 EXPECT_FALSE(root->child_at(0)->render_manager()->speculative_frame_host());
alexmos0431b6b2017-03-01 02:52:439557 EXPECT_EQ(root->child_at(0)->current_url(), redirected_url);
9558 EXPECT_EQ(b_site_instance,
9559 root->child_at(0)->current_frame_host()->GetSiteInstance());
9560}
9561
Daniel Cheng999698bd2017-03-22 04:56:379562IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
9563 FrameSwapPreservesUniqueName) {
9564 GURL main_url(embedded_test_server()->GetURL(
9565 "a.com", "/cross_site_iframe_factory.html?a(a)"));
9566 ASSERT_TRUE(NavigateToURL(shell(), main_url));
9567
9568 // Navigate the subframe cross-site…
9569 {
9570 GURL url(embedded_test_server()->GetURL("b.com", "/title1.html"));
9571 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "child-0", url));
9572 }
9573 // and then same-site…
9574 {
9575 GURL url(embedded_test_server()->GetURL("a.com", "/title1.html"));
9576 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "child-0", url));
9577 }
9578 // and cross-site once more.
9579 {
9580 GURL url(embedded_test_server()->GetURL("b.com", "/title1.html"));
9581 EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(), "child-0", url));
9582 }
9583
9584 // Inspect the navigation entries and make sure that the navigation target
9585 // remained constant across frame swaps.
Lucas Furukawa Gadani5553a152019-01-08 18:55:579586 auto& controller = static_cast<NavigationControllerImpl&>(
Daniel Cheng999698bd2017-03-22 04:56:379587 shell()->web_contents()->GetController());
9588 EXPECT_EQ(4, controller.GetEntryCount());
9589
9590 std::set<std::string> names;
9591 for (int i = 0; i < controller.GetEntryCount(); ++i) {
9592 NavigationEntryImpl::TreeNode* root =
9593 controller.GetEntryAtIndex(i)->root_node();
9594 ASSERT_EQ(1U, root->children.size());
9595 names.insert(root->children[0]->frame_entry->frame_unique_name());
9596 }
9597
9598 // More than one entry in the set means that the subframe frame navigation
9599 // entries didn't have a consistent unique name. This will break history
9600 // navigations =(
9601 EXPECT_THAT(names, SizeIs(1)) << "Mismatched names for subframe!";
9602}
9603
arthursonzognibc5732b52017-05-17 03:37:289604// Tests that POST body is not lost when it targets a OOPIF.
9605// See https://crbug.com/710937.
9606IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, PostTargetSubFrame) {
9607 // Navigate to a page with an OOPIF.
9608 GURL main_url(
9609 embedded_test_server()->GetURL("/frame_tree/page_with_one_frame.html"));
9610 EXPECT_TRUE(NavigateToURL(shell(), main_url));
9611 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
9612 ->GetFrameTree()
9613 ->root();
9614
9615 // The main frame and the subframe live on different processes.
9616 EXPECT_EQ(1u, root->child_count());
9617 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
9618 root->child_at(0)->current_frame_host()->GetSiteInstance());
9619
9620 // Make a form submission from the main frame and target the OOPIF.
9621 GURL form_url(embedded_test_server()->GetURL("/echoall"));
9622 TestNavigationObserver form_post_observer(shell()->web_contents(), 1);
danakj824a7ff2019-02-07 20:34:029623 EXPECT_TRUE(ExecuteScript(shell()->web_contents(), JsReplace(R"(
arthursonzognibc5732b52017-05-17 03:37:289624 var form = document.createElement('form');
9625
9626 // POST form submission to /echoall.
9627 form.setAttribute("method", "POST");
danakj824a7ff2019-02-07 20:34:029628 form.setAttribute("action", $1);
arthursonzognibc5732b52017-05-17 03:37:289629
9630 // Target the OOPIF.
9631 form.setAttribute("target", "child-name-0");
9632
9633 // Add some POST data: "my_token=my_value";
9634 var input = document.createElement("input");
9635 input.setAttribute("type", "hidden");
9636 input.setAttribute("name", "my_token");
9637 input.setAttribute("value", "my_value");
9638 form.appendChild(input);
9639
9640 // Submit the form.
9641 document.body.appendChild(form);
9642 form.submit();
danakj824a7ff2019-02-07 20:34:029643 )",
9644 form_url)));
arthursonzognibc5732b52017-05-17 03:37:289645 form_post_observer.Wait();
9646
9647 NavigationEntryImpl* entry = static_cast<NavigationEntryImpl*>(
9648 shell()->web_contents()->GetController().GetLastCommittedEntry());
9649 // TODO(arthursonzogni): This is wrong. The last committed entry was
9650 // renderer-initiated. See https://crbug.com/722251.
9651 EXPECT_FALSE(entry->is_renderer_initiated());
9652
9653 // Verify that POST body was correctly passed to the server and ended up in
9654 // the body of the page.
Nick Carterb7e71312018-08-03 23:36:139655 EXPECT_EQ("my_token=my_value\n",
9656 EvalJs(root->child_at(0),
9657 "document.getElementsByTagName('pre')[0].innerText;"));
arthursonzognibc5732b52017-05-17 03:37:289658}
9659
Alex Moshchuk5bb1f9442018-01-27 07:41:329660// Tests that POST method and body is not lost when an OOPIF submits a form
9661// that targets the main frame. See https://crbug.com/806215.
9662IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
9663 PostTargetsMainFrameFromOOPIF) {
9664 // Navigate to a page with an OOPIF.
9665 GURL main_url(
9666 embedded_test_server()->GetURL("/frame_tree/page_with_one_frame.html"));
9667 EXPECT_TRUE(NavigateToURL(shell(), main_url));
9668 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
9669
9670 // The main frame and the subframe live on different processes.
9671 EXPECT_EQ(1u, root->child_count());
9672 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
9673 root->child_at(0)->current_frame_host()->GetSiteInstance());
9674
9675 // Make a form submission from the subframe and target its parent frame.
9676 GURL form_url(embedded_test_server()->GetURL("/echoall"));
9677 TestNavigationObserver form_post_observer(web_contents());
danakj824a7ff2019-02-07 20:34:029678 EXPECT_TRUE(ExecuteScript(root->child_at(0)->current_frame_host(),
9679 JsReplace(R"(
Alex Moshchuk5bb1f9442018-01-27 07:41:329680 var form = document.createElement('form');
9681
9682 // POST form submission to /echoall.
9683 form.setAttribute("method", "POST");
danakj824a7ff2019-02-07 20:34:029684 form.setAttribute("action", $1);
Alex Moshchuk5bb1f9442018-01-27 07:41:329685
9686 // Target the parent.
9687 form.setAttribute("target", "_parent");
9688
9689 // Add some POST data: "my_token=my_value";
9690 var input = document.createElement("input");
9691 input.setAttribute("type", "hidden");
9692 input.setAttribute("name", "my_token");
9693 input.setAttribute("value", "my_value");
9694 form.appendChild(input);
9695
9696 // Submit the form.
9697 document.body.appendChild(form);
9698 form.submit();
danakj824a7ff2019-02-07 20:34:029699 )",
9700 form_url)));
Alex Moshchuk5bb1f9442018-01-27 07:41:329701 form_post_observer.Wait();
9702
9703 // Verify that the FrameNavigationEntry's method is POST.
9704 NavigationEntryImpl* entry = static_cast<NavigationEntryImpl*>(
9705 web_contents()->GetController().GetLastCommittedEntry());
9706 EXPECT_EQ("POST", entry->root_node()->frame_entry->method());
9707
9708 // Verify that POST body was correctly passed to the server and ended up in
9709 // the body of the page.
9710 std::string body;
9711 EXPECT_TRUE(ExecuteScriptAndExtractString(root, R"(
9712 var body = document.getElementsByTagName('pre')[0].innerText;
9713 window.domAutomationController.send(body);)", &body));
9714 EXPECT_EQ("my_token=my_value\n", body);
9715
9716 // Reload the main frame and ensure the POST body is preserved. This checks
9717 // that the POST body was saved in the FrameNavigationEntry.
9718 web_contents()->GetController().Reload(ReloadType::NORMAL,
9719 false /* check_for_repost */);
9720 EXPECT_TRUE(WaitForLoadStop(web_contents()));
9721 body = "";
9722 EXPECT_TRUE(ExecuteScriptAndExtractString(root, R"(
9723 var body = document.getElementsByTagName('pre')[0].innerText;
9724 window.domAutomationController.send(body);)", &body));
9725 EXPECT_EQ("my_token=my_value\n", body);
9726}
9727
alexmosc5e91faf2017-05-25 02:55:019728// Verify that a remote-to-local main frame navigation doesn't overwrite
9729// the previous history entry. See https://crbug.com/725716.
Luciano Pachecoc2072682018-07-02 06:36:299730IN_PROC_BROWSER_TEST_F(
9731 SitePerProcessBrowserTest,
9732 DISABLED_CrossProcessMainFrameNavigationDoesNotOverwriteHistory) {
alexmosc5e91faf2017-05-25 02:55:019733 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
9734 GURL bar_url(embedded_test_server()->GetURL("bar.com", "/title2.html"));
9735
9736 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
9737
9738 // Open a same-site popup to keep the www.foo.com process alive.
9739 OpenPopup(shell(), GURL(url::kAboutBlankURL), "foo");
9740
9741 // Navigate foo -> bar -> foo.
Alex Moshchuk7e26eca2018-03-03 01:34:299742 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), bar_url));
9743 EXPECT_TRUE(NavigateToURLFromRenderer(shell(), foo_url));
alexmosc5e91faf2017-05-25 02:55:019744
9745 // There should be three history entries.
9746 EXPECT_EQ(3, web_contents()->GetController().GetEntryCount());
9747
9748 // Go back: this should go to bar.com.
9749 {
9750 TestNavigationObserver back_observer(web_contents());
9751 web_contents()->GetController().GoBack();
9752 back_observer.Wait();
9753 }
9754 EXPECT_EQ(bar_url, web_contents()->GetMainFrame()->GetLastCommittedURL());
9755
9756 // Go back again. This should go to foo.com.
9757 {
9758 TestNavigationObserver back_observer(web_contents());
9759 web_contents()->GetController().GoBack();
9760 back_observer.Wait();
9761 }
9762 EXPECT_EQ(foo_url, web_contents()->GetMainFrame()->GetLastCommittedURL());
9763}
9764
kenrb04323782017-06-23 01:23:329765// Class to sniff incoming IPCs for FrameHostMsg_SetIsInert messages.
9766class SetIsInertMessageFilter : public content::BrowserMessageFilter {
9767 public:
9768 SetIsInertMessageFilter()
9769 : content::BrowserMessageFilter(FrameMsgStart),
kenrb04323782017-06-23 01:23:329770 msg_received_(false) {}
9771
9772 bool OnMessageReceived(const IPC::Message& message) override {
9773 IPC_BEGIN_MESSAGE_MAP(SetIsInertMessageFilter, message)
9774 IPC_MESSAGE_HANDLER(FrameHostMsg_SetIsInert, OnSetIsInert)
9775 IPC_END_MESSAGE_MAP()
9776 return false;
9777 }
9778
9779 bool is_inert() const { return is_inert_; }
9780
Lukasz Anforowicz5a92f052018-08-15 20:38:479781 void Wait() { run_loop_.Run(); }
kenrb04323782017-06-23 01:23:329782
9783 private:
9784 ~SetIsInertMessageFilter() override {}
9785
9786 void OnSetIsInert(bool is_inert) {
Eric Seckler8652dcd52018-09-20 10:42:289787 base::PostTaskWithTraits(
9788 FROM_HERE, {content::BrowserThread::UI},
tzike2aca992017-09-05 08:50:549789 base::BindOnce(&SetIsInertMessageFilter::OnSetIsInertOnUI, this,
9790 is_inert));
kenrb04323782017-06-23 01:23:329791 }
9792 void OnSetIsInertOnUI(bool is_inert) {
9793 is_inert_ = is_inert;
9794 if (!msg_received_) {
9795 msg_received_ = true;
Lukasz Anforowicz5a92f052018-08-15 20:38:479796 run_loop_.Quit();
kenrb04323782017-06-23 01:23:329797 }
9798 }
Lukasz Anforowicz5a92f052018-08-15 20:38:479799 base::RunLoop run_loop_;
kenrb04323782017-06-23 01:23:329800 bool msg_received_;
9801 bool is_inert_;
9802 DISALLOW_COPY_AND_ASSIGN(SetIsInertMessageFilter);
9803};
9804
9805// Tests that when a frame contains a modal <dialog> element, out-of-process
9806// iframe children cannot take focus, because they are inert.
9807IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, CrossProcessInertSubframe) {
9808 // This uses a(b,b) instead of a(b) to preserve the b.com process even when
9809 // the first subframe is navigated away from it.
9810 GURL main_url(embedded_test_server()->GetURL(
9811 "a.com", "/cross_site_iframe_factory.html?a(b,b)"));
9812 EXPECT_TRUE(NavigateToURL(shell(), main_url));
9813
9814 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
9815 ->GetFrameTree()
9816 ->root();
9817 ASSERT_EQ(2U, root->child_count());
9818
9819 FrameTreeNode* iframe_node = root->child_at(0);
9820
9821 EXPECT_TRUE(ExecuteScript(
9822 iframe_node,
9823 "document.head.innerHTML = '';"
9824 "document.body.innerHTML = '<input id=\"text1\"> <input id=\"text2\">';"
9825 "text1.focus();"));
9826
9827 // Add a filter to the parent frame's process to monitor for inert bit
9828 // updates. These are sent through the proxy for b.com child frame.
9829 scoped_refptr<SetIsInertMessageFilter> filter = new SetIsInertMessageFilter();
9830 root->current_frame_host()->GetProcess()->AddFilter(filter.get());
9831
9832 // Add a <dialog> to the root frame and call showModal on it.
9833 EXPECT_TRUE(ExecuteScript(root,
9834 "let dialog = "
9835 "document.body.appendChild(document.createElement('"
9836 "dialog'));"
9837 "dialog.innerHTML = 'Modal dialog <input>';"
9838 "dialog.showModal();"));
9839 filter->Wait();
9840 EXPECT_TRUE(filter->is_inert());
9841
Ken Buchanan5005b6132018-11-21 23:39:229842 // Yield the UI thread to ensure that the real SetIsInert message
kenrb04323782017-06-23 01:23:329843 // handler runs, in order to guarantee that the update arrives at the
9844 // renderer process before the script below.
Ken Buchanan5005b6132018-11-21 23:39:229845 base::RunLoop().RunUntilIdle();
kenrb04323782017-06-23 01:23:329846
9847 std::string focused_element;
9848
9849 // Attempt to change focus in the inert subframe. This should fail.
9850 // The setTimeout ensures that the inert bit can propagate before the
9851 // test JS code runs.
9852 EXPECT_TRUE(ExecuteScriptAndExtractString(
9853 iframe_node,
9854 "window.setTimeout(() => {text2.focus();"
9855 "domAutomationController.send(document.activeElement.id);}, 0)",
9856 &focused_element));
9857 EXPECT_EQ("", focused_element);
9858
9859 // Navigate the child frame to another site, so that it moves into a new
9860 // process.
9861 GURL site_url(embedded_test_server()->GetURL("c.com", "/title1.html"));
9862 NavigateFrameToURL(iframe_node, site_url);
9863
Ken Buchanan5005b6132018-11-21 23:39:229864 // NavigateFrameToURL returns when the navigation commits, at which point
9865 // frame state has to be re-sent to the new frame.
9866 // Yield the thread to prevent races with the inertness update.
9867 base::RunLoop().RunUntilIdle();
9868
kenrb04323782017-06-23 01:23:329869 EXPECT_TRUE(ExecuteScript(
9870 iframe_node,
9871 "document.head.innerHTML = '';"
9872 "document.body.innerHTML = '<input id=\"text1\"> <input id=\"text2\">';"
9873 "text1.focus();"));
9874
9875 // Verify that inertness was preserved across the navigation.
9876 EXPECT_TRUE(ExecuteScriptAndExtractString(
9877 iframe_node,
9878 "text2.focus();"
9879 "domAutomationController.send(document.activeElement.id);",
9880 &focused_element));
9881 EXPECT_EQ("", focused_element);
9882
9883 // Navigate the subframe back into its parent process to verify that the
9884 // new local frame remains inert.
9885 GURL same_site_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
9886 NavigateFrameToURL(iframe_node, same_site_url);
9887
9888 EXPECT_TRUE(ExecuteScript(
9889 iframe_node,
9890 "document.head.innerHTML = '';"
9891 "document.body.innerHTML = '<input id=\"text1\"> <input id=\"text2\">';"
9892 "text1.focus();"));
9893
9894 // Verify that inertness was preserved across the navigation.
9895 EXPECT_TRUE(ExecuteScriptAndExtractString(
9896 iframe_node,
9897 "text2.focus();"
9898 "domAutomationController.send(document.activeElement.id);",
9899 &focused_element));
9900 EXPECT_EQ("", focused_element);
9901}
9902
alexmos13fe1962017-06-28 04:25:129903// Check that main frames for the same site rendering in unrelated tabs start
9904// sharing processes that are already dedicated to that site when over process
9905// limit. See https://crbug.com/513036.
9906IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
9907 MainFrameProcessReuseWhenOverLimit) {
9908 // Set the process limit to 1.
9909 RenderProcessHost::SetMaxRendererProcessCount(1);
9910
9911 GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
9912 ASSERT_TRUE(NavigateToURL(shell(), url_a));
9913
9914 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
9915
9916 // Create an unrelated shell window.
9917 GURL url_b(embedded_test_server()->GetURL("b.com", "/title2.html"));
9918 Shell* new_shell = CreateBrowser();
9919 EXPECT_TRUE(NavigateToURL(new_shell, url_b));
9920
9921 FrameTreeNode* new_shell_root =
9922 static_cast<WebContentsImpl*>(new_shell->web_contents())
9923 ->GetFrameTree()
9924 ->root();
9925
9926 // The new window's b.com root should not reuse the a.com process.
9927 EXPECT_NE(root->current_frame_host()->GetProcess(),
9928 new_shell_root->current_frame_host()->GetProcess());
9929
9930 // Navigating the new window to a.com should reuse the first window's
9931 // process.
9932 EXPECT_TRUE(NavigateToURL(new_shell, url_a));
9933 EXPECT_EQ(root->current_frame_host()->GetProcess(),
9934 new_shell_root->current_frame_host()->GetProcess());
9935}
9936
9937// Check that subframes for the same site rendering in unrelated tabs start
9938// sharing processes that are already dedicated to that site when over process
9939// limit. See https://crbug.com/513036.
9940IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
Alex Moshchuk777a6372017-07-25 21:59:439941 SubframeProcessReuseWhenOverLimit) {
alexmos13fe1962017-06-28 04:25:129942 // Set the process limit to 1.
9943 RenderProcessHost::SetMaxRendererProcessCount(1);
9944
9945 GURL first_url(embedded_test_server()->GetURL(
9946 "a.com", "/cross_site_iframe_factory.html?a(b,b(c))"));
9947 ASSERT_TRUE(NavigateToURL(shell(), first_url));
9948
9949 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
9950
9951 // Processes for dedicated sites should never be reused.
9952 EXPECT_NE(root->current_frame_host()->GetProcess(),
9953 root->child_at(0)->current_frame_host()->GetProcess());
9954 EXPECT_NE(root->current_frame_host()->GetProcess(),
9955 root->child_at(1)->current_frame_host()->GetProcess());
9956 EXPECT_NE(root->current_frame_host()->GetProcess(),
9957 root->child_at(1)->child_at(0)->current_frame_host()->GetProcess());
9958 EXPECT_NE(root->child_at(1)->current_frame_host()->GetProcess(),
9959 root->child_at(1)->child_at(0)->current_frame_host()->GetProcess());
9960 EXPECT_EQ(root->child_at(0)->current_frame_host()->GetProcess(),
9961 root->child_at(1)->current_frame_host()->GetProcess());
9962
9963 // Create an unrelated shell window.
9964 Shell* new_shell = CreateBrowser();
9965
9966 GURL new_shell_url(embedded_test_server()->GetURL(
9967 "d.com", "/cross_site_iframe_factory.html?d(a(b))"));
9968 ASSERT_TRUE(NavigateToURL(new_shell, new_shell_url));
9969
9970 FrameTreeNode* new_shell_root =
9971 static_cast<WebContentsImpl*>(new_shell->web_contents())
9972 ->GetFrameTree()
9973 ->root();
9974
9975 // New tab's root (d.com) should go into a separate process.
9976 EXPECT_NE(root->current_frame_host()->GetProcess(),
9977 new_shell_root->current_frame_host()->GetProcess());
9978 EXPECT_NE(root->child_at(0)->current_frame_host()->GetProcess(),
9979 new_shell_root->current_frame_host()->GetProcess());
9980 EXPECT_NE(root->child_at(1)->child_at(0)->current_frame_host()->GetProcess(),
9981 new_shell_root->current_frame_host()->GetProcess());
9982
9983 // The new tab's subframe should reuse the a.com process.
9984 EXPECT_EQ(root->current_frame_host()->GetProcess(),
9985 new_shell_root->child_at(0)->current_frame_host()->GetProcess());
9986
9987 // The new tab's grandchild frame should reuse the b.com process.
9988 EXPECT_EQ(root->child_at(0)->current_frame_host()->GetProcess(),
9989 new_shell_root->child_at(0)
9990 ->child_at(0)
9991 ->current_frame_host()
9992 ->GetProcess());
9993}
9994
Alex Moshchuk27caae82017-09-11 23:11:189995// Check that when a main frame and a subframe start navigating to the same
9996// cross-site URL at the same time, the new RenderFrame for the subframe is
9997// created successfully without crashing, and the navigations complete
9998// successfully. This test checks the scenario where the main frame ends up
9999// committing before the subframe, and the test below checks the case where the
10000// subframe commits first.
10001//
10002// This used to be problematic in that the main frame navigation created an
10003// active RenderViewHost with a RenderFrame already swapped into the tree, and
10004// then while that navigation was still pending, the subframe navigation
10005// created its RenderFrame, which crashed when referencing its parent by a
10006// proxy which didn't exist.
10007//
10008// All cross-process navigations now require creating a RenderFrameProxy before
10009// creating a RenderFrame, which makes such navigations follow the provisional
10010// frame (remote-to-local navigation) paths, where such a scenario is no longer
10011// possible. See https://crbug.com/756790.
10012IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
10013 TwoCrossSitePendingNavigationsAndMainFrameWins) {
Alex Moshchuk27caae82017-09-11 23:11:1810014 GURL main_url(embedded_test_server()->GetURL(
10015 "a.com", "/cross_site_iframe_factory.html?a(a)"));
10016 EXPECT_TRUE(NavigateToURL(shell(), main_url));
10017 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
10018 FrameTreeNode* child = root->child_at(0);
10019
10020 // Navigate both frames cross-site to b.com simultaneously.
10021 GURL new_url_1(embedded_test_server()->GetURL("b.com", "/title1.html"));
10022 GURL new_url_2(embedded_test_server()->GetURL("b.com", "/title2.html"));
10023 TestNavigationManager manager1(web_contents(), new_url_1);
10024 TestNavigationManager manager2(web_contents(), new_url_2);
danakj824a7ff2019-02-07 20:34:0210025 auto script = JsReplace("location = $1; frames[0].location = $2;", new_url_1,
10026 new_url_2);
Alex Moshchuk27caae82017-09-11 23:11:1810027 EXPECT_TRUE(ExecuteScript(web_contents(), script));
10028
10029 // Wait for main frame request, but don't commit it yet. This should create
10030 // a speculative RenderFrameHost.
10031 ASSERT_TRUE(manager1.WaitForRequestStart());
10032 RenderFrameHostImpl* root_speculative_rfh =
10033 root->render_manager()->speculative_frame_host();
10034 EXPECT_TRUE(root_speculative_rfh);
10035 scoped_refptr<SiteInstanceImpl> b_site_instance(
10036 root_speculative_rfh->GetSiteInstance());
10037
10038 // There should now be a live b.com proxy for the root, since it is doing a
10039 // cross-process navigation.
10040 RenderFrameProxyHost* root_proxy =
10041 root->render_manager()->GetRenderFrameProxyHost(b_site_instance.get());
10042 EXPECT_TRUE(root_proxy);
10043 EXPECT_TRUE(root_proxy->is_render_frame_proxy_live());
10044
10045 // Wait for subframe request, but don't commit it yet.
10046 ASSERT_TRUE(manager2.WaitForRequestStart());
10047 EXPECT_TRUE(child->render_manager()->speculative_frame_host());
10048
10049 // Similarly, the subframe should also have a b.com proxy (unused in this
10050 // test), since it is also doing a cross-process navigation.
10051 RenderFrameProxyHost* child_proxy =
10052 child->render_manager()->GetRenderFrameProxyHost(b_site_instance.get());
10053 EXPECT_TRUE(child_proxy);
10054 EXPECT_TRUE(child_proxy->is_render_frame_proxy_live());
10055
10056 // Now let the main frame commit.
10057 manager1.WaitForNavigationFinished();
10058
10059 // Make sure the process is live and at the new URL.
Lukasz Anforowicz5510ed652018-06-06 16:16:1910060 EXPECT_TRUE(b_site_instance->GetProcess()->IsInitializedAndNotDead());
Alex Moshchuk27caae82017-09-11 23:11:1810061 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
10062 EXPECT_EQ(root_speculative_rfh, root->current_frame_host());
10063 EXPECT_EQ(new_url_1, root->current_frame_host()->GetLastCommittedURL());
10064
10065 // The subframe should be gone, so the second navigation should have no
10066 // effect.
10067 manager2.WaitForNavigationFinished();
10068
10069 // The new commit should have detached the old child frame.
10070 EXPECT_EQ(0U, root->child_count());
10071 int length = -1;
10072 EXPECT_TRUE(ExecuteScriptAndExtractInt(
10073 web_contents(), "domAutomationController.send(frames.length);", &length));
10074 EXPECT_EQ(0, length);
10075
10076 // The root proxy should be gone.
10077 EXPECT_FALSE(
10078 root->render_manager()->GetRenderFrameProxyHost(b_site_instance.get()));
10079}
10080
10081// Similar to TwoCrossSitePendingNavigationsAndMainFrameWins, but checks the
10082// case where the subframe navigation commits before the main frame. See
10083// https://crbug.com/756790.
10084IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
10085 TwoCrossSitePendingNavigationsAndSubframeWins) {
Alex Moshchuk27caae82017-09-11 23:11:1810086 GURL main_url(embedded_test_server()->GetURL(
10087 "a.com", "/cross_site_iframe_factory.html?a(a,a)"));
10088 EXPECT_TRUE(NavigateToURL(shell(), main_url));
10089 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
10090 FrameTreeNode* child = root->child_at(0);
10091 FrameTreeNode* child2 = root->child_at(1);
10092
10093 // Install postMessage handlers in main frame and second subframe for later
10094 // use.
10095 EXPECT_TRUE(
10096 ExecuteScript(root->current_frame_host(),
10097 "window.addEventListener('message', function(event) {\n"
10098 " event.source.postMessage(event.data + '-reply', '*');\n"
10099 "});"));
10100 EXPECT_TRUE(ExecuteScript(
10101 child2->current_frame_host(),
10102 "window.addEventListener('message', function(event) {\n"
10103 " event.source.postMessage(event.data + '-subframe-reply', '*');\n"
10104 "});"));
10105
10106 // Start a main frame navigation to b.com.
10107 GURL new_url_1(embedded_test_server()->GetURL("b.com", "/title1.html"));
10108 TestNavigationManager manager1(web_contents(), new_url_1);
10109 EXPECT_TRUE(
danakj824a7ff2019-02-07 20:34:0210110 ExecuteScript(web_contents(), JsReplace("location = $1", new_url_1)));
Alex Moshchuk27caae82017-09-11 23:11:1810111
10112 // Wait for main frame request and check the frame tree. There should be a
10113 // proxy for b.com at the root, but nowhere else at this point.
10114 ASSERT_TRUE(manager1.WaitForRequestStart());
10115 EXPECT_EQ(
10116 " Site A (B speculative) -- proxies for B\n"
10117 " |--Site A\n"
10118 " +--Site A\n"
10119 "Where A = http://a.com/\n"
10120 " B = http://b.com/",
10121 DepictFrameTree(root));
10122
10123 // Now start navigating the first subframe to b.com.
10124 GURL new_url_2(embedded_test_server()->GetURL("b.com", "/title2.html"));
10125 TestNavigationManager manager2(web_contents(), new_url_2);
danakj824a7ff2019-02-07 20:34:0210126 EXPECT_TRUE(ExecuteScript(web_contents(),
10127 JsReplace("frames[0].location = $1", new_url_2)));
Alex Moshchuk27caae82017-09-11 23:11:1810128
10129 // Wait for subframe request.
10130 ASSERT_TRUE(manager2.WaitForRequestStart());
10131 RenderFrameHostImpl* child_speculative_rfh =
10132 child->render_manager()->speculative_frame_host();
10133 EXPECT_TRUE(child_speculative_rfh);
10134 scoped_refptr<SiteInstanceImpl> b_site_instance(
10135 child_speculative_rfh->GetSiteInstance());
10136
10137 // Check that all frames have proxies for b.com at this point. The proxy for
10138 // |child2| is important to create since |child| has to use it to communicate
10139 // with |child2| if |child| commits first.
10140 EXPECT_EQ(
10141 " Site A (B speculative) -- proxies for B\n"
10142 " |--Site A (B speculative) -- proxies for B\n"
10143 " +--Site A ------- proxies for B\n"
10144 "Where A = http://a.com/\n"
10145 " B = http://b.com/",
10146 DepictFrameTree(root));
10147
10148 // Now let the subframe commit.
10149 manager2.WaitForNavigationFinished();
10150
10151 // Make sure the process is live and at the new URL.
Lukasz Anforowicz5510ed652018-06-06 16:16:1910152 EXPECT_TRUE(b_site_instance->GetProcess()->IsInitializedAndNotDead());
Alex Moshchuk27caae82017-09-11 23:11:1810153 ASSERT_EQ(2U, root->child_count());
10154 EXPECT_TRUE(child->current_frame_host()->IsRenderFrameLive());
10155 EXPECT_EQ(child_speculative_rfh, child->current_frame_host());
10156 EXPECT_EQ(new_url_2, child->current_frame_host()->GetLastCommittedURL());
10157
10158 // Recheck the proxies. Main frame should still be pending.
10159 EXPECT_EQ(
10160 " Site A (B speculative) -- proxies for B\n"
10161 " |--Site B ------- proxies for A\n"
10162 " +--Site A ------- proxies for B\n"
10163 "Where A = http://a.com/\n"
10164 " B = http://b.com/",
10165 DepictFrameTree(root));
10166
10167 // Make sure the subframe can communicate to both the root remote frame
10168 // (where the postMessage should go to the current RenderFrameHost rather
10169 // than the pending one) and its sibling remote frame in the a.com process.
10170 EXPECT_TRUE(
10171 ExecuteScript(child->current_frame_host(),
10172 "window.addEventListener('message', function(event) {\n"
10173 " domAutomationController.send(event.data);\n"
10174 "});"));
10175 std::string response;
10176 EXPECT_TRUE(ExecuteScriptAndExtractString(
10177 child, "parent.postMessage('root-ping', '*')", &response));
10178 EXPECT_EQ("root-ping-reply", response);
10179
10180 EXPECT_TRUE(ExecuteScriptAndExtractString(
10181 child, "parent.frames[1].postMessage('sibling-ping', '*')", &response));
10182 EXPECT_EQ("sibling-ping-subframe-reply", response);
10183
10184 // Cancel the pending main frame navigation, and verify that the subframe can
10185 // still communicate with the (old) main frame.
10186 root->navigator()->CancelNavigation(root, true /* inform_renderer */);
10187 EXPECT_FALSE(root->render_manager()->speculative_frame_host());
10188 response = "";
10189 EXPECT_TRUE(ExecuteScriptAndExtractString(
10190 child, "parent.postMessage('root-ping', '*')", &response));
10191 EXPECT_EQ("root-ping-reply", response);
10192}
10193
10194// Similar to TwoCrossSitePendingNavigations* tests above, but checks the case
10195// where the current window and its opener navigate simultaneously.
10196// See https://crbug.com/756790.
10197IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
10198 TwoCrossSitePendingNavigationsWithOpener) {
Alex Moshchuk27caae82017-09-11 23:11:1810199 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
10200 EXPECT_TRUE(NavigateToURL(shell(), main_url));
10201 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
10202
10203 // Install a postMessage handler in main frame for later use.
10204 EXPECT_TRUE(
10205 ExecuteScript(web_contents(),
10206 "window.addEventListener('message', function(event) {\n"
10207 " event.source.postMessage(event.data + '-reply', '*');\n"
10208 "});"));
10209
10210 Shell* popup_shell =
10211 OpenPopup(shell()->web_contents(), GURL(url::kAboutBlankURL), "popup");
10212
10213 // Start a navigation to b.com in the first (opener) tab.
10214 GURL new_url_1(embedded_test_server()->GetURL("b.com", "/title1.html"));
10215 TestNavigationManager manager(web_contents(), new_url_1);
10216 EXPECT_TRUE(
danakj824a7ff2019-02-07 20:34:0210217 ExecuteScript(web_contents(), JsReplace("location = $1", new_url_1)));
Alex Moshchuk27caae82017-09-11 23:11:1810218 ASSERT_TRUE(manager.WaitForRequestStart());
10219
10220 // Before it commits, start and commit a navigation to b.com in the second
10221 // tab.
10222 GURL new_url_2(embedded_test_server()->GetURL("b.com", "/title2.html"));
Alex Moshchuk7e26eca2018-03-03 01:34:2910223 EXPECT_TRUE(NavigateToURLFromRenderer(popup_shell, new_url_2));
Alex Moshchuk27caae82017-09-11 23:11:1810224
10225 // Check that the opener still has a speculative RenderFrameHost and a
10226 // corresponding proxy for b.com.
10227 RenderFrameHostImpl* speculative_rfh =
10228 root->render_manager()->speculative_frame_host();
10229 EXPECT_TRUE(speculative_rfh);
10230 scoped_refptr<SiteInstanceImpl> b_site_instance(
10231 speculative_rfh->GetSiteInstance());
10232 RenderFrameProxyHost* proxy =
10233 root->render_manager()->GetRenderFrameProxyHost(b_site_instance.get());
10234 EXPECT_TRUE(proxy);
10235 EXPECT_TRUE(proxy->is_render_frame_proxy_live());
10236
10237 // Make sure the second tab can communicate to its (old) opener remote frame.
10238 // The postMessage should go to the current RenderFrameHost rather than the
10239 // pending one in the first tab's main frame.
10240 EXPECT_TRUE(
10241 ExecuteScript(popup_shell->web_contents(),
10242 "window.addEventListener('message', function(event) {\n"
10243 " domAutomationController.send(event.data);\n"
10244 "});"));
10245
10246 std::string response;
10247 EXPECT_TRUE(ExecuteScriptAndExtractString(
10248 popup_shell->web_contents(), "opener.postMessage('opener-ping', '*');",
10249 &response));
10250 EXPECT_EQ("opener-ping-reply", response);
10251
10252 // Cancel the pending main frame navigation, and verify that the subframe can
10253 // still communicate with the (old) main frame.
10254 root->navigator()->CancelNavigation(root, true /* inform_renderer */);
10255 EXPECT_FALSE(root->render_manager()->speculative_frame_host());
10256 response = "";
10257 EXPECT_TRUE(ExecuteScriptAndExtractString(
10258 popup_shell->web_contents(), "opener.postMessage('opener-ping', '*')",
10259 &response));
10260 EXPECT_EQ("opener-ping-reply", response);
10261}
10262
Bo Liu168c8642017-08-28 18:26:0210263#if defined(OS_ANDROID)
Kevin McNee18430052018-08-10 16:50:5610264
10265namespace {
10266
10267class MockEventHandlerAndroid : public ui::EventHandlerAndroid {
10268 public:
10269 bool OnTouchEvent(const ui::MotionEventAndroid& event) override {
10270 did_receive_event_ = true;
10271 return true;
10272 }
10273
10274 bool did_receive_event() { return did_receive_event_; }
10275
10276 private:
10277 bool did_receive_event_ = false;
10278};
10279
10280} // namespace
10281
10282IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
10283 SpeculativeRenderFrameHostDoesNotReceiveInput) {
10284 GURL url1(embedded_test_server()->GetURL("a.com", "/title1.html"));
10285 EXPECT_TRUE(NavigateToURL(shell(), url1));
10286
10287 RenderWidgetHostViewAndroid* rwhva =
10288 static_cast<RenderWidgetHostViewAndroid*>(
10289 shell()->web_contents()->GetRenderWidgetHostView());
10290 ui::ViewAndroid* rwhva_native_view = rwhva->GetNativeView();
10291 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
10292
10293 // Start a cross-site navigation.
10294 GURL url2(embedded_test_server()->GetURL("b.com", "/title2.html"));
10295 TestNavigationManager nav_manager(web_contents(), url2);
10296 shell()->LoadURL(url2);
10297
10298 // Wait for the request, but don't commit it yet. This should create a
10299 // speculative RenderFrameHost.
10300 ASSERT_TRUE(nav_manager.WaitForRequestStart());
10301 RenderFrameHostImpl* root_speculative_rfh =
10302 root->render_manager()->speculative_frame_host();
10303 EXPECT_TRUE(root_speculative_rfh);
10304 RenderWidgetHostViewAndroid* rwhv_speculative =
10305 static_cast<RenderWidgetHostViewAndroid*>(
10306 root_speculative_rfh->GetView());
10307 ui::ViewAndroid* rwhv_speculative_native_view =
10308 rwhv_speculative->GetNativeView();
10309
10310 ui::ViewAndroid* root_view = web_contents()->GetView()->GetNativeView();
10311 EXPECT_TRUE(root_view);
10312
10313 MockEventHandlerAndroid mock_handler;
10314 rwhva_native_view->set_event_handler(&mock_handler);
10315 MockEventHandlerAndroid mock_handler_speculative;
10316 rwhv_speculative_native_view->set_event_handler(&mock_handler_speculative);
10317 // Avoid having the root try to handle the following event.
10318 root_view->set_event_handler(nullptr);
10319
10320 auto size = root_view->GetSize();
10321 float x = size.width() / 2;
10322 float y = size.height() / 2;
10323 ui::MotionEventAndroid::Pointer pointer0(0, x, y, 0, 0, 0, 0, 0);
10324 ui::MotionEventAndroid::Pointer pointer1(0, 0, 0, 0, 0, 0, 0, 0);
10325 ui::MotionEventAndroid event(nullptr, nullptr, 1.f / root_view->GetDipScale(),
10326 0.f, 0.f, 0.f, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
10327 false, &pointer0, &pointer1);
10328 root_view->OnTouchEventForTesting(event);
10329
10330 EXPECT_TRUE(mock_handler.did_receive_event());
10331 EXPECT_FALSE(mock_handler_speculative.did_receive_event());
10332}
10333
Bo Liu7c6779e92017-08-16 02:02:2810334IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, TestChildProcessImportance) {
Bo Liu16dd7b8322018-05-02 21:19:5010335 web_contents()->SetMainFrameImportance(ChildProcessImportance::MODERATE);
Bo Liu7c6779e92017-08-16 02:02:2810336
10337 // Construct root page with one child in different domain.
10338 GURL main_url(embedded_test_server()->GetURL(
10339 "a.com", "/cross_site_iframe_factory.html?a(b)"));
10340 EXPECT_TRUE(NavigateToURL(shell(), main_url));
10341 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
10342 ASSERT_EQ(1u, root->child_count());
10343 FrameTreeNode* child = root->child_at(0);
10344
Bo Liu16dd7b8322018-05-02 21:19:5010345 // Importance should survive initial navigation. Note importance only affect
10346 // main frame, so sub frame process should remain NORMAL throughout.
Bo Liue0ec68b2018-03-26 18:24:0610347 EXPECT_EQ(ChildProcessImportance::MODERATE,
10348 root->current_frame_host()->GetProcess()->GetEffectiveImportance());
Bo Liu18c4a5a2017-08-29 16:00:1810349 EXPECT_EQ(
Bo Liu16dd7b8322018-05-02 21:19:5010350 ChildProcessImportance::NORMAL,
Bo Liue0ec68b2018-03-26 18:24:0610351 child->current_frame_host()->GetProcess()->GetEffectiveImportance());
Bo Liu7c6779e92017-08-16 02:02:2810352
10353 // Check setting importance.
Bo Liu16dd7b8322018-05-02 21:19:5010354 web_contents()->SetMainFrameImportance(ChildProcessImportance::NORMAL);
Bo Liue0ec68b2018-03-26 18:24:0610355 EXPECT_EQ(ChildProcessImportance::NORMAL,
10356 root->current_frame_host()->GetProcess()->GetEffectiveImportance());
Bo Liu18c4a5a2017-08-29 16:00:1810357 EXPECT_EQ(
10358 ChildProcessImportance::NORMAL,
Bo Liue0ec68b2018-03-26 18:24:0610359 child->current_frame_host()->GetProcess()->GetEffectiveImportance());
Bo Liu16dd7b8322018-05-02 21:19:5010360 web_contents()->SetMainFrameImportance(ChildProcessImportance::IMPORTANT);
Bo Liue0ec68b2018-03-26 18:24:0610361 EXPECT_EQ(ChildProcessImportance::IMPORTANT,
10362 root->current_frame_host()->GetProcess()->GetEffectiveImportance());
Bo Liu18c4a5a2017-08-29 16:00:1810363 EXPECT_EQ(
Bo Liu16dd7b8322018-05-02 21:19:5010364 ChildProcessImportance::NORMAL,
Bo Liue0ec68b2018-03-26 18:24:0610365 child->current_frame_host()->GetProcess()->GetEffectiveImportance());
Bo Liu7c6779e92017-08-16 02:02:2810366
10367 // Check importance is maintained if child navigates to new domain.
10368 int old_child_process_id = child->current_frame_host()->GetProcess()->GetID();
10369 GURL url = embedded_test_server()->GetURL("foo.com", "/title2.html");
10370 {
10371 RenderFrameDeletedObserver deleted_observer(child->current_frame_host());
10372 NavigateFrameToURL(root->child_at(0), url);
10373 deleted_observer.WaitUntilDeleted();
10374 }
10375 int new_child_process_id = child->current_frame_host()->GetProcess()->GetID();
10376 EXPECT_NE(old_child_process_id, new_child_process_id);
Bo Liu18c4a5a2017-08-29 16:00:1810377 EXPECT_EQ(
Bo Liu16dd7b8322018-05-02 21:19:5010378 ChildProcessImportance::NORMAL,
Bo Liue0ec68b2018-03-26 18:24:0610379 child->current_frame_host()->GetProcess()->GetEffectiveImportance());
Bo Liu16dd7b8322018-05-02 21:19:5010380 EXPECT_EQ(ChildProcessImportance::IMPORTANT,
10381 root->current_frame_host()->GetProcess()->GetEffectiveImportance());
Bo Liu7c6779e92017-08-16 02:02:2810382
10383 // Check importance is maintained if root navigates to new domain.
10384 int old_root_process_id = root->current_frame_host()->GetProcess()->GetID();
10385 child = nullptr; // Going to navigate root to page without any child.
10386 {
10387 RenderFrameDeletedObserver deleted_observer(root->current_frame_host());
10388 NavigateFrameToURL(root, url);
10389 deleted_observer.WaitUntilDeleted();
10390 }
10391 EXPECT_EQ(0u, root->child_count());
10392 int new_root_process_id = root->current_frame_host()->GetProcess()->GetID();
10393 EXPECT_NE(old_root_process_id, new_root_process_id);
Bo Liue0ec68b2018-03-26 18:24:0610394 EXPECT_EQ(ChildProcessImportance::IMPORTANT,
10395 root->current_frame_host()->GetProcess()->GetEffectiveImportance());
Bo Liu7c6779e92017-08-16 02:02:2810396
10397 // Check interstitial maintains importance.
10398 TestInterstitialDelegate* delegate = new TestInterstitialDelegate;
10399 WebContentsImpl* contents_impl =
10400 static_cast<WebContentsImpl*>(web_contents());
10401 GURL interstitial_url("http://interstitial");
10402 InterstitialPageImpl* interstitial = new InterstitialPageImpl(
10403 contents_impl, contents_impl, true, interstitial_url, delegate);
10404 interstitial->Show();
10405 WaitForInterstitialAttach(contents_impl);
Bo Liu18c4a5a2017-08-29 16:00:1810406 RenderProcessHost* interstitial_process =
10407 interstitial->GetMainFrame()->GetProcess();
Bo Liu7c6779e92017-08-16 02:02:2810408 EXPECT_EQ(ChildProcessImportance::IMPORTANT,
Bo Liue0ec68b2018-03-26 18:24:0610409 interstitial_process->GetEffectiveImportance());
Bo Liu7c6779e92017-08-16 02:02:2810410
Bo Liu16dd7b8322018-05-02 21:19:5010411 web_contents()->SetMainFrameImportance(ChildProcessImportance::MODERATE);
Bo Liu7c6779e92017-08-16 02:02:2810412 EXPECT_EQ(ChildProcessImportance::MODERATE,
Bo Liue0ec68b2018-03-26 18:24:0610413 interstitial_process->GetEffectiveImportance());
Bo Liu7c6779e92017-08-16 02:02:2810414}
10415
W. James MacLean12ba7972017-07-08 01:58:2610416// Tests for Android TouchSelectionEditing.
W. James MacLean12ba7972017-07-08 01:58:2610417class FrameStableObserver {
10418 public:
10419 FrameStableObserver(RenderWidgetHostViewBase* view, base::TimeDelta delta)
10420 : view_(view), delta_(delta) {}
10421 virtual ~FrameStableObserver() {}
10422
10423 void WaitUntilStable() {
10424 uint32_t current_frame_number = view_->RendererFrameNumber();
10425 uint32_t previous_frame_number;
10426
10427 do {
10428 base::RunLoop run_loop;
10429 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
10430 FROM_HERE, run_loop.QuitClosure(), delta_);
10431 run_loop.Run();
10432 previous_frame_number = current_frame_number;
10433 current_frame_number = view_->RendererFrameNumber();
10434 } while (current_frame_number != previous_frame_number);
10435 }
10436
10437 private:
10438 RenderWidgetHostViewBase* view_;
10439 base::TimeDelta delta_;
10440
10441 DISALLOW_COPY_AND_ASSIGN(FrameStableObserver);
10442};
10443
10444class TouchSelectionControllerClientTestWrapper
10445 : public ui::TouchSelectionControllerClient {
10446 public:
10447 explicit TouchSelectionControllerClientTestWrapper(
10448 ui::TouchSelectionControllerClient* client)
10449 : expected_event_(ui::SELECTION_HANDLES_SHOWN), client_(client) {}
10450
10451 ~TouchSelectionControllerClientTestWrapper() override {}
10452
10453 void InitWaitForSelectionEvent(ui::SelectionEventType expected_event) {
10454 DCHECK(!run_loop_);
10455 expected_event_ = expected_event;
10456 run_loop_.reset(new base::RunLoop());
10457 }
10458
10459 void Wait() {
10460 DCHECK(run_loop_);
10461 run_loop_->Run();
10462 run_loop_.reset();
10463 }
10464
10465 private:
10466 // TouchSelectionControllerClient:
10467 void OnSelectionEvent(ui::SelectionEventType event) override {
10468 client_->OnSelectionEvent(event);
10469 if (run_loop_ && event == expected_event_)
10470 run_loop_->Quit();
10471 }
10472
10473 bool SupportsAnimation() const override {
10474 return client_->SupportsAnimation();
10475 }
10476
10477 void SetNeedsAnimate() override { client_->SetNeedsAnimate(); }
10478
10479 void MoveCaret(const gfx::PointF& position) override {
10480 client_->MoveCaret(position);
10481 }
10482
10483 void MoveRangeSelectionExtent(const gfx::PointF& extent) override {
10484 client_->MoveRangeSelectionExtent(extent);
10485 }
10486
10487 void SelectBetweenCoordinates(const gfx::PointF& base,
10488 const gfx::PointF& extent) override {
10489 client_->SelectBetweenCoordinates(base, extent);
10490 }
10491
10492 std::unique_ptr<ui::TouchHandleDrawable> CreateDrawable() override {
10493 return client_->CreateDrawable();
10494 }
10495
W. James MacLean37517c12017-09-27 13:55:2610496 void DidScroll() override {}
10497
Shimi Zhang0f797b72018-02-03 03:01:1310498 void OnDragUpdate(const gfx::PointF& position) override {}
10499
W. James MacLean12ba7972017-07-08 01:58:2610500 ui::SelectionEventType expected_event_;
10501 std::unique_ptr<base::RunLoop> run_loop_;
10502 // Not owned.
10503 ui::TouchSelectionControllerClient* client_;
10504
10505 DISALLOW_COPY_AND_ASSIGN(TouchSelectionControllerClientTestWrapper);
10506};
10507
W. James MacLean53a6e842018-10-17 16:19:0110508class TouchSelectionControllerClientAndroidSiteIsolationTest
10509 : public SitePerProcessBrowserTest {
10510 public:
10511 TouchSelectionControllerClientAndroidSiteIsolationTest()
10512 : root_rwhv_(nullptr),
10513 child_rwhv_(nullptr),
10514 child_frame_tree_node_(nullptr),
10515 selection_controller_client_(nullptr) {}
10516
10517 void SetUpCommandLine(base::CommandLine* command_line) override {
10518 IsolateAllSitesForTesting(command_line);
10519 }
10520
10521 RenderWidgetHostViewAndroid* GetRenderWidgetHostViewAndroid() {
10522 return static_cast<RenderWidgetHostViewAndroid*>(
10523 shell()->web_contents()->GetRenderWidgetHostView());
10524 }
10525
10526 void SelectWithLongPress(gfx::Point point) {
10527 // Get main frame view for event insertion.
10528 RenderWidgetHostViewAndroid* main_view = GetRenderWidgetHostViewAndroid();
10529
10530 SendTouch(main_view, ui::MotionEvent::Action::DOWN, point);
10531 // action_timeout() is far longer than needed for a LongPress, so we use
10532 // a custom timeout here.
10533 DelayBy(base::TimeDelta::FromMilliseconds(2000));
10534 SendTouch(main_view, ui::MotionEvent::Action::UP, point);
10535 }
10536
10537 void SimpleTap(gfx::Point point) {
10538 // Get main frame view for event insertion.
10539 RenderWidgetHostViewAndroid* main_view = GetRenderWidgetHostViewAndroid();
10540
10541 SendTouch(main_view, ui::MotionEvent::Action::DOWN, point);
10542 // tiny_timeout() is way shorter than a reasonable user-created tap gesture,
10543 // so we use a custom timeout here.
10544 DelayBy(base::TimeDelta::FromMilliseconds(300));
10545 SendTouch(main_view, ui::MotionEvent::Action::UP, point);
10546 }
10547
10548 void SetupTest() {
10549 GURL test_url(embedded_test_server()->GetURL(
10550 "a.com", "/cross_site_iframe_factory.html?a(a)"));
10551 EXPECT_TRUE(NavigateToURL(shell(), test_url));
10552 frame_observer_ = std::make_unique<RenderFrameSubmissionObserver>(
10553 shell()->web_contents());
10554 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
10555 ->GetFrameTree()
10556 ->root();
10557 EXPECT_EQ(
10558 " Site A\n"
10559 " +--Site A\n"
10560 "Where A = http://a.com/",
10561 FrameTreeVisualizer().DepictFrameTree(root));
10562 TestNavigationObserver observer(shell()->web_contents());
10563 EXPECT_EQ(1u, root->child_count());
10564 child_frame_tree_node_ = root->child_at(0);
10565
10566 root_rwhv_ = static_cast<RenderWidgetHostViewAndroid*>(
10567 root->current_frame_host()->GetRenderWidgetHost()->GetView());
10568 selection_controller_client_ =
10569 new TouchSelectionControllerClientTestWrapper(
10570 root_rwhv_->GetSelectionControllerClientManagerForTesting());
10571 root_rwhv_->SetSelectionControllerClientForTesting(
10572 base::WrapUnique(selection_controller_client_));
10573
10574 // We need to load the desired subframe and then wait until it's stable,
10575 // i.e. generates no new compositor frames for some reasonable time period:
10576 // a stray frame between touch selection's pre-handling of GestureLongPress
10577 // and the expected frame containing the selected region can confuse the
10578 // TouchSelectionController, causing it to fail to show selection handles.
10579 // Note this is an issue with the TouchSelectionController in general, and
10580 // not a property of this test.
10581 GURL child_url(
10582 embedded_test_server()->GetURL("b.com", "/touch_selection.html"));
10583 NavigateFrameToURL(child_frame_tree_node_, child_url);
10584 EXPECT_EQ(
10585 " Site A ------------ proxies for B\n"
10586 " +--Site B ------- proxies for A\n"
10587 "Where A = http://a.com/\n"
10588 " B = http://b.com/",
10589 FrameTreeVisualizer().DepictFrameTree(root));
10590 // The child will change with the cross-site navigation. It shouldn't change
10591 // after this.
10592 child_frame_tree_node_ = root->child_at(0);
10593 WaitForHitTestDataOrChildSurfaceReady(
10594 child_frame_tree_node_->current_frame_host());
10595
10596 child_rwhv_ = static_cast<RenderWidgetHostViewChildFrame*>(
10597 child_frame_tree_node_->current_frame_host()
10598 ->GetRenderWidgetHost()
10599 ->GetView());
10600
10601 EXPECT_EQ(child_url, observer.last_navigation_url());
10602 EXPECT_TRUE(observer.last_navigation_succeeded());
10603 FrameStableObserver child_frame_stable_observer(
10604 child_rwhv_, TestTimeouts::tiny_timeout());
10605 child_frame_stable_observer.WaitUntilStable();
10606 }
10607
10608 // This must be called before the main-frame's RenderWidgetHostView is freed,
10609 // else we'll have a nullptr dereference on shutdown.
10610 void ShutdownTest() {
10611 ASSERT_TRUE(frame_observer_);
10612 frame_observer_.reset();
10613 }
10614
10615 gfx::PointF GetPointInChild() {
10616 gfx::PointF point_f;
10617 std::string str;
10618 EXPECT_TRUE(ExecuteScriptAndExtractString(
10619 child_frame_tree_node_->current_frame_host(), "get_point_inside_text()",
10620 &str));
10621 ConvertJSONToPoint(str, &point_f);
10622 point_f = child_rwhv()->TransformPointToRootCoordSpaceF(point_f);
10623 return point_f;
10624 }
10625
10626 void VerifyHandlePosition() {
10627 // Check that selection handles are close to the selection range.
10628 // The test will timeout if this never happens.
10629 ui::TouchSelectionController* touch_selection_controller =
10630 root_rwhv()->touch_selection_controller();
10631
10632 bool handles_in_place = false;
10633 while (!handles_in_place) {
10634 gfx::PointF selection_start =
10635 touch_selection_controller->GetStartPosition();
10636 gfx::PointF selection_end = touch_selection_controller->GetEndPosition();
10637 gfx::RectF handle_start =
10638 touch_selection_controller->GetStartHandleRect();
10639 gfx::RectF handle_end = touch_selection_controller->GetEndHandleRect();
10640
10641 // Not all Android bots seem to actually show the handle, so check first.
10642 if (handle_start.IsEmpty()) {
10643 handles_in_place = true;
10644 } else {
10645 bool has_end_handle =
10646 !touch_selection_controller->GetEndHandleRect().IsEmpty();
10647 // handle_start.y() defined the top of the handle's rect, and x() is
10648 // left.
10649 bool start_near_y =
10650 std::abs(selection_start.y() - handle_start.y()) <= 3.f;
10651 bool start_in_x_range = selection_start.x() >= handle_start.x() &&
10652 selection_start.x() <= handle_start.right();
10653 bool end_near_y = std::abs(selection_end.y() - handle_end.y()) <= 3.f;
10654 bool end_in_x_range = selection_end.x() >= handle_end.x() &&
10655 selection_end.x() <= handle_end.right();
10656 handles_in_place = start_near_y && start_in_x_range && end_near_y &&
10657 end_in_x_range && has_end_handle;
10658 }
10659 if (!handles_in_place)
10660 DelayBy(base::TimeDelta::FromMilliseconds(100));
10661 }
10662 }
10663
10664 RenderWidgetHostViewAndroid* root_rwhv() { return root_rwhv_; }
10665
10666 RenderWidgetHostViewChildFrame* child_rwhv() { return child_rwhv_; }
10667
10668 float PageScaleFactor() {
10669 return frame_observer_->LastRenderFrameMetadata().page_scale_factor;
10670 }
10671
10672 TouchSelectionControllerClientTestWrapper* selection_controller_client() {
10673 return selection_controller_client_;
10674 }
10675
10676 void OnSyntheticGestureSent() {
10677 gesture_run_loop_ = std::make_unique<base::RunLoop>();
10678 gesture_run_loop_->Run();
10679 }
10680
10681 void OnSyntheticGestureCompleted(SyntheticGesture::Result result) {
10682 EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
10683 gesture_run_loop_->Quit();
10684 }
10685
10686 protected:
10687 void DelayBy(base::TimeDelta delta) {
10688 base::RunLoop run_loop;
10689 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
10690 FROM_HERE, run_loop.QuitClosure(), delta);
10691 run_loop.Run();
10692 }
10693
10694 private:
10695 void SendTouch(RenderWidgetHostViewAndroid* view,
10696 ui::MotionEvent::Action action,
10697 gfx::Point point) {
10698 DCHECK(action >= ui::MotionEvent::Action::DOWN &&
10699 action < ui::MotionEvent::Action::CANCEL);
10700
10701 ui::MotionEventAndroid::Pointer p(0, point.x(), point.y(), 10, 0, 0, 0, 0);
10702 JNIEnv* env = base::android::AttachCurrentThread();
10703 auto time_ms = (ui::EventTimeForNow() - base::TimeTicks()).InMilliseconds();
10704 ui::MotionEventAndroid touch(
10705 env, nullptr, 1.f, 0, 0, 0, time_ms,
10706 ui::MotionEventAndroid::GetAndroidAction(action), 1, 0, 0, 0, 0, 0, 0,
10707 0, false, &p, nullptr);
10708 view->OnTouchEvent(touch);
10709 }
10710
10711 RenderWidgetHostViewAndroid* root_rwhv_;
10712 RenderWidgetHostViewChildFrame* child_rwhv_;
10713 FrameTreeNode* child_frame_tree_node_;
10714 std::unique_ptr<RenderFrameSubmissionObserver> frame_observer_;
10715 TouchSelectionControllerClientTestWrapper* selection_controller_client_;
10716
10717 std::unique_ptr<base::RunLoop> gesture_run_loop_;
10718};
10719
W. James MacLean12ba7972017-07-08 01:58:2610720IN_PROC_BROWSER_TEST_F(TouchSelectionControllerClientAndroidSiteIsolationTest,
10721 BasicSelectionIsolatedIframe) {
W. James MacLean53a6e842018-10-17 16:19:0110722 // Load test URL with cross-process child.
10723 SetupTest();
W. James MacLean12ba7972017-07-08 01:58:2610724
10725 EXPECT_EQ(ui::TouchSelectionController::INACTIVE,
W. James MacLean53a6e842018-10-17 16:19:0110726 root_rwhv()->touch_selection_controller()->active_status());
W. James MacLean12ba7972017-07-08 01:58:2610727 // Find the location of some text to select.
W. James MacLean53a6e842018-10-17 16:19:0110728 gfx::PointF point_f = GetPointInChild();
W. James MacLean12ba7972017-07-08 01:58:2610729
10730 // Initiate selection with a sequence of events that go through the targeting
10731 // system.
W. James MacLean53a6e842018-10-17 16:19:0110732 selection_controller_client()->InitWaitForSelectionEvent(
W. James MacLean12ba7972017-07-08 01:58:2610733 ui::SELECTION_HANDLES_SHOWN);
10734
10735 SelectWithLongPress(gfx::Point(point_f.x(), point_f.y()));
10736
W. James MacLean53a6e842018-10-17 16:19:0110737 selection_controller_client()->Wait();
W. James MacLean12ba7972017-07-08 01:58:2610738
10739 // Check that selection is active and the quick menu is showing.
10740 EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE,
W. James MacLean53a6e842018-10-17 16:19:0110741 root_rwhv()->touch_selection_controller()->active_status());
W. James MacLean12ba7972017-07-08 01:58:2610742
W. James MacLean53a6e842018-10-17 16:19:0110743 // Make sure handles are correctly positioned.
10744 VerifyHandlePosition();
W. James MacLeanfdef91872018-09-29 19:17:0310745
W. James MacLean12ba7972017-07-08 01:58:2610746 // Tap inside/outside the iframe and make sure the selection handles go away.
W. James MacLean53a6e842018-10-17 16:19:0110747 selection_controller_client()->InitWaitForSelectionEvent(
W. James MacLean12ba7972017-07-08 01:58:2610748 ui::SELECTION_HANDLES_CLEARED);
10749 // Since Android tests may run with page_scale_factor < 1, use an offset a
10750 // bigger than +/-1 for doing the inside/outside taps to cancel the selection
10751 // handles.
W. James MacLeana7f4ab12017-12-13 00:37:5510752 gfx::PointF point_inside_iframe =
W. James MacLean53a6e842018-10-17 16:19:0110753 child_rwhv()->TransformPointToRootCoordSpaceF(gfx::PointF(+5.f, +5.f));
W. James MacLean12ba7972017-07-08 01:58:2610754 SimpleTap(gfx::Point(point_inside_iframe.x(), point_inside_iframe.y()));
W. James MacLean53a6e842018-10-17 16:19:0110755 selection_controller_client()->Wait();
W. James MacLean12ba7972017-07-08 01:58:2610756
10757 EXPECT_EQ(ui::TouchSelectionController::INACTIVE,
W. James MacLean53a6e842018-10-17 16:19:0110758 root_rwhv()->touch_selection_controller()->active_status());
W. James MacLean12ba7972017-07-08 01:58:2610759
10760 // Let's wait for the previous events to clear the round-trip to the renders
10761 // and back.
10762 DelayBy(base::TimeDelta::FromMilliseconds(2000));
10763
10764 // Initiate selection with a sequence of events that go through the targeting
10765 // system. Repeat of above but this time we'l cancel the selection by
10766 // tapping outside of the OOPIF.
W. James MacLean53a6e842018-10-17 16:19:0110767 selection_controller_client()->InitWaitForSelectionEvent(
W. James MacLean12ba7972017-07-08 01:58:2610768 ui::SELECTION_HANDLES_SHOWN);
10769
10770 SelectWithLongPress(gfx::Point(point_f.x(), point_f.y()));
10771
W. James MacLean53a6e842018-10-17 16:19:0110772 selection_controller_client()->Wait();
W. James MacLean12ba7972017-07-08 01:58:2610773
10774 // Check that selection is active and the quick menu is showing.
10775 EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE,
W. James MacLean53a6e842018-10-17 16:19:0110776 root_rwhv()->touch_selection_controller()->active_status());
W. James MacLean12ba7972017-07-08 01:58:2610777
10778 // Tap inside/outside the iframe and make sure the selection handles go away.
W. James MacLean53a6e842018-10-17 16:19:0110779 selection_controller_client()->InitWaitForSelectionEvent(
W. James MacLean12ba7972017-07-08 01:58:2610780 ui::SELECTION_HANDLES_CLEARED);
10781 // Since Android tests may run with page_scale_factor < 1, use an offset a
10782 // bigger than +/-1 for doing the inside/outside taps to cancel the selection
10783 // handles.
W. James MacLeana7f4ab12017-12-13 00:37:5510784 gfx::PointF point_outside_iframe =
W. James MacLean53a6e842018-10-17 16:19:0110785 child_rwhv()->TransformPointToRootCoordSpaceF(gfx::PointF(-5.f, -5.f));
W. James MacLean12ba7972017-07-08 01:58:2610786 SimpleTap(gfx::Point(point_outside_iframe.x(), point_outside_iframe.y()));
W. James MacLean53a6e842018-10-17 16:19:0110787 selection_controller_client()->Wait();
W. James MacLean12ba7972017-07-08 01:58:2610788
10789 EXPECT_EQ(ui::TouchSelectionController::INACTIVE,
W. James MacLean53a6e842018-10-17 16:19:0110790 root_rwhv()->touch_selection_controller()->active_status());
10791
10792 // Cleanup before shutting down.
10793 ShutdownTest();
W. James MacLean12ba7972017-07-08 01:58:2610794}
10795
W. James MacLean53a6e842018-10-17 16:19:0110796// This test verifies that the handles associated with an active touch selection
10797// are still correctly positioned after a pinch-zoom operation.
Donn Denmanf318fc42018-11-17 01:19:4510798#if defined(OS_ANDROID) // Flaky on Android. See https://crbug.com/906204.
10799#define MAYBE_SelectionThenPinchInOOPIF DISABLED_SelectionThenPinchInOOPIF
10800#else
10801#define MAYBE_SelectionThenPinchInOOPIF SelectionThenPinchInOOPIF
10802#endif
W. James MacLean53a6e842018-10-17 16:19:0110803IN_PROC_BROWSER_TEST_F(TouchSelectionControllerClientAndroidSiteIsolationTest,
Donn Denmanf318fc42018-11-17 01:19:4510804 MAYBE_SelectionThenPinchInOOPIF) {
W. James MacLean53a6e842018-10-17 16:19:0110805 // Load test URL with cross-process child.
10806 SetupTest();
10807
10808 EXPECT_EQ(ui::TouchSelectionController::INACTIVE,
10809 root_rwhv()->touch_selection_controller()->active_status());
10810 // Find the location of some text to select.
10811 gfx::PointF point_f = GetPointInChild();
10812
10813 // Initiate selection with a sequence of events that go through the targeting
10814 // system.
10815 selection_controller_client()->InitWaitForSelectionEvent(
10816 ui::SELECTION_HANDLES_SHOWN);
10817
10818 SelectWithLongPress(gfx::Point(point_f.x(), point_f.y()));
10819
10820 selection_controller_client()->Wait();
10821
10822 // Check that selection is active and the quick menu is showing.
10823 EXPECT_EQ(ui::TouchSelectionController::SELECTION_ACTIVE,
10824 root_rwhv()->touch_selection_controller()->active_status());
10825
10826 // Make sure handles are correctly positioned.
10827 VerifyHandlePosition();
10828
10829 // Generate a pinch sequence, then re-verify handles are in the correct
10830 // location.
10831 float page_scale_delta = 2.f;
10832 float current_page_scale = PageScaleFactor();
10833 float target_page_scale = current_page_scale * page_scale_delta;
10834
10835 SyntheticPinchGestureParams params;
10836 // We'll use the selection point for the pinch center to minimize the
10837 // likelihood of the selection getting zoomed offscreen.
10838 params.anchor = point_f;
10839 // Note: the |scale_factor| in |params| is actually treated as a delta, not
10840 // absolute, page scale.
10841 params.scale_factor = page_scale_delta;
10842 auto synthetic_pinch_gesture =
10843 std::make_unique<SyntheticTouchscreenPinchGesture>(params);
10844
10845 auto* host =
10846 static_cast<RenderWidgetHostImpl*>(root_rwhv()->GetRenderWidgetHost());
10847 InputEventAckWaiter gesture_pinch_end_waiter(
10848 host, blink::WebInputEvent::kGesturePinchEnd);
10849 host->QueueSyntheticGesture(
10850 std::move(synthetic_pinch_gesture),
10851 base::BindOnce(&TouchSelectionControllerClientAndroidSiteIsolationTest::
10852 OnSyntheticGestureCompleted,
10853 base::Unretained(this)));
10854 OnSyntheticGestureSent();
10855 // Make sure the gesture is complete from the renderer's point of view.
10856 gesture_pinch_end_waiter.Wait();
10857
10858 VerifyHandlePosition();
W. James MacLeanf50e1aea2018-10-19 15:57:1710859 // TODO(wjmaclean): Investigate why SyntheticTouchscreenPinchGesture final
10860 // scales are so imprecise.
10861 // https://crbug.com/897173
10862 const float kScaleFactorTolerance = 0.05f;
10863 EXPECT_NEAR(target_page_scale, PageScaleFactor(), kScaleFactorTolerance);
W. James MacLean53a6e842018-10-17 16:19:0110864
10865 // Cleanup before shutting down.
10866 ShutdownTest();
10867}
W. James MacLean12ba7972017-07-08 01:58:2610868#endif // defined(OS_ANDROID)
10869
W. James MacLeanad0bd35f2018-12-12 22:10:0510870class TouchEventObserver : public RenderWidgetHost::InputEventObserver {
10871 public:
10872 TouchEventObserver(std::vector<uint32_t>* outgoing_touch_event_ids,
10873 std::vector<uint32_t>* acked_touch_event_ids)
10874 : outgoing_touch_event_ids_(outgoing_touch_event_ids),
10875 acked_touch_event_ids_(acked_touch_event_ids) {}
10876
10877 void OnInputEvent(const blink::WebInputEvent& event) override {
10878 if (!blink::WebInputEvent::IsTouchEventType(event.GetType()))
10879 return;
10880
10881 const auto& touch_event = static_cast<const blink::WebTouchEvent&>(event);
10882 outgoing_touch_event_ids_->push_back(touch_event.unique_touch_event_id);
10883 }
10884
10885 void OnInputEventAck(InputEventAckSource source,
10886 InputEventAckState state,
10887 const blink::WebInputEvent& event) override {
10888 if (!blink::WebInputEvent::IsTouchEventType(event.GetType()))
10889 return;
10890
10891 const auto& touch_event = static_cast<const blink::WebTouchEvent&>(event);
10892 acked_touch_event_ids_->push_back(touch_event.unique_touch_event_id);
10893 }
10894
10895 private:
10896 std::vector<uint32_t>* outgoing_touch_event_ids_;
10897 std::vector<uint32_t>* acked_touch_event_ids_;
10898 DISALLOW_COPY_AND_ASSIGN(TouchEventObserver);
10899};
10900
10901// This test verifies the ability of the TouchEventAckQueue to send TouchEvent
10902// acks to the root view in the correct order in the event of a slow renderer.
10903// This test uses a main-frame which acks instantly (no touch handler), and a
10904// child frame which acks very slowly. A synthetic gesture tap is sent to the
10905// child first, then the main frame. In this scenario, we expect the touch
10906// events sent to the main-frame to ack first, which will be problematic if
10907// the events are acked to the GestureRecognizer out of order.
Justin DeWitt136b01a2019-03-22 23:34:5710908//
10909// This test is disabled due to flakiness on all platforms, but especially on
10910// Android. See https://crbug.com/945025.
10911IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
10912 DISABLED_TouchEventAckQueueOrdering) {
W. James MacLeanad0bd35f2018-12-12 22:10:0510913 GURL main_url(embedded_test_server()->GetURL(
10914 "a.com", "/cross_site_iframe_factory.html?a(b)"));
10915 EXPECT_TRUE(NavigateToURL(shell(), main_url));
10916
10917 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
10918 ASSERT_EQ(1u, root->child_count());
10919 FrameTreeNode* child_node = root->child_at(0);
10920
Michael Spang3105cbf2019-02-23 02:54:2610921 // Add a *slow* & non-passive touch event handler in the child. It needs to
10922 // be non-passive to ensure TouchStart doesn't get acked until after the
10923 // touch handler completes.
W. James MacLeanad0bd35f2018-12-12 22:10:0510924 EXPECT_TRUE(ExecuteScript(child_node,
10925 "touch_event_count = 0;\
10926 function touch_handler(ev) {\
10927 var start = Date.now();\
10928 while (Date.now() < start + 1000) {}\
10929 touch_event_count++;\
10930 }\
10931 document.body.addEventListener('touchstart', touch_handler,\
10932 { passive : false });\
10933 document.body.addEventListener('touchend', touch_handler,\
10934 { passive : false });"));
10935
10936 WaitForHitTestDataOrChildSurfaceReady(child_node->current_frame_host());
10937
10938 auto* root_host = static_cast<RenderWidgetHostImpl*>(
10939 root->current_frame_host()->GetRenderWidgetHost());
10940 auto* child_host = static_cast<RenderWidgetHostImpl*>(
10941 child_node->current_frame_host()->GetRenderWidgetHost());
10942
10943 // Create InputEventObserver for both, with access to common queue for
10944 // logging.
10945 std::vector<uint32_t> outgoing_touch_event_ids;
10946 std::vector<uint32_t> acked_touch_event_ids;
10947
10948 TouchEventObserver parent_touch_event_observer(&outgoing_touch_event_ids,
10949 &acked_touch_event_ids);
10950 TouchEventObserver child_touch_event_observer(&outgoing_touch_event_ids,
10951 &acked_touch_event_ids);
10952
10953 root_host->AddInputEventObserver(&parent_touch_event_observer);
10954 child_host->AddInputEventObserver(&child_touch_event_observer);
10955
10956 InputEventAckWaiter root_ack_waiter(root_host,
10957 blink::WebInputEvent::kTouchEnd);
10958 InputEventAckWaiter child_ack_waiter(child_host,
10959 blink::WebInputEvent::kTouchEnd);
10960 InputEventAckWaiter child_gesture_tap_ack_waiter(
10961 child_host, blink::WebInputEvent::kGestureTap);
10962
10963 // Create GestureTap for child.
10964 gfx::PointF child_tap_point;
10965 {
10966 // We need to know the center of the child's body, but in root view
10967 // coordinates.
10968 std::string str;
10969 EXPECT_TRUE(ExecuteScriptAndExtractString(
10970 child_node,
10971 "var rect = document.body.getBoundingClientRect();\
10972 var point = {\
10973 x: rect.left + rect.width / 2,\
10974 y: rect.top + rect.height / 2\
10975 };\
10976 window.domAutomationController.send(JSON.stringify(point));",
10977 &str));
10978 ConvertJSONToPoint(str, &child_tap_point);
10979 child_tap_point = child_node->current_frame_host()
10980 ->GetView()
10981 ->TransformPointToRootCoordSpaceF(child_tap_point);
10982 }
10983 SyntheticTapGestureParams child_tap_params;
10984 child_tap_params.position = child_tap_point;
10985 child_tap_params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
10986 child_tap_params.duration_ms = 300.f;
10987 auto child_tap_gesture =
10988 std::make_unique<SyntheticTapGesture>(child_tap_params);
10989
10990 // Create GestureTap for root.
10991 SyntheticTapGestureParams root_tap_params;
10992 root_tap_params.position = gfx::PointF(5.f, 5.f);
10993 root_tap_params.duration_ms = 300.f;
10994 root_tap_params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
10995 auto root_tap_gesture =
10996 std::make_unique<SyntheticTapGesture>(root_tap_params);
10997
10998 // Queue both GestureTaps, child first.
Michael Spang3105cbf2019-02-23 02:54:2610999 // Note that we want the SyntheticGestureController to start sending the
11000 // root tap gesture as soon as it's finished sending the events for the
11001 // child tap gesture, otherwise it would wait for the acks from the child
11002 // before starting the root gesture which defeats the purpose of this test.
11003 root_host->QueueSyntheticGestureCompleteImmediately(
11004 std::move(child_tap_gesture));
W. James MacLeanad0bd35f2018-12-12 22:10:0511005 root_host->QueueSyntheticGesture(
11006 std::move(root_tap_gesture),
11007 base::BindOnce([](SyntheticGesture::Result result) {
11008 EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
11009 }));
11010
11011 root_ack_waiter.Wait();
11012 child_ack_waiter.Wait();
11013
11014 // Verify the child did receive two touch events.
11015 int child_touch_event_count = 0;
11016 EXPECT_TRUE(ExecuteScriptAndExtractInt(
11017 child_node, "window.domAutomationController.send(touch_event_count);",
11018 &child_touch_event_count));
11019 EXPECT_EQ(2, child_touch_event_count);
11020
11021 // Verify Acks from parent arrive first.
11022 EXPECT_EQ(4u, outgoing_touch_event_ids.size());
11023 EXPECT_EQ(4u, acked_touch_event_ids.size());
11024 EXPECT_EQ(outgoing_touch_event_ids[2], acked_touch_event_ids[0]);
11025 EXPECT_EQ(outgoing_touch_event_ids[3], acked_touch_event_ids[1]);
11026
11027 // Verify no DCHECKs from GestureRecognizer, indicating acks happened in
11028 // order.
11029 child_gesture_tap_ack_waiter.Wait();
11030}
11031
W. James MacLean2a90bff2018-11-05 20:52:4711032// This test verifies that the main-frame's page scale factor propagates to
11033// the compositor layertrees in each of the child processes.
11034IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
Kevin McNeea4b47d72019-02-28 18:03:3611035 PageScaleFactorPropagatesToOOPIFs) {
W. James MacLean2a90bff2018-11-05 20:52:4711036 GURL main_url(embedded_test_server()->GetURL(
11037 "a.com", "/cross_site_iframe_factory.html?a(b(c),d)"));
11038 EXPECT_TRUE(NavigateToURL(shell(), main_url));
11039
11040 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
11041 ASSERT_EQ(2u, root->child_count());
11042 FrameTreeNode* child_b = root->child_at(0);
11043 FrameTreeNode* child_c = root->child_at(1);
11044 ASSERT_EQ(1U, child_b->child_count());
11045 FrameTreeNode* child_d = child_b->child_at(0);
11046
11047 ASSERT_TRUE(child_b);
11048 ASSERT_TRUE(child_c);
11049 ASSERT_TRUE(child_d);
11050
11051 EXPECT_EQ(
11052 " Site A ------------ proxies for B C D\n"
11053 " |--Site B ------- proxies for A C D\n"
11054 " | +--Site C -- proxies for A B D\n"
11055 " +--Site D ------- proxies for A B C\n"
11056 "Where A = http://a.com/\n"
11057 " B = http://b.com/\n"
11058 " C = http://c.com/\n"
11059 " D = http://d.com/",
11060 DepictFrameTree(root));
11061
11062 RenderFrameSubmissionObserver observer_a(root);
11063 RenderFrameSubmissionObserver observer_b(child_b);
11064 RenderFrameSubmissionObserver observer_c(child_c);
11065 RenderFrameSubmissionObserver observer_d(child_d);
11066
W. James MacLean2cd99b62019-04-08 13:54:4311067 // Monitor visual sync messages coming from the mainframe to make sure
11068 // |is_pinch_gesture_active| goes true during the pinch gesture.
W. James MacLeane2df7ef2019-04-09 11:49:5711069 auto filter_mainframe =
11070 base::MakeRefCounted<SynchronizeVisualPropertiesMessageFilter>();
W. James MacLean2cd99b62019-04-08 13:54:4311071 root->current_frame_host()->GetProcess()->AddFilter(filter_mainframe.get());
11072 // Monitor frame sync messages coming from child_b as it will need to
11073 // relay them to child_d.
W. James MacLeane2df7ef2019-04-09 11:49:5711074 auto filter_child_b =
11075 base::MakeRefCounted<SynchronizeVisualPropertiesMessageFilter>();
W. James MacLean2cd99b62019-04-08 13:54:4311076 child_b->current_frame_host()->GetProcess()->AddFilter(filter_child_b.get());
11077
W. James MacLean2a90bff2018-11-05 20:52:4711078 // We need to observe a root frame submission to pick up the initial page
11079 // scale factor.
11080 observer_a.WaitForAnyFrameSubmission();
11081
11082 const float kPageScaleDelta = 2.f;
11083 // On desktop systems we expect |current_page_scale| to be 1.f, but on
11084 // Android it will typically be less than 1.f, and may take on arbitrary
11085 // values.
11086 float current_page_scale =
11087 observer_a.LastRenderFrameMetadata().page_scale_factor;
11088 float target_page_scale = current_page_scale * kPageScaleDelta;
11089
11090 SyntheticPinchGestureParams params;
11091 auto* host = static_cast<RenderWidgetHostImpl*>(
11092 root->current_frame_host()->GetRenderWidgetHost());
11093 gfx::Rect bounds(host->GetView()->GetViewBounds().size());
11094 // The synthetic gesture code expects a location in root-view coordinates.
11095 params.anchor = gfx::PointF(bounds.CenterPoint());
11096 // In SyntheticPinchGestureParams, |scale_factor| is really a delta.
11097 params.scale_factor = kPageScaleDelta;
11098#if defined(OS_MACOSX)
11099 auto synthetic_pinch_gesture =
11100 std::make_unique<SyntheticTouchpadPinchGesture>(params);
11101#else
11102 auto synthetic_pinch_gesture =
11103 std::make_unique<SyntheticTouchscreenPinchGesture>(params);
11104#endif
11105
11106 // Send pinch gesture and verify we receive the ack.
11107 InputEventAckWaiter ack_waiter(host, blink::WebInputEvent::kGesturePinchEnd);
11108 host->QueueSyntheticGesture(
11109 std::move(synthetic_pinch_gesture),
11110 base::BindOnce([](SyntheticGesture::Result result) {
11111 EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
11112 }));
11113 ack_waiter.Wait();
11114
11115 // Make sure all the page scale values behave as expected.
W. James MacLeana32d7da32019-01-18 19:01:3811116 const float kScaleTolerance = 0.1f;
W. James MacLean2a90bff2018-11-05 20:52:4711117 observer_a.WaitForPageScaleFactor(target_page_scale, kScaleTolerance);
11118 observer_b.WaitForExternalPageScaleFactor(target_page_scale, kScaleTolerance);
11119 observer_c.WaitForExternalPageScaleFactor(target_page_scale, kScaleTolerance);
11120 observer_d.WaitForExternalPageScaleFactor(target_page_scale, kScaleTolerance);
W. James MacLean2cd99b62019-04-08 13:54:4311121
11122 // The change in |is_pinch_gesture_active| that signals the end of the pinch
11123 // gesture will occur sometime after the ack for GesturePinchEnd, so we need
11124 // to wait for it from each renderer. If it's never seen, the test fails by
11125 // timing out.
11126 filter_mainframe->WaitForPinchGestureEnd();
11127 EXPECT_TRUE(filter_mainframe->pinch_gesture_active_set());
11128 EXPECT_TRUE(filter_mainframe->pinch_gesture_active_cleared());
11129 filter_child_b->WaitForPinchGestureEnd();
11130 EXPECT_TRUE(filter_child_b->pinch_gesture_active_set());
11131 EXPECT_TRUE(filter_child_b->pinch_gesture_active_cleared());
W. James MacLean2a90bff2018-11-05 20:52:4711132}
11133
Ian Clelland5cbaaf82017-11-27 22:00:0311134// Verify that sandbox flags specified by a CSP header are properly inherited by
11135// child frames, but are removed when the frame navigates.
11136IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
11137 ActiveSandboxFlagsMaintainedAcrossNavigation) {
11138 GURL main_url(
11139 embedded_test_server()->GetURL("a.com", "/sandbox_main_frame_csp.html"));
11140 EXPECT_TRUE(NavigateToURL(shell(), main_url));
11141
11142 // It is safe to obtain the root frame tree node here, as it doesn't change.
11143 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
11144 ASSERT_EQ(1u, root->child_count());
11145
11146 EXPECT_EQ(
11147 " Site A\n"
11148 " +--Site A\n"
11149 "Where A = http://a.com/",
11150 DepictFrameTree(root));
11151
11152 FrameTreeNode* child_node = root->child_at(0);
11153
11154 EXPECT_EQ(shell()->web_contents()->GetSiteInstance(),
11155 child_node->current_frame_host()->GetSiteInstance());
11156
11157 // Main page is served with a CSP header applying sandbox flags allow-popups,
11158 // allow-pointer-lock and allow-scripts.
11159 EXPECT_EQ(blink::WebSandboxFlags::kNone,
11160 root->pending_frame_policy().sandbox_flags);
11161 EXPECT_EQ(blink::WebSandboxFlags::kNone,
11162 root->effective_frame_policy().sandbox_flags);
11163 EXPECT_EQ(blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kPopups &
11164 ~blink::WebSandboxFlags::kPointerLock &
11165 ~blink::WebSandboxFlags::kScripts &
11166 ~blink::WebSandboxFlags::kAutomaticFeatures,
11167 root->active_sandbox_flags());
11168
11169 // Child frame has iframe sandbox flags allow-popups, allow-scripts, and
11170 // allow-orientation-lock. It should receive the intersection of those with
11171 // the parent sandbox flags: allow-popups and allow-scripts.
11172 EXPECT_EQ(blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kPopups &
11173 ~blink::WebSandboxFlags::kScripts &
11174 ~blink::WebSandboxFlags::kAutomaticFeatures,
11175 root->child_at(0)->pending_frame_policy().sandbox_flags);
11176 EXPECT_EQ(blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kPopups &
11177 ~blink::WebSandboxFlags::kScripts &
11178 ~blink::WebSandboxFlags::kAutomaticFeatures,
11179 root->child_at(0)->effective_frame_policy().sandbox_flags);
11180
11181 // Document in child frame is served with a CSP header giving sandbox flags
11182 // allow-scripts, allow-popups and allow-pointer-lock. The final effective
11183 // flags should only include allow-scripts and allow-popups.
11184 EXPECT_EQ(blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kPopups &
11185 ~blink::WebSandboxFlags::kScripts &
11186 ~blink::WebSandboxFlags::kAutomaticFeatures,
11187 root->child_at(0)->active_sandbox_flags());
11188
11189 // Navigate the child frame to a new page. This should clear any CSP-applied
11190 // sandbox flags.
11191 GURL frame_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
11192 NavigateFrameToURL(root->child_at(0), frame_url);
11193
11194 EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
11195 child_node->current_frame_host()->GetSiteInstance());
11196
11197 // Navigating should reset the sandbox flags to the frame owner flags:
11198 // allow-popups and allow-scripts.
11199 EXPECT_EQ(blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kPopups &
11200 ~blink::WebSandboxFlags::kScripts &
11201 ~blink::WebSandboxFlags::kAutomaticFeatures,
11202 root->child_at(0)->active_sandbox_flags());
11203 EXPECT_EQ(blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kPopups &
11204 ~blink::WebSandboxFlags::kScripts &
11205 ~blink::WebSandboxFlags::kAutomaticFeatures,
11206 root->child_at(0)->pending_frame_policy().sandbox_flags);
11207 EXPECT_EQ(blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kPopups &
11208 ~blink::WebSandboxFlags::kScripts &
11209 ~blink::WebSandboxFlags::kAutomaticFeatures,
11210 root->child_at(0)->effective_frame_policy().sandbox_flags);
11211}
11212
11213// Test that after an RFH is swapped out, its old sandbox flags remain active.
11214IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
11215 ActiveSandboxFlagsRetainedAfterSwapOut) {
11216 GURL main_url(embedded_test_server()->GetURL(
11217 "a.com", "/sandboxed_main_frame_script.html"));
11218 EXPECT_TRUE(NavigateToURL(shell(), main_url));
11219
11220 // It is safe to obtain the root frame tree node here, as it doesn't change.
11221 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
11222 ->GetFrameTree()
11223 ->root();
11224
11225 RenderFrameHostImpl* rfh =
11226 static_cast<WebContentsImpl*>(shell()->web_contents())->GetMainFrame();
11227
11228 // Check sandbox flags on RFH before navigating away.
11229 EXPECT_EQ(blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kPopups &
11230 ~blink::WebSandboxFlags::kPointerLock &
11231 ~blink::WebSandboxFlags::kScripts &
11232 ~blink::WebSandboxFlags::kAutomaticFeatures,
11233 rfh->active_sandbox_flags());
11234
11235 // Set up a slow unload handler to force the RFH to linger in the swapped
11236 // out but not-yet-deleted state.
11237 EXPECT_TRUE(
11238 ExecuteScript(rfh, "window.onunload=function(e){ while(1); };\n"));
11239
11240 rfh->DisableSwapOutTimerForTesting();
11241 RenderFrameDeletedObserver rfh_observer(rfh);
11242
11243 // Navigate to a page with no sandbox, but wait for commit, not for the actual
11244 // load to finish.
11245 TestFrameNavigationObserver commit_observer(root);
11246 shell()->LoadURL(
11247 GURL(embedded_test_server()->GetURL("b.com", "/title1.html")));
11248 commit_observer.WaitForCommit();
11249
11250 // The previous RFH should still be pending deletion, as we wait for either
11251 // the SwapOut ACK or a timeout.
11252 ASSERT_TRUE(rfh->IsRenderFrameLive());
11253 ASSERT_FALSE(rfh->is_active());
11254 ASSERT_FALSE(rfh_observer.deleted());
11255
11256 // Check sandbox flags on old RFH -- they should be unchanged.
11257 EXPECT_EQ(blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kPopups &
11258 ~blink::WebSandboxFlags::kPointerLock &
11259 ~blink::WebSandboxFlags::kScripts &
11260 ~blink::WebSandboxFlags::kAutomaticFeatures,
11261 rfh->active_sandbox_flags());
11262
11263 // The FrameTreeNode should have flags which represent the new state.
11264 EXPECT_EQ(blink::WebSandboxFlags::kNone,
11265 root->effective_frame_policy().sandbox_flags);
11266}
11267
11268// Verify that when CSP-set sandbox flags on a page change due to navigation,
11269// the new flags are propagated to proxies in other SiteInstances.
11270//
11271// A A A A
11272// \ \ \ \ .
11273// B -> B* -> B* -> B*
11274// / \ / \ / \ .
11275// B B A B C B
11276//
11277// (B* has CSP-set sandbox flags)
11278// The test checks sandbox flags for the proxy added in step 2, by checking
11279// whether the grandchild frames navigated to in step 3 and 4 see the correct
11280// sandbox flags.
11281IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
11282 ActiveSandboxFlagsCorrectInProxies) {
11283 GURL main_url(embedded_test_server()->GetURL(
11284 "foo.com", "/cross_site_iframe_factory.html?foo(bar)"));
11285 EXPECT_TRUE(NavigateToURL(shell(), main_url));
11286
11287 // It is safe to obtain the root frame tree node here, as it doesn't change.
11288 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
11289 TestNavigationObserver observer(shell()->web_contents());
11290
11291 EXPECT_EQ(
11292 " Site A ------------ proxies for B\n"
11293 " +--Site B ------- proxies for A\n"
11294 "Where A = http://foo.com/\n"
11295 " B = http://bar.com/",
11296 DepictFrameTree(root));
11297
11298 // Navigate the child to a CSP-sandboxed page on the same origin as it is
11299 // currently. This should update the flags in its proxies as well.
11300 NavigateFrameToURL(
11301 root->child_at(0),
11302 embedded_test_server()->GetURL("bar.com", "/csp_sandboxed_frame.html"));
11303
11304 EXPECT_EQ(
11305 " Site A ------------ proxies for B\n"
11306 " +--Site B ------- proxies for A\n"
11307 " |--Site B -- proxies for A\n"
11308 " +--Site B -- proxies for A\n"
11309 "Where A = http://foo.com/\n"
11310 " B = http://bar.com/",
11311 DepictFrameTree(root));
11312
11313 // Now navigate the first grandchild to a page on the same origin as the main
11314 // frame. It should still be sandboxed, as it should get its flags from its
11315 // (remote) parent.
11316 NavigateFrameToURL(root->child_at(0)->child_at(0),
11317 embedded_test_server()->GetURL("foo.com", "/title1.html"));
11318
11319 EXPECT_EQ(
11320 " Site A ------------ proxies for B\n"
11321 " +--Site B ------- proxies for A\n"
11322 " |--Site A -- proxies for B\n"
11323 " +--Site B -- proxies for A\n"
11324 "Where A = http://foo.com/\n"
11325 " B = http://bar.com/",
11326 DepictFrameTree(root));
11327
11328 // The child of the sandboxed frame should've inherited sandbox flags, so it
11329 // should not be able to create popups.
11330 EXPECT_EQ(
11331 blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kScripts &
11332 ~blink::WebSandboxFlags::kAutomaticFeatures,
11333 root->child_at(0)->child_at(0)->effective_frame_policy().sandbox_flags);
11334 EXPECT_EQ(
11335 root->child_at(0)->child_at(0)->active_sandbox_flags(),
11336 root->child_at(0)->child_at(0)->effective_frame_policy().sandbox_flags);
11337 bool success = false;
11338 EXPECT_TRUE(ExecuteScriptAndExtractBool(
11339 root->child_at(0)->child_at(0),
11340 "window.domAutomationController.send("
11341 " !window.open('data:text/html,dataurl'));",
11342 &success));
11343 EXPECT_TRUE(success);
11344 EXPECT_EQ(1u, Shell::windows().size());
11345
11346 // Finally, navigate the grandchild frame to a new origin, creating a new site
11347 // instance. Again, the new document should be sandboxed, as it should get its
11348 // flags from its (remote) parent in B.
11349 NavigateFrameToURL(root->child_at(0)->child_at(0),
11350 embedded_test_server()->GetURL("baz.com", "/title1.html"));
11351
11352 EXPECT_EQ(
11353 " Site A ------------ proxies for B C\n"
11354 " +--Site B ------- proxies for A C\n"
11355 " |--Site C -- proxies for A B\n"
11356 " +--Site B -- proxies for A C\n"
11357 "Where A = http://foo.com/\n"
11358 " B = http://bar.com/\n"
11359 " C = http://baz.com/",
11360 DepictFrameTree(root));
11361
11362 // The child of the sandboxed frame should've inherited sandbox flags, so it
11363 // should not be able to create popups.
11364 EXPECT_EQ(
11365 blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kScripts &
11366 ~blink::WebSandboxFlags::kAutomaticFeatures,
11367 root->child_at(0)->child_at(0)->effective_frame_policy().sandbox_flags);
11368 EXPECT_EQ(
11369 root->child_at(0)->child_at(0)->active_sandbox_flags(),
11370 root->child_at(0)->child_at(0)->effective_frame_policy().sandbox_flags);
11371 success = false;
11372 EXPECT_TRUE(ExecuteScriptAndExtractBool(
11373 root->child_at(0)->child_at(0),
11374 "window.domAutomationController.send("
11375 " !window.open('data:text/html,dataurl'));",
11376 &success));
11377 EXPECT_TRUE(success);
11378 EXPECT_EQ(1u, Shell::windows().size());
11379}
11380
11381// Verify that when the sandbox iframe attribute changes on a page which also
11382// has CSP-set sandbox flags, that the correct combination of flags is set in
11383// the sandboxed page after navigation.
11384//
11385// A A A A
11386// \ \ \ \ .
11387// B -> B* -> B* -> (change sandbox attr) -> B*
11388// / \ / \ / \ .
11389// B B A B A' B
11390//
11391// (B* has CSP-set sandbox flags)
11392IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
11393 ActiveSandboxFlagsCorrectAfterUpdate) {
11394 GURL main_url(embedded_test_server()->GetURL(
11395 "foo.com", "/cross_site_iframe_factory.html?foo(bar)"));
11396 EXPECT_TRUE(NavigateToURL(shell(), main_url));
11397
11398 // It is safe to obtain the root frame tree node here, as it doesn't change.
11399 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
11400 TestNavigationObserver observer(shell()->web_contents());
11401
11402 // Navigate the child to a CSP-sandboxed page on the same origin as it is
11403 // currently. This should update the flags in its proxies as well.
11404 NavigateFrameToURL(
11405 root->child_at(0),
11406 embedded_test_server()->GetURL("bar.com", "/csp_sandboxed_frame.html"));
11407
11408 // Now navigate the first grandchild to a page on the same origin as the main
11409 // frame. It should still be sandboxed, as it should get its flags from its
11410 // (remote) parent.
11411 NavigateFrameToURL(root->child_at(0)->child_at(0),
11412 embedded_test_server()->GetURL("foo.com", "/title1.html"));
11413
11414 // The child of the sandboxed frame should've inherited sandbox flags, so it
11415 // should not be able to create popups.
11416 EXPECT_EQ(
11417 blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kScripts &
11418 ~blink::WebSandboxFlags::kAutomaticFeatures,
11419 root->child_at(0)->child_at(0)->effective_frame_policy().sandbox_flags);
11420 EXPECT_EQ(
11421 root->child_at(0)->child_at(0)->active_sandbox_flags(),
11422 root->child_at(0)->child_at(0)->effective_frame_policy().sandbox_flags);
11423 bool success = false;
11424 EXPECT_TRUE(ExecuteScriptAndExtractBool(
11425 root->child_at(0)->child_at(0),
11426 "window.domAutomationController.send("
11427 " !window.open('data:text/html,dataurl'));",
11428 &success));
11429 EXPECT_TRUE(success);
11430 EXPECT_EQ(1u, Shell::windows().size());
11431
11432 // Update the sandbox attribute in the child frame. This should be overridden
11433 // by the CSP-set sandbox on this frame: The grandchild should *not* receive
11434 // an allowance for popups after it is navigated.
11435 EXPECT_TRUE(ExecuteScript(root->child_at(0),
11436 "document.querySelector('iframe').sandbox = "
11437 " 'allow-scripts allow-popups';"));
11438 // Finally, navigate the grandchild frame to another page on the top-level
11439 // origin; the active sandbox flags should still come from the it's parent's
11440 // CSP and the frame owner attributes.
11441 NavigateFrameToURL(root->child_at(0)->child_at(0),
11442 embedded_test_server()->GetURL("foo.com", "/title2.html"));
11443 EXPECT_EQ(
11444 blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kScripts &
11445 ~blink::WebSandboxFlags::kAutomaticFeatures,
11446 root->child_at(0)->child_at(0)->effective_frame_policy().sandbox_flags);
11447 EXPECT_EQ(
11448 root->child_at(0)->child_at(0)->active_sandbox_flags(),
11449 root->child_at(0)->child_at(0)->effective_frame_policy().sandbox_flags);
11450 success = false;
11451 EXPECT_TRUE(ExecuteScriptAndExtractBool(
11452 root->child_at(0)->child_at(0),
11453 "window.domAutomationController.send("
11454 " !window.open('data:text/html,dataurl'));",
11455 &success));
11456 EXPECT_TRUE(success);
11457 EXPECT_EQ(1u, Shell::windows().size());
11458}
11459
11460// Verify that when the sandbox iframe attribute is removed from a page which
11461// also has CSP-set sandbox flags, that the flags are cleared in the browser
11462// and renderers (including proxies) after navigation to a page without CSP-set
11463// flags.
11464IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
11465 ActiveSandboxFlagsCorrectWhenCleared) {
11466 GURL main_url(
11467 embedded_test_server()->GetURL("foo.com", "/sandboxed_frames_csp.html"));
11468 EXPECT_TRUE(NavigateToURL(shell(), main_url));
11469
11470 // It is safe to obtain the root frame tree node here, as it doesn't change.
11471 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
11472 TestNavigationObserver observer(shell()->web_contents());
11473
11474 // The second child has both iframe-attribute sandbox flags and CSP-set flags.
11475 // Verify that it the flags are combined correctly in the frame tree.
11476 EXPECT_EQ(blink::WebSandboxFlags::kAll &
11477 ~blink::WebSandboxFlags::kPointerLock &
11478 ~blink::WebSandboxFlags::kOrientationLock &
11479 ~blink::WebSandboxFlags::kScripts &
11480 ~blink::WebSandboxFlags::kAutomaticFeatures,
11481 root->child_at(1)->effective_frame_policy().sandbox_flags);
11482 EXPECT_EQ(blink::WebSandboxFlags::kAll &
11483 ~blink::WebSandboxFlags::kPointerLock &
11484 ~blink::WebSandboxFlags::kScripts &
11485 ~blink::WebSandboxFlags::kAutomaticFeatures,
11486 root->child_at(1)->active_sandbox_flags());
11487
11488 NavigateFrameToURL(
11489 root->child_at(1),
11490 embedded_test_server()->GetURL("bar.com", "/sandboxed_child_frame.html"));
11491 EXPECT_EQ(blink::WebSandboxFlags::kAll &
11492 ~blink::WebSandboxFlags::kPointerLock &
11493 ~blink::WebSandboxFlags::kOrientationLock &
11494 ~blink::WebSandboxFlags::kScripts &
11495 ~blink::WebSandboxFlags::kAutomaticFeatures,
11496 root->child_at(1)->effective_frame_policy().sandbox_flags);
11497 EXPECT_EQ(blink::WebSandboxFlags::kAll &
11498 ~blink::WebSandboxFlags::kPointerLock &
11499 ~blink::WebSandboxFlags::kScripts &
11500 ~blink::WebSandboxFlags::kAutomaticFeatures,
11501 root->child_at(1)->active_sandbox_flags());
11502
11503 // Remove the sandbox attribute from the child frame.
11504 EXPECT_TRUE(ExecuteScript(root,
11505 "document.querySelectorAll('iframe')[1]"
11506 ".removeAttribute('sandbox');"));
11507 // Finally, navigate that child frame to another page on the same origin with
11508 // no CSP-set sandbox. Its sandbox flags should be completely cleared, and
11509 // should be cleared in the proxy in the main frame's renderer as well.
11510 // We can check that the flags were properly cleared by nesting another frame
11511 // under the child, and ensuring that *it* saw no sandbox flags in the
11512 // browser, or in the RemoteSecurityContext in the main frame's renderer.
11513 NavigateFrameToURL(
11514 root->child_at(1),
11515 embedded_test_server()->GetURL(
11516 "bar.com", "/cross_site_iframe_factory.html?bar(foo)"));
11517
11518 // Check the sandbox flags on the child frame in the browser process.
11519 EXPECT_EQ(blink::WebSandboxFlags::kNone,
11520 root->child_at(1)->effective_frame_policy().sandbox_flags);
11521 EXPECT_EQ(blink::WebSandboxFlags::kNone,
11522 root->child_at(1)->active_sandbox_flags());
11523
11524 // Check the sandbox flags on the grandchid frame in the browser process.
11525 EXPECT_EQ(
11526 blink::WebSandboxFlags::kNone,
11527 root->child_at(1)->child_at(0)->effective_frame_policy().sandbox_flags);
11528 EXPECT_EQ(
11529 root->child_at(1)->child_at(0)->active_sandbox_flags(),
11530 root->child_at(1)->child_at(0)->effective_frame_policy().sandbox_flags);
11531
11532 // Check the sandbox flags in the grandchild frame's renderer by attempting
11533 // to open a popup. This should succeed.
11534 bool success = false;
11535 EXPECT_TRUE(ExecuteScriptAndExtractBool(
11536 root->child_at(1)->child_at(0),
11537 "window.domAutomationController.send("
11538 " !!window.open('data:text/html,dataurl'));",
11539 &success));
11540 EXPECT_TRUE(success);
11541 EXPECT_EQ(2u, Shell::windows().size());
11542}
11543
Alex Moshchuk13587e752017-12-07 20:39:1011544// Check that a subframe that requires a dedicated process will attempt to
11545// reuse an existing process for the same site, even across BrowsingInstances.
11546// This helps consolidate processes when running under --site-per-process.
11547IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
11548 SubframeReusesExistingProcess) {
11549 GURL foo_url(
11550 embedded_test_server()->GetURL("foo.com", "/page_with_iframe.html"));
11551 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
11552 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
11553 FrameTreeNode* child = root->child_at(0);
11554
11555 // Open an unrelated tab in a separate BrowsingInstance, and navigate it to
11556 // to bar.com. This SiteInstance should have a default process reuse
11557 // policy - only subframes attempt process reuse.
11558 GURL bar_url(
11559 embedded_test_server()->GetURL("bar.com", "/page_with_iframe.html"));
11560 Shell* second_shell = CreateBrowser();
11561 EXPECT_TRUE(NavigateToURL(second_shell, bar_url));
11562 scoped_refptr<SiteInstanceImpl> second_shell_instance =
11563 static_cast<SiteInstanceImpl*>(
11564 second_shell->web_contents()->GetMainFrame()->GetSiteInstance());
11565 EXPECT_FALSE(second_shell_instance->IsRelatedSiteInstance(
11566 root->current_frame_host()->GetSiteInstance()));
11567 RenderProcessHost* bar_process = second_shell_instance->GetProcess();
11568 EXPECT_EQ(SiteInstanceImpl::ProcessReusePolicy::DEFAULT,
11569 second_shell_instance->process_reuse_policy());
11570
11571 // Now navigate the first tab's subframe to bar.com. Confirm that it reuses
11572 // |bar_process|.
11573 NavigateIframeToURL(web_contents(), "test_iframe", bar_url);
11574 EXPECT_EQ(bar_url, child->current_url());
11575 EXPECT_EQ(bar_process, child->current_frame_host()->GetProcess());
11576 EXPECT_EQ(
11577 SiteInstanceImpl::ProcessReusePolicy::REUSE_PENDING_OR_COMMITTED_SITE,
11578 child->current_frame_host()->GetSiteInstance()->process_reuse_policy());
11579
11580 EXPECT_TRUE(child->current_frame_host()->IsCrossProcessSubframe());
11581 EXPECT_EQ(
11582 bar_url.host(),
11583 child->current_frame_host()->GetSiteInstance()->GetSiteURL().host());
11584
11585 // The subframe's SiteInstance should still be different from second_shell's
11586 // SiteInstance, and they should be in separate BrowsingInstances.
11587 EXPECT_NE(second_shell_instance,
11588 child->current_frame_host()->GetSiteInstance());
11589 EXPECT_FALSE(second_shell_instance->IsRelatedSiteInstance(
11590 child->current_frame_host()->GetSiteInstance()));
11591
11592 // Navigate the second tab to a foo.com URL with a same-site subframe. This
11593 // leaves only the first tab's subframe in the bar.com process.
11594 EXPECT_TRUE(NavigateToURL(second_shell, foo_url));
11595 EXPECT_NE(bar_process,
11596 second_shell->web_contents()->GetMainFrame()->GetProcess());
11597
11598 // Navigate the second tab's subframe to bar.com, and check that this
11599 // new subframe reuses the process of the subframe in the first tab, even
11600 // though the two are in separate BrowsingInstances.
11601 NavigateIframeToURL(second_shell->web_contents(), "test_iframe", bar_url);
11602 FrameTreeNode* second_subframe =
11603 static_cast<WebContentsImpl*>(second_shell->web_contents())
11604 ->GetFrameTree()
11605 ->root()
11606 ->child_at(0);
11607 EXPECT_EQ(bar_process, second_subframe->current_frame_host()->GetProcess());
11608 EXPECT_NE(child->current_frame_host()->GetSiteInstance(),
11609 second_subframe->current_frame_host()->GetSiteInstance());
11610
11611 // Open a third, unrelated tab, navigate it to bar.com, and check that
11612 // its main frame doesn't share a process with the existing bar.com
11613 // subframes.
11614 Shell* third_shell = CreateBrowser();
11615 EXPECT_TRUE(NavigateToURL(third_shell, bar_url));
11616 SiteInstanceImpl* third_shell_instance = static_cast<SiteInstanceImpl*>(
11617 third_shell->web_contents()->GetMainFrame()->GetSiteInstance());
11618 EXPECT_NE(third_shell_instance,
11619 second_subframe->current_frame_host()->GetSiteInstance());
11620 EXPECT_NE(third_shell_instance,
11621 child->current_frame_host()->GetSiteInstance());
11622 EXPECT_NE(third_shell_instance->GetProcess(), bar_process);
11623}
11624
Alex Moshchuk7e26eca2018-03-03 01:34:2911625// Check that when a subframe reuses an existing process for the same site
11626// across BrowsingInstances, a browser-initiated navigation in that subframe's
11627// tab doesn't unnecessarily share the reused process. See
11628// https://crbug.com/803367.
11629IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
11630 NoProcessSharingAfterSubframeReusesExistingProcess) {
11631 GURL foo_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
11632 EXPECT_TRUE(NavigateToURL(shell(), foo_url));
11633 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
11634 SiteInstanceImpl* foo_instance =
11635 root->current_frame_host()->GetSiteInstance();
11636
11637 // Open an unrelated tab in a separate BrowsingInstance, and navigate it to
11638 // to bar.com.
11639 GURL bar_url(
11640 embedded_test_server()->GetURL("bar.com", "/page_with_iframe.html"));
11641 Shell* second_shell = CreateBrowser();
11642 EXPECT_TRUE(NavigateToURL(second_shell, bar_url));
11643 FrameTreeNode* second_root =
11644 static_cast<WebContentsImpl*>(second_shell->web_contents())
11645 ->GetFrameTree()
11646 ->root();
11647 FrameTreeNode* second_child = second_root->child_at(0);
11648 scoped_refptr<SiteInstanceImpl> bar_instance =
11649 second_root->current_frame_host()->GetSiteInstance();
11650 EXPECT_FALSE(bar_instance->IsRelatedSiteInstance(foo_instance));
11651
11652 // Navigate the second tab's subframe to foo.com. Confirm that it reuses
11653 // first tab's process.
11654 NavigateIframeToURL(second_shell->web_contents(), "test_iframe", foo_url);
11655 EXPECT_EQ(foo_url, second_child->current_url());
11656 scoped_refptr<SiteInstanceImpl> second_child_foo_instance =
11657 second_child->current_frame_host()->GetSiteInstance();
11658 EXPECT_EQ(
11659 SiteInstanceImpl::ProcessReusePolicy::REUSE_PENDING_OR_COMMITTED_SITE,
11660 second_child_foo_instance->process_reuse_policy());
11661 EXPECT_NE(foo_instance, second_child_foo_instance);
11662 EXPECT_EQ(foo_instance->GetProcess(),
11663 second_child_foo_instance->GetProcess());
11664
11665 // Perform a browser-initiated address bar navigation in the second tab to
11666 // foo.com. This should swap BrowsingInstances and end up in a separate
11667 // process from the first tab.
11668 EXPECT_TRUE(NavigateToURL(second_shell, foo_url));
11669 SiteInstanceImpl* new_instance =
11670 second_root->current_frame_host()->GetSiteInstance();
11671 EXPECT_NE(second_child_foo_instance, new_instance);
11672 EXPECT_FALSE(second_child_foo_instance->IsRelatedSiteInstance(new_instance));
11673 EXPECT_FALSE(bar_instance->IsRelatedSiteInstance(new_instance));
11674 EXPECT_FALSE(foo_instance->IsRelatedSiteInstance(new_instance));
11675 EXPECT_NE(new_instance->GetProcess(), foo_instance->GetProcess());
11676 EXPECT_NE(new_instance->GetProcess(), bar_instance->GetProcess());
11677}
11678
Balazs Engedy1cb5c0e2018-01-30 14:34:2011679namespace {
11680
11681// Intercepts the next DidCommitProvisionalLoad message for |deferred_url| in
11682// any frame of the |web_contents|, and holds off on dispatching it until
11683// *after* the DidCommitProvisionalLoad message for the next navigation in the
11684// |web_contents| has been dispatched.
11685//
11686// Reversing the order in which the commit messages are dispatched simulates a
11687// busy renderer that takes a very long time to actually commit the navigation
11688// to |deferred_url| after receiving FrameNavigationControl::CommitNavigation;
11689// whereas there is a fast cross-site navigation taking place in the same
11690// frame which starts second but finishes first.
Arthur Hemery2a0a28be2019-03-06 17:51:3611691class CommitMessageOrderReverser : public DidCommitNavigationInterceptor {
Balazs Engedy1cb5c0e2018-01-30 14:34:2011692 public:
11693 using DidStartDeferringCommitCallback =
11694 base::OnceCallback<void(RenderFrameHost*)>;
11695
11696 CommitMessageOrderReverser(
11697 WebContents* web_contents,
11698 const GURL& deferred_url,
11699 DidStartDeferringCommitCallback deferred_url_triggered_action)
Arthur Hemery2a0a28be2019-03-06 17:51:3611700 : DidCommitNavigationInterceptor(web_contents),
Balazs Engedy1cb5c0e2018-01-30 14:34:2011701 deferred_url_(deferred_url),
11702 deferred_url_triggered_action_(
11703 std::move(deferred_url_triggered_action)) {}
11704 ~CommitMessageOrderReverser() override = default;
11705
11706 void WaitForBothCommits() { outer_run_loop.Run(); }
11707
11708 protected:
Arthur Hemery2a0a28be2019-03-06 17:51:3611709 bool WillProcessDidCommitNavigation(
Balazs Engedy1cb5c0e2018-01-30 14:34:2011710 RenderFrameHost* render_frame_host,
Arthur Hemery2a0a28be2019-03-06 17:51:3611711 NavigationRequest* navigation_request,
Balazs Engedy1cb5c0e2018-01-30 14:34:2011712 ::FrameHostMsg_DidCommitProvisionalLoad_Params* params,
Lukasz Anforowicz3cfc1efd2019-02-22 02:29:2911713 mojom::DidCommitProvisionalLoadInterfaceParamsPtr* interface_params)
Oksana Zhuravlova8b88e572019-01-07 21:54:0011714 override {
Balazs Engedy1cb5c0e2018-01-30 14:34:2011715 // The DidCommitProvisionalLoad message is dispatched once this method
11716 // returns, so to defer committing the the navigation to |deferred_url_|,
11717 // run a nested message loop until the subsequent other commit message is
11718 // dispatched.
11719 if (params->url == deferred_url_) {
11720 std::move(deferred_url_triggered_action_).Run(render_frame_host);
11721
Gabriel Charettead1a7832018-04-17 14:33:0211722 base::RunLoop nested_run_loop(base::RunLoop::Type::kNestableTasksAllowed);
Balazs Engedy1cb5c0e2018-01-30 14:34:2011723 nested_loop_quit_ = nested_run_loop.QuitClosure();
11724 nested_run_loop.Run();
11725 outer_run_loop.Quit();
11726 } else if (nested_loop_quit_) {
11727 std::move(nested_loop_quit_).Run();
11728 }
clamyd3bfdb02018-07-12 13:52:1811729 return true;
Balazs Engedy1cb5c0e2018-01-30 14:34:2011730 }
11731
11732 private:
11733 base::RunLoop outer_run_loop;
11734 base::OnceClosure nested_loop_quit_;
11735
11736 const GURL deferred_url_;
11737 DidStartDeferringCommitCallback deferred_url_triggered_action_;
11738
11739 DISALLOW_COPY_AND_ASSIGN(CommitMessageOrderReverser);
11740};
11741
11742} // namespace
11743
11744// Regression test for https://crbug.com/877239, simulating the following
11745// scenario:
11746//
11747// 1) http://a.com/empty.html is loaded in a main frame.
11748// 2) Dynamically by JS, a same-site child frame is added:
11749// <iframe 'src=http://a.com/title1.html'/>.
11750// 3) The initial byte of the response for `title1.html` arrives, causing
11751// FrameMsg_CommitNavigation to be sent to the same renderer.
11752// 4) Just before processing this message, however, `main.html` navigates
11753// the iframe to http://baz.com/title2.html, which results in mojom::Frame::
11754// BeginNavigation being called on the RenderFrameHost.
11755// 5) Suppose that immediately afterwards, `main.html` enters a busy-loop.
11756// 6) The cross site navigation in the child frame starts, the first response
11757// byte arrives quickly, and thus the navigation commits quickly.
11758// 6.1) FrameTreeNode::has_committed_real_load is set to true for the child.
11759// 6.2) The same-site RenderFrame in the child FrameTreeNode is swapped out,
11760// i.e. FrameMsg_SwapOut is sent.
11761// 7) The renderer for site instance `a.com` exits from the busy loop,
11762// and starts processing messages in order:
11763// 7.1) The first being processed is FrameMsg_CommitNavigation, so a
11764// provisional load is created and immediately committed to
11765// http://a.com/title1.html.
11766// 7.2) Because at the time the same-site child RenderFrame was created,
11767// there had been no real load committed in the child frame, and because
11768// the navigation from the initial empty document to the first real
11769// document was same-origin, the global object is reused and the
11770// RemoteInterfaceProvider of the RenderFrame is not rebound.
11771// 7.3) The obsoleted load in the same-site child frame commits, calling
11772// mojom::Frame::DidCommitProvisionalLoad, however, with
11773// |interface_provider_request| being null.
11774// 8) RenderFrameHostImpl::DidCommitProvisionalLoad sees that a real load was
11775// already committed in the frame, but |interface_provider_request| is
11776// missing. However, it also sees that the frame was waiting for a swap-out
11777// ACK, so ignores the commit, and does not kill the renderer process.
11778//
11779// In the simulation of this scenario, we simulate (5) not by delaying
11780// renderer-side processing of the CommmitNavigation message, but by delaying
11781// browser-side processing of the response to it, of DidCommitProvisionalLoad.
11782IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
11783 InterfaceProviderRequestIsOptionalForRaceyFirstCommits) {
Arthur Hemeryc65cae02019-02-28 10:26:1111784 // This test does not make sense anymore for the new NavigationClient way
11785 // of doing navigations. The first iframe navigation will get instantly
11786 // destroyed by the NavigationClient disconnection handle
11787 // NavigationRequest::OnRendererAbortedNavigation.
11788 // The pipe gets closed in the renderer when the javascript starts the
11789 // second navigation.
11790 // TODO(ahemery): Remove this test when IsPerNavigationMojoInterfaceEnabled
11791 // is on by default.
11792 if (IsPerNavigationMojoInterfaceEnabled())
11793 return;
11794
Balazs Engedy1cb5c0e2018-01-30 14:34:2011795 const GURL kMainFrameUrl(
11796 embedded_test_server()->GetURL("a.com", "/empty.html"));
11797 const GURL kSubframeSameSiteUrl(
11798 embedded_test_server()->GetURL("a.com", "/title1.html"));
11799 const GURL kCrossSiteSubframeUrl(
11800 embedded_test_server()->GetURL("baz.com", "/title2.html"));
11801
danakj824a7ff2019-02-07 20:34:0211802 const auto kAddSameSiteDynamicSubframe = JsReplace(
Balazs Engedy1cb5c0e2018-01-30 14:34:2011803 "var f = document.createElement(\"iframe\");"
danakj824a7ff2019-02-07 20:34:0211804 "f.src=$1;"
Balazs Engedy1cb5c0e2018-01-30 14:34:2011805 "document.body.append(f);",
danakj824a7ff2019-02-07 20:34:0211806 kSubframeSameSiteUrl);
11807 const auto kNavigateSubframeCrossSite =
11808 JsReplace("f.src = $1;", kCrossSiteSubframeUrl);
Balazs Engedy1cb5c0e2018-01-30 14:34:2011809 const std::string kExtractSubframeUrl =
11810 "window.domAutomationController.send(f.src);";
11811
11812 ASSERT_TRUE(NavigateToURL(shell(), kMainFrameUrl));
11813
11814 const auto* main_rfh_site_instance =
11815 shell()->web_contents()->GetMainFrame()->GetSiteInstance();
11816
11817 auto did_start_deferring_commit_callback =
11818 base::BindLambdaForTesting([&](RenderFrameHost* subframe_rfh) {
11819 // Verify that the subframe starts out as same-process with its parent.
11820 ASSERT_EQ(main_rfh_site_instance, subframe_rfh->GetSiteInstance());
11821
11822 // Trigger the second commit now that we are deferring the first one.
11823 ASSERT_TRUE(ExecuteScript(shell(), kNavigateSubframeCrossSite));
11824 });
11825
11826 CommitMessageOrderReverser commit_order_reverser(
11827 shell()->web_contents(), kSubframeSameSiteUrl /* deferred_url */,
Tommy Nyquist4b749d02018-03-20 21:46:2911828 std::move(did_start_deferring_commit_callback));
Balazs Engedy1cb5c0e2018-01-30 14:34:2011829
11830 ASSERT_TRUE(ExecuteScript(shell(), kAddSameSiteDynamicSubframe));
11831 commit_order_reverser.WaitForBothCommits();
11832
11833 // Verify that:
11834 // - The cross-site navigation in the sub-frame was committed and the
11835 // same-site navigation was ignored.
11836 // - The parent frame thinks so, too.
11837 // - The renderer process corresponding to the sub-frame with the ignored
11838 // commit was not killed. This is verified implicitly: this is the same
11839 // renderer process where the parent RenderFrame lives, so if the call to
11840 // ExecuteScriptAndExtractString succeeds here, the process is still alive.
11841 std::string actual_subframe_url;
11842 ASSERT_TRUE(ExecuteScriptAndExtractString(shell(), kExtractSubframeUrl,
11843 &actual_subframe_url));
11844 EXPECT_EQ(kCrossSiteSubframeUrl.spec(), actual_subframe_url);
11845}
11846
Ken Buchananed449bb2018-02-01 21:02:0511847// Create an out-of-process iframe that causes itself to be detached during
11848// its layout/animate phase. See https://crbug.com/802932.
Ken Buchanan6004ffda2018-02-06 20:31:5811849// Disabled on Android due to flakiness, https://crbug.com/809580.
11850#if defined(OS_ANDROID)
11851#define MAYBE_OOPIFDetachDuringAnimation DISABLED_OOPIFDetachDuringAnimation
11852#else
11853#define MAYBE_OOPIFDetachDuringAnimation OOPIFDetachDuringAnimation
11854#endif
11855IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
11856 MAYBE_OOPIFDetachDuringAnimation) {
Ken Buchananed449bb2018-02-01 21:02:0511857 GURL main_url(embedded_test_server()->GetURL(
11858 "a.com", "/frame_tree/frame-detached-in-animationstart-event.html"));
11859 EXPECT_TRUE(NavigateToURL(shell(), main_url));
11860 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
11861
11862 EXPECT_EQ(
11863 " Site A ------------ proxies for B\n"
11864 " +--Site B ------- proxies for A\n"
11865 " +--Site A -- proxies for B\n"
11866 "Where A = http://a.com/\n"
11867 " B = http://b.com/",
11868 DepictFrameTree(root));
11869
11870 FrameTreeNode* nested_child = root->child_at(0)->child_at(0);
jonross09d21de2018-06-13 12:31:3611871 WaitForHitTestDataOrChildSurfaceReady(nested_child->current_frame_host());
Ken Buchananed449bb2018-02-01 21:02:0511872
11873 EXPECT_TRUE(
11874 ExecuteScript(nested_child->current_frame_host(), "startTest();"));
11875
11876 // Test passes if the main renderer doesn't crash. Ping to verify.
11877 bool success;
11878 EXPECT_TRUE(ExecuteScriptAndExtractBool(
11879 root->current_frame_host(), "window.domAutomationController.send(true);",
11880 &success));
11881 EXPECT_TRUE(success);
11882}
11883
clamyaf4bf2d92018-02-06 10:54:3611884// Tests that a cross-process iframe asked to navigate to the same URL will
11885// successfully commit the navigation.
11886IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
11887 IFrameSameDocumentNavigation) {
11888 GURL main_url(embedded_test_server()->GetURL(
11889 "foo.com", "/cross_site_iframe_factory.html?foo(bar)"));
11890 EXPECT_TRUE(NavigateToURL(shell(), main_url));
11891
11892 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
11893 FrameTreeNode* iframe = root->child_at(0);
11894
11895 EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
11896 iframe->current_frame_host()->GetSiteInstance());
11897
11898 // The iframe navigates same-document to a fragment.
11899 GURL iframe_fragment_url = GURL(iframe->current_url().spec() + "#foo");
11900 {
11901 TestNavigationObserver observer(shell()->web_contents());
11902 EXPECT_TRUE(
11903 ExecuteScript(iframe->current_frame_host(),
danakj824a7ff2019-02-07 20:34:0211904 JsReplace("location.href=$1", iframe_fragment_url)));
clamyaf4bf2d92018-02-06 10:54:3611905 observer.Wait();
11906 EXPECT_TRUE(observer.last_navigation_succeeded());
11907 EXPECT_EQ(iframe_fragment_url, iframe->current_url());
11908 }
11909
11910 // The parent frame wants the iframe do a navigation to the same URL. Because
11911 // the URL has a fragment, this will be treated as a same-document navigation,
11912 // and not as a normal load of the same URL. This should succeed.
11913 {
11914 TestNavigationObserver observer(shell()->web_contents());
danakj824a7ff2019-02-07 20:34:0211915 EXPECT_TRUE(
11916 ExecuteScript(root->current_frame_host(),
11917 JsReplace("document.getElementById('child-0').src=$1",
11918 iframe_fragment_url)));
clamyaf4bf2d92018-02-06 10:54:3611919 observer.Wait();
11920 EXPECT_TRUE(observer.last_navigation_succeeded());
11921 EXPECT_EQ(iframe_fragment_url, iframe->current_url());
11922 }
11923}
11924
Lucas Furukawa Gadanie5d27a362018-02-13 14:26:0211925// Verifies the the renderer has the size of the frame after commit.
11926// https://crbug/804046, https://crbug.com/801091
11927IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, SizeAvailableAfterCommit) {
11928 GURL main_url(embedded_test_server()->GetURL(
11929 "a.com", "/cross_site_iframe_factory.html?a(a)"));
11930 EXPECT_TRUE(NavigateToURL(shell(), main_url));
11931
11932 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
11933 FrameTreeNode* child = root->child_at(0);
11934
11935 GURL b_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
11936 TestFrameNavigationObserver commit_observer(child);
11937 NavigationController::LoadURLParams params(b_url);
11938 params.transition_type = PageTransitionFromInt(ui::PAGE_TRANSITION_LINK);
11939 params.frame_tree_node_id = child->frame_tree_node_id();
11940 child->navigator()->GetController()->LoadURLWithParams(params);
11941 commit_observer.WaitForCommit();
11942
11943 int height = -1;
11944 EXPECT_TRUE(ExecuteScriptAndExtractInt(
11945 child, "window.domAutomationController.send(window.innerHeight);",
11946 &height));
11947
11948 EXPECT_GT(height, 0);
11949}
Alex Moshchuk45625822018-03-21 19:28:2711950// Test that a late swapout ACK won't incorrectly mark RenderViewHost as
11951// inactive if it's already been reused and switched to active by another
11952// navigation. See https://crbug.com/823567.
11953IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
11954 RenderViewHostStaysActiveWithLateSwapoutACK) {
11955 EXPECT_TRUE(NavigateToURL(
11956 shell(), embedded_test_server()->GetURL("a.com", "/title1.html")));
11957
11958 // Open a popup and navigate it to a.com.
11959 Shell* popup = OpenPopup(
11960 shell(), embedded_test_server()->GetURL("a.com", "/title2.html"), "foo");
11961 WebContentsImpl* popup_contents =
11962 static_cast<WebContentsImpl*>(popup->web_contents());
11963 RenderFrameHostImpl* rfh = popup_contents->GetMainFrame();
11964 RenderViewHostImpl* rvh = rfh->render_view_host();
11965
11966 // Disable the swapout ACK and the swapout timer.
danakjcdee06c2019-02-08 18:04:0711967 auto filter = base::MakeRefCounted<DropMessageFilter>(
11968 FrameMsgStart, FrameHostMsg_SwapOut_ACK::ID);
Alex Moshchuk45625822018-03-21 19:28:2711969 rfh->GetProcess()->AddFilter(filter.get());
11970 rfh->DisableSwapOutTimerForTesting();
11971
11972 // Navigate popup to b.com. Because there's an opener, the RVH for a.com
11973 // stays around in swapped-out state.
11974 EXPECT_TRUE(NavigateToURLInSameBrowsingInstance(
11975 popup, embedded_test_server()->GetURL("b.com", "/title3.html")));
11976 EXPECT_FALSE(rvh->is_active());
11977
11978 // The old RenderFrameHost is now pending deletion.
11979 ASSERT_TRUE(rfh->IsRenderFrameLive());
11980 ASSERT_FALSE(rfh->is_active());
11981
11982 // Kill the b.com process.
11983 RenderProcessHost* b_process = popup_contents->GetMainFrame()->GetProcess();
11984 RenderProcessHostWatcher crash_observer(
11985 b_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
11986 b_process->Shutdown(0);
11987 crash_observer.Wait();
11988
11989 // Go back in the popup from b.com to a.com/title2.html. Because the current
11990 // b.com RFH is dead, the new RFH is committed right away (without waiting
11991 // for renderer to commit), so that users don't need to look at the sad tab.
11992 TestNavigationObserver back_observer(popup_contents);
11993 popup_contents->GetController().GoBack();
11994
11995 // Pretend that the original RFH in a.com now finishes running its unload
11996 // handler and sends the swapout ACK.
11997 rfh->OnSwappedOut();
11998
11999 // Wait for the new a.com navigation to finish.
12000 back_observer.Wait();
12001
12002 // The RVH for a.com should've been reused, and it should be active. Its
12003 // main frame should've been updated to the RFH from the back navigation.
12004 EXPECT_EQ(popup_contents->GetMainFrame()->render_view_host(), rvh);
12005 EXPECT_TRUE(rvh->is_active());
12006 EXPECT_EQ(rvh->GetMainFrame(), popup_contents->GetMainFrame());
12007}
Lucas Furukawa Gadanie5d27a362018-02-13 14:26:0212008
Alex Moshchukb13f850c2018-03-20 17:38:0912009// Check that when A opens a new window with B which embeds an A subframe, the
12010// subframe is visible and generates paint events. See
12011// https://crbug.com/638375.
12012IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
danakje80fd032018-12-04 17:39:2612013 SubframeVisibleAfterRenderViewBecomesSwappedOut) {
Alex Moshchukb13f850c2018-03-20 17:38:0912014 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
12015 EXPECT_TRUE(NavigateToURL(shell(), main_url));
12016
12017 GURL popup_url(embedded_test_server()->GetURL(
12018 "b.com", "/cross_site_iframe_factory.html?b(b)"));
12019 Shell* popup_shell = OpenPopup(shell()->web_contents(), popup_url, "popup");
Alex Moshchukb13f850c2018-03-20 17:38:0912020 FrameTreeNode* popup_child =
12021 static_cast<WebContentsImpl*>(popup_shell->web_contents())
12022 ->GetFrameTree()
12023 ->root()
12024 ->child_at(0);
12025
12026 // Navigate popup's subframe to a page on a.com, which will generate
12027 // continuous compositor frames by incrementing a counter on the page.
12028 NavigateFrameToURL(popup_child,
12029 embedded_test_server()->GetURL("a.com", "/counter.html"));
12030
12031 RenderWidgetHostViewChildFrame* child_view =
12032 static_cast<RenderWidgetHostViewChildFrame*>(
12033 popup_child->current_frame_host()->GetView());
12034
12035 // Make sure the child frame keeps generating compositor frames.
jonrossafe76382018-05-25 16:44:3212036 RenderFrameSubmissionObserver frame_counter(
12037 child_view->host_->render_frame_metadata_provider());
12038 while (frame_counter.render_frame_count() < 10)
12039 frame_counter.WaitForAnyFrameSubmission();
Alex Moshchukb13f850c2018-03-20 17:38:0912040}
12041
Bo Liua13e7c02018-03-28 22:24:0212042IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, FrameDepthSimple) {
12043 // Five nodes, from depth 0 to 4.
12044 GURL main_url(embedded_test_server()->GetURL(
12045 "a.com", "/cross_site_iframe_factory.html?a(b(c(d(e))))"));
12046 const size_t number_of_nodes = 5;
12047 EXPECT_TRUE(NavigateToURL(shell(), main_url));
12048
12049 FrameTreeNode* node = web_contents()->GetFrameTree()->root();
12050 for (unsigned int expected_depth = 0; expected_depth < number_of_nodes;
12051 ++expected_depth) {
12052 CheckFrameDepth(expected_depth, node);
12053
12054 if (expected_depth + 1 < number_of_nodes)
12055 node = node->child_at(0);
12056 }
12057}
12058
12059IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, FrameDepthTest) {
12060 GURL main_url(embedded_test_server()->GetURL(
12061 "a.com", "/cross_site_iframe_factory.html?a(a,b(a))"));
12062 EXPECT_TRUE(NavigateToURL(shell(), main_url));
12063
12064 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
12065 CheckFrameDepth(0u, root);
12066
12067 FrameTreeNode* child0 = root->child_at(0);
12068 {
12069 EXPECT_EQ(1u, child0->depth());
12070 RenderProcessHost::Priority priority =
12071 child0->current_frame_host()->GetRenderWidgetHost()->GetPriority();
12072 // Same site instance as root.
12073 EXPECT_EQ(0u, priority.frame_depth);
Bo Liu2f75db32018-05-03 00:56:2112074 EXPECT_EQ(0u, child0->current_frame_host()->GetProcess()->GetFrameDepth());
Bo Liua13e7c02018-03-28 22:24:0212075 }
12076
12077 FrameTreeNode* child1 = root->child_at(1);
12078 CheckFrameDepth(1u, child1);
12079 // In addition, site b's inactive Widget should not contribute priority.
12080 RenderViewHostImpl* child1_rvh =
12081 child1->current_frame_host()->render_view_host();
12082 EXPECT_FALSE(child1_rvh->is_active());
12083 EXPECT_EQ(RenderProcessHostImpl::kMaxFrameDepthForPriority,
12084 child1_rvh->GetWidget()->GetPriority().frame_depth);
12085 EXPECT_FALSE(static_cast<RenderWidgetHostOwnerDelegate*>(child1_rvh)
12086 ->ShouldContributePriorityToProcess());
12087
12088 FrameTreeNode* grand_child = root->child_at(1)->child_at(0);
12089 {
12090 EXPECT_EQ(2u, grand_child->depth());
12091 RenderProcessHost::Priority priority =
12092 grand_child->current_frame_host()->GetRenderWidgetHost()->GetPriority();
12093 EXPECT_EQ(2u, priority.frame_depth);
12094 // Same process as root
Bo Liu2f75db32018-05-03 00:56:2112095 EXPECT_EQ(0u,
12096 grand_child->current_frame_host()->GetProcess()->GetFrameDepth());
Bo Liua13e7c02018-03-28 22:24:0212097 }
12098}
12099
12100IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, VisibilityFrameDepthTest) {
12101 GURL main_url(embedded_test_server()->GetURL(
12102 "a.com", "/cross_site_iframe_factory.html?a(b)"));
12103 GURL popup_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
12104 EXPECT_TRUE(NavigateToURL(shell(), main_url));
12105 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
12106 Shell* new_shell = OpenPopup(root->child_at(0), popup_url, "");
12107 FrameTreeNode* popup_root =
12108 static_cast<WebContentsImpl*>(new_shell->web_contents())
12109 ->GetFrameTree()
12110 ->root();
12111
12112 // Subframe and popup share the same process. Both are visible, so depth
12113 // should be 0.
12114 RenderProcessHost* subframe_process =
12115 root->child_at(0)->current_frame_host()->GetProcess();
12116 RenderProcessHost* popup_process =
12117 popup_root->current_frame_host()->GetProcess();
12118 EXPECT_EQ(subframe_process, popup_process);
12119 EXPECT_EQ(2, popup_process->VisibleClientCount());
Bo Liu2f75db32018-05-03 00:56:2112120 EXPECT_EQ(0u, popup_process->GetFrameDepth());
Bo Liua13e7c02018-03-28 22:24:0212121
12122 // Hide popup. Process should have one visible client and depth should be 1,
12123 // since depth 0 popup is hidden.
12124 new_shell->web_contents()->WasHidden();
12125 EXPECT_EQ(1, popup_process->VisibleClientCount());
Bo Liu2f75db32018-05-03 00:56:2112126 EXPECT_EQ(1u, popup_process->GetFrameDepth());
Bo Liua13e7c02018-03-28 22:24:0212127
12128 // Navigate main page to same origin as popup in same BrowsingInstance,
12129 // s main page should run in the same process as the popup. The depth on the
12130 // process should be 0, from the main frame of main page.
12131 EXPECT_TRUE(NavigateToURLInSameBrowsingInstance(shell(), popup_url));
12132 // Performing a Load causes aura window to be focused (see
12133 // Shell::LoadURLForFrame) which recomputes window occlusion for all windows
12134 // (on chromeos) which unhides the popup. Hide popup again.
12135 new_shell->web_contents()->WasHidden();
12136 RenderProcessHost* new_root_process =
12137 root->current_frame_host()->GetProcess();
12138 EXPECT_EQ(new_root_process, popup_process);
12139 EXPECT_EQ(1, popup_process->VisibleClientCount());
Bo Liu2f75db32018-05-03 00:56:2112140 EXPECT_EQ(0u, popup_process->GetFrameDepth());
Bo Liua13e7c02018-03-28 22:24:0212141
12142 // Go back on main page. Should go back to same state as before navigation.
12143 TestNavigationObserver back_load_observer(shell()->web_contents());
12144 shell()->web_contents()->GetController().GoBack();
12145 back_load_observer.Wait();
12146 EXPECT_EQ(1, popup_process->VisibleClientCount());
Bo Liu2f75db32018-05-03 00:56:2112147 EXPECT_EQ(1u, popup_process->GetFrameDepth());
Bo Liua13e7c02018-03-28 22:24:0212148
12149 // Unhide popup. Should go back to same state as before hide.
12150 new_shell->web_contents()->WasShown();
12151 EXPECT_EQ(2, popup_process->VisibleClientCount());
Bo Liu2f75db32018-05-03 00:56:2112152 EXPECT_EQ(0u, popup_process->GetFrameDepth());
Bo Liua13e7c02018-03-28 22:24:0212153}
12154
Bo Liuf3a5a642018-07-30 17:28:4412155IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
12156 FrameViewportIntersectionTestSimple) {
12157 GURL main_url(embedded_test_server()->GetURL(
12158 "a.com", "/cross_site_iframe_factory.html?a(b(c),d,e(f))"));
12159 EXPECT_TRUE(NavigateToURL(shell(), main_url));
12160
12161 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
12162 scoped_refptr<UpdateViewportIntersectionMessageFilter> root_filter =
12163 new UpdateViewportIntersectionMessageFilter();
12164 root->current_frame_host()->GetProcess()->AddFilter(root_filter.get());
12165
12166 scoped_refptr<UpdateViewportIntersectionMessageFilter> child0_filter =
12167 new UpdateViewportIntersectionMessageFilter();
12168 root->child_at(0)->current_frame_host()->GetProcess()->AddFilter(
12169 child0_filter.get());
12170
12171 scoped_refptr<UpdateViewportIntersectionMessageFilter> child2_filter =
12172 new UpdateViewportIntersectionMessageFilter();
12173 root->child_at(2)->current_frame_host()->GetProcess()->AddFilter(
12174 child2_filter.get());
12175
12176 // Each immediate child is sized to 100% width and 75% height.
12177 LayoutNonRecursiveForTestingViewportIntersection(shell()->web_contents());
12178
12179 while (true) {
12180 base::RunLoop run_loop;
12181 root_filter->set_run_loop(&run_loop);
12182 child0_filter->set_run_loop(&run_loop);
12183 child2_filter->set_run_loop(&run_loop);
12184 run_loop.Run();
12185 root_filter->set_run_loop(nullptr);
12186 child0_filter->set_run_loop(nullptr);
12187 child2_filter->set_run_loop(nullptr);
12188
12189 if ( // Root should always intersect.
12190 CheckIntersectsViewport(true, root) &&
12191 // Child 0 should be entirely in viewport.
12192 CheckIntersectsViewport(true, root->child_at(0)) &&
12193 // Grand child should match parent.
12194 CheckIntersectsViewport(true, root->child_at(0)->child_at(0)) &&
12195 // Child 1 should be partially in viewport.
12196 CheckIntersectsViewport(true, root->child_at(1)) &&
12197 // Child 2 should be not be in viewport.
12198 CheckIntersectsViewport(false, root->child_at(2)) &&
12199 // Grand child should match parent.
12200 CheckIntersectsViewport(false, root->child_at(2)->child_at(0))) {
12201 break;
12202 }
12203 }
12204}
12205
12206IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
12207 FrameViewportIntersectionTestAggregate) {
12208 GURL main_url(embedded_test_server()->GetURL(
12209 "a.com", "/cross_site_iframe_factory.html?a(b,c,a,b)"));
12210 EXPECT_TRUE(NavigateToURL(shell(), main_url));
12211
12212 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
12213 scoped_refptr<UpdateViewportIntersectionMessageFilter> filter =
12214 new UpdateViewportIntersectionMessageFilter();
12215 root->current_frame_host()->GetProcess()->AddFilter(filter.get());
12216
12217 // Each immediate child is sized to 100% width and 75% height.
12218 LayoutNonRecursiveForTestingViewportIntersection(shell()->web_contents());
12219
12220 while (true) {
12221 filter->Wait();
12222
12223 bool pass = true;
12224 {
12225 // Child 2 does not intersect, but shares widget with the main frame.
12226 FrameTreeNode* node = root->child_at(2);
12227 RenderProcessHost::Priority priority =
12228 node->current_frame_host()->GetRenderWidgetHost()->GetPriority();
12229 pass = pass && priority.intersects_viewport;
12230 pass = pass &&
12231 node->current_frame_host()->GetProcess()->GetIntersectsViewport();
12232 }
12233
12234 {
12235 // Child 3 does not intersect, but shares a process with child 0.
12236 FrameTreeNode* node = root->child_at(3);
12237 RenderProcessHost::Priority priority =
12238 node->current_frame_host()->GetRenderWidgetHost()->GetPriority();
12239 pass = pass && !priority.intersects_viewport;
12240 pass = pass &&
12241 node->current_frame_host()->GetProcess()->GetIntersectsViewport();
12242 }
12243
12244 if (pass)
12245 break;
12246 }
12247}
12248
Alex Moshchuk07a131892018-04-13 23:02:1812249// Check that when a postMessage is called on a remote frame, it waits for the
12250// current script block to finish executing before forwarding the postMessage,
12251// so that if the script causes any other IPCs to be sent in the same event
12252// loop iteration, those IPCs are processed, and their side effects are
12253// observed by the target frame before it receives the forwarded postMessage.
12254// See https://crbug.com/828529.
12255IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
12256 CrossProcessPostMessageWaitsForCurrentScriptToFinish) {
12257 GURL main_url(embedded_test_server()->GetURL(
12258 "a.com", "/cross_site_iframe_factory.html?a(b)"));
12259 EXPECT_TRUE(NavigateToURL(shell(), main_url));
12260 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
12261 EXPECT_EQ(root, root->frame_tree()->GetFocusedFrame());
12262
12263 // Add an onmessage handler to the subframe to send back a bool of whether
12264 // the subframe has focus.
12265 EXPECT_TRUE(
12266 ExecuteScript(root->child_at(0),
12267 "window.addEventListener('message', function(event) {\n"
12268 " domAutomationController.send(document.hasFocus());\n"
12269 "});"));
12270
12271 // Now, send a postMessage from main frame to subframe, and then focus the
12272 // subframe in the same script. postMessage should be scheduled after the
12273 // focus() call, so the IPC to focus the subframe should arrive before the
12274 // postMessage IPC, and the subframe should already know that it's focused in
12275 // the onmessage handler.
12276 bool child_has_focus = false;
12277 EXPECT_TRUE(ExecuteScriptAndExtractBool(root,
12278 "frames[0].postMessage('','*');\n"
12279 "frames[0].focus();\n",
12280 &child_has_focus));
12281 EXPECT_TRUE(child_has_focus);
12282}
12283
12284// Ensure that if a cross-process postMessage is scheduled, and then the target
12285// frame is detached before the postMessage is forwarded, the source frame's
12286// renderer does not crash.
12287IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
12288 CrossProcessPostMessageAndDetachTarget) {
12289 GURL main_url(embedded_test_server()->GetURL(
12290 "a.com", "/cross_site_iframe_factory.html?a(b)"));
12291 EXPECT_TRUE(NavigateToURL(shell(), main_url));
12292 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
12293
12294 // Send a postMessage to the subframe and then immediately detach the
12295 // subframe.
12296 EXPECT_TRUE(ExecuteScript(root,
12297 "frames[0].postMessage('','*');\n"
12298 "document.body.removeChild(\n"
12299 " document.querySelector('iframe'));\n"));
12300
12301 // Test passes if the main renderer doesn't crash. Use setTimeout to ensure
12302 // this ping is evaluated after the (scheduled) postMessage is processed.
12303 bool success;
12304 EXPECT_TRUE(ExecuteScriptAndExtractBool(
12305 root,
12306 "setTimeout(() => { window.domAutomationController.send(true); }, 0)",
12307 &success));
12308 EXPECT_TRUE(success);
12309}
12310
Alex Moshchuk02c5156d2018-04-25 22:26:0412311// Tests that the last committed URL is preserved on an RFH even after the RFH
12312// goes into the pending deletion state.
12313IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
12314 LastCommittedURLRetainedAfterSwapOut) {
12315 // Navigate to a.com.
12316 GURL start_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
12317 EXPECT_TRUE(NavigateToURL(shell(), start_url));
12318 RenderFrameHostImpl* rfh = web_contents()->GetMainFrame();
12319 EXPECT_EQ(start_url, rfh->GetLastCommittedURL());
12320
12321 // Disable the swapout ACK and the swapout timer.
danakjcdee06c2019-02-08 18:04:0712322 auto filter = base::MakeRefCounted<DropMessageFilter>(
12323 FrameMsgStart, FrameHostMsg_SwapOut_ACK::ID);
Alex Moshchuk02c5156d2018-04-25 22:26:0412324 rfh->GetProcess()->AddFilter(filter.get());
12325 rfh->DisableSwapOutTimerForTesting();
12326
12327 // Open a popup on a.com to keep the process alive.
12328 OpenPopup(shell(), embedded_test_server()->GetURL("a.com", "/title2.html"),
12329 "foo");
12330
12331 // Navigate cross-process to b.com.
12332 EXPECT_TRUE(NavigateToURL(
12333 shell(), embedded_test_server()->GetURL("b.com", "/title3.html")));
12334
12335 // The old RFH should be pending deletion.
12336 EXPECT_FALSE(rfh->is_active());
12337 EXPECT_FALSE(rfh->IsCurrent());
12338 EXPECT_NE(rfh, web_contents()->GetMainFrame());
12339
12340 // Check that it still has a valid last committed URL.
12341 EXPECT_EQ(start_url, rfh->GetLastCommittedURL());
12342}
12343
Ken Buchanan75dd4f62018-04-26 18:37:4912344// Tests that when a large OOPIF has been scaled, the compositor raster area
12345// sent from the embedder is correct.
Guido Urdaneta35d81482018-05-07 10:52:0112346#if defined(OS_ANDROID) || defined(OS_MACOSX)
Ken Buchanan75dd4f62018-04-26 18:37:4912347// Temporarily disabled on Android because this doesn't account for browser
12348// control height or page scale factor.
Guido Urdaneta35d81482018-05-07 10:52:0112349// Flaky on Mac. https://crbug.com/840314
Ken Buchanan75dd4f62018-04-26 18:37:4912350#define MAYBE_ScaledIframeRasterSize DISABLED_ScaledframeRasterSize
12351#else
12352#define MAYBE_ScaledIframeRasterSize ScaledIframeRasterSize
12353#endif
12354IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
12355 MAYBE_ScaledIframeRasterSize) {
12356 GURL http_url(embedded_test_server()->GetURL(
12357 "a.com", "/frame_tree/page_with_scaled_large_frame.html"));
12358 EXPECT_TRUE(NavigateToURL(shell(), http_url));
12359
12360 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
12361 ->GetFrameTree()
12362 ->root();
12363
12364 scoped_refptr<UpdateViewportIntersectionMessageFilter> filter =
12365 new UpdateViewportIntersectionMessageFilter();
12366 root->current_frame_host()->GetProcess()->AddFilter(filter.get());
12367
12368 EXPECT_TRUE(ExecuteScript(
12369 root, "document.getElementsByTagName('div')[0].scrollTo(0, 5000);"));
12370
12371 gfx::Rect compositing_rect;
12372 while (compositing_rect.y() == 0) {
12373 // Ignore any messages that arrive before the compositing_rect scrolls
12374 // away from the origin.
12375 filter->Wait();
12376 compositing_rect = filter->GetCompositingRect();
12377 }
12378
12379 float scale_factor = 1.0f;
12380 if (IsUseZoomForDSFEnabled())
12381 scale_factor = GetFrameDeviceScaleFactor(shell()->web_contents());
12382
12383 // The math below replicates the calculations in
12384 // RemoteFrameView::GetCompositingRect(). That could be subject to tweaking,
12385 // which would have to be reflected in these test expectations. Also, any
12386 // changes to Blink that would affect the size of the frame rect or the
12387 // visibile viewport would need to be accounted for.
12388 // The multiplication by 5 accounts for the 0.2 scale factor in the test,
12389 // which increases the area that has to be drawn in the OOPIF.
12390 int view_height = root->current_frame_host()
12391 ->GetRenderWidgetHost()
12392 ->GetView()
12393 ->GetViewBounds()
12394 .height() *
12395 5 * scale_factor;
12396 int expected_height = view_height * 13 / 10;
12397
12398 // 185 is the height of the div in the main page after subtracting scroll
12399 // bar height.
12400 int expected_offset =
12401 (5000 * scale_factor) - (expected_height - 185 * scale_factor) / 2;
12402
12403 // Allow a small amount for rounding differences from applying page and
12404 // device scale factors at different times.
12405 EXPECT_GE(compositing_rect.height(), expected_height - 2);
12406 EXPECT_LE(compositing_rect.height(), expected_height + 2);
12407 EXPECT_GE(compositing_rect.y(), expected_offset - 2);
12408 EXPECT_LE(compositing_rect.y(), expected_offset + 2);
12409}
12410
12411// Similar to ScaledIFrameRasterSize but with nested OOPIFs to ensure
12412// propagation works correctly.
12413#if defined(OS_ANDROID)
12414// Temporarily disabled on Android because this doesn't account for browser
12415// control height or page scale factor.
12416#define MAYBE_ScaledNestedIframeRasterSize DISABLED_ScaledNestedIframeRasterSize
12417#else
12418#define MAYBE_ScaledNestedIframeRasterSize ScaledNestedIframeRasterSize
12419#endif
12420IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
12421 MAYBE_ScaledNestedIframeRasterSize) {
12422 GURL http_url(embedded_test_server()->GetURL(
12423 "a.com", "/frame_tree/page_with_scaled_large_frames_nested.html"));
12424 EXPECT_TRUE(NavigateToURL(shell(), http_url));
12425
12426 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
12427 ->GetFrameTree()
12428 ->root();
12429 FrameTreeNode* child = root->child_at(0);
12430
12431 NavigateFrameToURL(
12432 child,
12433 embedded_test_server()->GetURL(
12434 "bar.com", "/frame_tree/page_with_large_scrollable_frame.html"));
12435
12436 EXPECT_EQ(
12437 " Site A ------------ proxies for B C\n"
12438 " +--Site B ------- proxies for A C\n"
12439 " +--Site C -- proxies for A B\n"
12440 "Where A = http://a.com/\n"
12441 " B = http://bar.com/\n"
12442 " C = http://baz.com/",
12443 DepictFrameTree(root));
12444
12445 // This adds the filter to the immediate child iframe. It verifies that the
12446 // child sets the nested iframe's compositing rect correctly.
12447 scoped_refptr<UpdateViewportIntersectionMessageFilter> filter =
12448 new UpdateViewportIntersectionMessageFilter();
12449 child->current_frame_host()->GetProcess()->AddFilter(filter.get());
12450
12451 // This scrolls the div containing in the 'Site B' iframe that contains the
12452 // 'Site C' iframe, and then we verify that the 'Site C' frame receives the
12453 // correct compositor frame.
12454 EXPECT_TRUE(ExecuteScript(
12455 child, "document.getElementsByTagName('div')[0].scrollTo(0, 5000);"));
12456
12457 gfx::Rect compositing_rect;
12458 while (compositing_rect.y() == 0) {
12459 // Ignore any messages that arrive before the compositing_rect scrolls
12460 // away from the origin.
12461 filter->Wait();
12462 compositing_rect = filter->GetCompositingRect();
12463 }
12464
12465 float scale_factor = 1.0f;
12466 if (IsUseZoomForDSFEnabled())
12467 scale_factor = GetFrameDeviceScaleFactor(shell()->web_contents());
12468
12469 // See comment in ScaledIframeRasterSize for explanation of this. In this
12470 // case, the raster area of the large iframe should be restricted to
12471 // approximately the area of the smaller iframe in which it is embedded.
12472 int view_height = child->current_frame_host()
Stefan Zagerefc9fac2019-03-11 15:39:1812473 ->GetRenderWidgetHost()
12474 ->GetView()
12475 ->GetViewBounds()
12476 .height() *
12477 scale_factor;
Ken Buchanan75dd4f62018-04-26 18:37:4912478 int expected_height = view_height * 13 / 10;
12479 int expected_offset =
12480 (5000 * scale_factor) - (expected_height - 185 * scale_factor) / 2;
12481
12482 // Allow a small amount for rounding differences from applying page and
12483 // device scale factors at different times.
Stefan Zagerefc9fac2019-03-11 15:39:1812484 EXPECT_GE(compositing_rect.height(), expected_height - 3);
12485 EXPECT_LE(compositing_rect.height(), expected_height + 3);
12486 EXPECT_GE(compositing_rect.y(), expected_offset - 3);
12487 EXPECT_LE(compositing_rect.y(), expected_offset + 3);
Ken Buchanan75dd4f62018-04-26 18:37:4912488}
12489
Ken Buchananaf20a60e2018-05-06 17:20:3012490// Verify that OOPIF select element popup menu coordinates account for scroll
12491// offset in containers embedding frame.
Alexander Timin4ac83892018-07-03 09:32:5012492// TODO(crbug.com/859552): Reenable this.
12493IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
12494 DISABLED_PopupMenuInTallIframeTest) {
Ken Buchananaf20a60e2018-05-06 17:20:3012495 GURL main_url(embedded_test_server()->GetURL(
12496 "/frame_tree/page_with_tall_positioned_frame.html"));
12497 EXPECT_TRUE(NavigateToURL(shell(), main_url));
12498
12499 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
12500 FrameTreeNode* child_node = root->child_at(0);
12501 GURL site_url(embedded_test_server()->GetURL(
12502 "baz.com", "/site_isolation/page-with-select.html"));
12503 NavigateFrameToURL(child_node, site_url);
12504
12505 scoped_refptr<UpdateViewportIntersectionMessageFilter> filter =
12506 new UpdateViewportIntersectionMessageFilter();
12507 root->current_frame_host()->GetProcess()->AddFilter(filter.get());
12508
12509 // Position the select element so that it is out of the viewport, then scroll
12510 // it into view.
12511 EXPECT_TRUE(ExecuteScript(
12512 child_node, "document.querySelector('select').style.top='2000px';"));
12513 EXPECT_TRUE(ExecuteScript(root, "window.scrollTo(0, 1900);"));
12514
12515 // Wait for a viewport intersection update to be dispatched to the child, and
12516 // ensure it is processed by the browser before continuing.
12517 filter->Wait();
12518 {
12519 // This yields the UI thread in order to ensure that the new viewport
12520 // intersection is sent to the to child renderer before the mouse click
12521 // below.
12522 base::RunLoop loop;
12523 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
12524 loop.QuitClosure());
12525 loop.Run();
12526 }
12527
12528 scoped_refptr<ShowWidgetMessageFilter> show_widget_filter =
12529 new ShowWidgetMessageFilter();
12530 child_node->current_frame_host()->GetProcess()->AddFilter(
12531 show_widget_filter.get());
12532
12533 SimulateMouseClick(child_node->current_frame_host()->GetRenderWidgetHost(),
12534 55, 2005);
12535
12536 // Dismiss the popup.
12537 SimulateMouseClick(child_node->current_frame_host()->GetRenderWidgetHost(), 1,
12538 1);
12539
12540 // The test passes if this wait returns, indicating that the popup was
12541 // scrolled into view and the OOPIF renderer displayed it. Other tests verify
12542 // the correctness of popup menu coordinates.
12543 show_widget_filter->Wait();
12544}
12545
Ken Buchananeb3c53e2018-07-28 16:12:3712546// Test to verify that viewport intersection is propagated to nested OOPIFs
12547// even when a parent OOPIF has been throttled.
Yutaka Hirano673064d2018-09-10 09:16:2512548// TODO(crbug.com/869758) The test is flaky on android and Linux.
12549#if defined(OS_ANDROID) || defined(OS_LINUX)
Christos Froussios53b7c432018-08-01 14:45:4212550#define MAYBE_NestedFrameViewportIntersectionUpdated \
12551 DISABLED_NestedFrameViewportIntersectionUpdated
12552#else
12553#define MAYBE_NestedFrameViewportIntersectionUpdated \
12554 NestedFrameViewportIntersectionUpdated
12555#endif
Ken Buchananeb3c53e2018-07-28 16:12:3712556IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
Christos Froussios53b7c432018-08-01 14:45:4212557 MAYBE_NestedFrameViewportIntersectionUpdated) {
Ken Buchananeb3c53e2018-07-28 16:12:3712558 GURL main_url(embedded_test_server()->GetURL(
12559 "foo.com", "/frame_tree/scrollable_page_with_positioned_frame.html"));
12560 EXPECT_TRUE(NavigateToURL(shell(), main_url));
12561
12562 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
12563 FrameTreeNode* child_node = root->child_at(0);
12564 GURL site_url(embedded_test_server()->GetURL(
12565 "bar.com", "/frame_tree/page_with_positioned_frame.html"));
12566 NavigateFrameToURL(child_node, site_url);
12567
12568 EXPECT_EQ(
12569 " Site A ------------ proxies for B C\n"
12570 " +--Site B ------- proxies for A C\n"
12571 " +--Site C -- proxies for A B\n"
12572 "Where A = http://foo.com/\n"
12573 " B = http://bar.com/\n"
12574 " C = http://baz.com/",
12575 DepictFrameTree(root));
12576
12577 scoped_refptr<UpdateViewportIntersectionMessageFilter> filter =
12578 new UpdateViewportIntersectionMessageFilter();
12579 child_node->current_frame_host()->GetProcess()->AddFilter(filter.get());
12580
12581 // Scroll the child frame out of view, causing it to become throttled.
12582 EXPECT_TRUE(ExecuteScript(root, "window.scrollTo(0, 5000);"));
12583 while (true) {
12584 filter->Wait();
12585 if (filter->GetViewportIntersection().IsEmpty())
12586 break;
12587 }
12588
12589 // Scroll the frame back into view.
12590 EXPECT_TRUE(ExecuteScript(root, "window.scrollTo(0, 0);"));
12591 while (true) {
12592 filter->Wait();
12593 if (!filter->GetViewportIntersection().IsEmpty())
12594 break;
12595 }
12596}
12597
Alex Moshchukbf8684e2018-05-02 22:59:0212598namespace {
12599
12600// Helper class to intercept DidCommitProvisionalLoad messages and inject a
12601// call to close the current tab right before them.
Arthur Hemery2a0a28be2019-03-06 17:51:3612602class ClosePageBeforeCommitHelper : public DidCommitNavigationInterceptor {
Alex Moshchukbf8684e2018-05-02 22:59:0212603 public:
12604 explicit ClosePageBeforeCommitHelper(WebContents* web_contents)
Arthur Hemery2a0a28be2019-03-06 17:51:3612605 : DidCommitNavigationInterceptor(web_contents) {}
Alex Moshchukbf8684e2018-05-02 22:59:0212606
12607 void Wait() {
12608 run_loop_.reset(new base::RunLoop());
12609 run_loop_->Run();
12610 run_loop_.reset();
12611 }
12612
12613 private:
Arthur Hemery2a0a28be2019-03-06 17:51:3612614 // DidCommitNavigationInterceptor:
12615 bool WillProcessDidCommitNavigation(
Alex Moshchukbf8684e2018-05-02 22:59:0212616 RenderFrameHost* render_frame_host,
Arthur Hemery2a0a28be2019-03-06 17:51:3612617 NavigationRequest* navigation_request,
Alex Moshchukbf8684e2018-05-02 22:59:0212618 ::FrameHostMsg_DidCommitProvisionalLoad_Params* params,
Lukasz Anforowicz3cfc1efd2019-02-22 02:29:2912619 mojom::DidCommitProvisionalLoadInterfaceParamsPtr* interface_params)
Oksana Zhuravlova8b88e572019-01-07 21:54:0012620 override {
Alex Moshchukbf8684e2018-05-02 22:59:0212621 RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>(
12622 render_frame_host->GetRenderViewHost());
12623 EXPECT_TRUE(rvh->is_active());
12624 rvh->ClosePage();
12625 if (run_loop_)
12626 run_loop_->Quit();
clamyd3bfdb02018-07-12 13:52:1812627 return true;
Alex Moshchukbf8684e2018-05-02 22:59:0212628 }
12629
12630 std::unique_ptr<base::RunLoop> run_loop_;
12631
12632 DISALLOW_COPY_AND_ASSIGN(ClosePageBeforeCommitHelper);
12633};
12634
12635} // namespace
12636
12637// Verify that when a tab is closed just before a commit IPC arrives for a
12638// subframe in the tab, a subsequent resource timing IPC from the subframe RFH
12639// won't generate a renderer kill. See https://crbug.com/805705.
12640IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
12641 CloseTabBeforeSubframeCommits) {
12642 GURL main_url(embedded_test_server()->GetURL(
12643 "a.com", "/cross_site_iframe_factory.html?a(b)"));
12644 EXPECT_TRUE(NavigateToURL(shell(), main_url));
12645 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
12646
12647 // Open a popup in a.com to keep that process alive.
12648 GURL same_site_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
12649 Shell* new_shell = OpenPopup(root, same_site_url, "");
12650
12651 // Add a blank grandchild frame.
12652 RenderFrameHostCreatedObserver frame_observer(shell()->web_contents(), 1);
12653 EXPECT_TRUE(ExecuteScript(
12654 root->child_at(0),
12655 "document.body.appendChild(document.createElement('iframe'));"));
12656 frame_observer.Wait();
12657 FrameTreeNode* grandchild = root->child_at(0)->child_at(0);
12658
12659 // Navigate grandchild to an a.com URL. Note that only a frame's initial
12660 // navigation forwards resource timing info to parent, so it's important that
12661 // this iframe was initially blank.
12662 //
12663 // Just before this URL commits, close the page.
12664 ClosePageBeforeCommitHelper close_page_helper(web_contents());
12665 EXPECT_TRUE(
danakj824a7ff2019-02-07 20:34:0212666 ExecuteScript(grandchild, JsReplace("location = $1", same_site_url)));
Alex Moshchukbf8684e2018-05-02 22:59:0212667 close_page_helper.Wait();
12668
12669 // Test passes if the a.com renderer doesn't crash. Ping to verify.
12670 bool success;
12671 EXPECT_TRUE(ExecuteScriptAndExtractBool(
12672 new_shell, "window.domAutomationController.send(true);", &success));
12673 EXPECT_TRUE(success);
12674}
12675
sunxd540a9962018-05-24 22:51:0612676class SitePerProcessBrowserTouchActionTest : public SitePerProcessBrowserTest {
12677 public:
Xida Chena964d8a92019-03-15 20:09:1912678 SitePerProcessBrowserTouchActionTest()
12679 : compositor_touch_action_enabled_(
12680 base::FeatureList::IsEnabled(features::kCompositorTouchAction)) {}
sunxd540a9962018-05-24 22:51:0612681
Xida Chen9d4c1be2018-10-13 00:52:1212682 // Computes the effective and white-listed touch action for |rwhv_child| by
12683 // dispatching a touch to it through |rwhv_root|. |rwhv_root| is the root
12684 // frame containing |rwhv_child|. |rwhv_child| is the child (or indirect
12685 // descendent) of |rwhv_root| to get the touch action of. |event_position|
12686 // should be within |rwhv_child| in |rwhv_root|'s coordinate space.
12687 void GetTouchActionsForChild(
sunxd540a9962018-05-24 22:51:0612688 RenderWidgetHostInputEventRouter* router,
12689 RenderWidgetHostViewBase* rwhv_root,
12690 RenderWidgetHostViewBase* rwhv_child,
Xida Chen81f32ee2018-09-13 21:41:2812691 const gfx::Point& event_position,
Xida Chen9d4c1be2018-10-13 00:52:1212692 base::Optional<cc::TouchAction>& effective_touch_action,
Xida Chen81f32ee2018-09-13 21:41:2812693 base::Optional<cc::TouchAction>& whitelisted_touch_action) {
sunxd540a9962018-05-24 22:51:0612694 InputEventAckWaiter ack_observer(
12695 rwhv_child->GetRenderWidgetHost(),
12696 base::BindRepeating([](content::InputEventAckSource source,
12697 content::InputEventAckState state,
12698 const blink::WebInputEvent& event) {
12699 return event.GetType() == blink::WebGestureEvent::kTouchStart ||
12700 event.GetType() == blink::WebGestureEvent::kTouchMove ||
12701 event.GetType() == blink::WebGestureEvent::kTouchEnd;
12702 }));
12703
Xida Chend74fd532018-10-16 17:46:2812704 InputRouterImpl* input_router = static_cast<InputRouterImpl*>(
12705 static_cast<RenderWidgetHostImpl*>(rwhv_child->GetRenderWidgetHost())
12706 ->input_router());
12707 // Clear the touch actions that were set by previous touches.
12708 input_router->touch_action_filter_.allowed_touch_action_.reset();
sunxd540a9962018-05-24 22:51:0612709 // Send a touch start event to child to get the TAF filled with child
12710 // frame's touch action.
12711 ack_observer.Reset();
12712 SyntheticWebTouchEvent touch_event;
12713 int index = touch_event.PressPoint(event_position.x(), event_position.y());
12714 router->RouteTouchEvent(rwhv_root, &touch_event,
12715 ui::LatencyInfo(ui::SourceEventType::TOUCH));
12716 ack_observer.Wait();
Xida Chena964d8a92019-03-15 20:09:1912717 // Reset them to get the new value.
12718 effective_touch_action.reset();
12719 whitelisted_touch_action.reset();
Xida Chen9d4c1be2018-10-13 00:52:1212720 effective_touch_action =
Xida Chend74fd532018-10-16 17:46:2812721 input_router->touch_action_filter_.allowed_touch_action_;
Xida Chen84d0845e2018-11-02 20:36:5412722 // Effective touch action are sent from a separate IPC
12723 // channel, so it is not guaranteed to have value when the ACK for the
12724 // touch start arrived because the ACK is from the main thread.
Xida Chen81f32ee2018-09-13 21:41:2812725 whitelisted_touch_action =
Xida Chend74fd532018-10-16 17:46:2812726 input_router->touch_action_filter_.white_listed_touch_action_;
Xida Chena964d8a92019-03-15 20:09:1912727 // When flag is enabled, we may not have the value for the effective touch
12728 // action when ack is received.
12729 if (!compositor_touch_action_enabled_) {
12730 while (!effective_touch_action.has_value()) {
12731 GiveItSomeTime(TestTimeouts::tiny_timeout());
12732 effective_touch_action =
12733 input_router->touch_action_filter_.allowed_touch_action_;
12734 }
Xida Chen84d0845e2018-11-02 20:36:5412735 }
sunxd540a9962018-05-24 22:51:0612736
12737 // Send a touch move and touch end to complete the sequence, this also
12738 // avoids triggering DCHECKs when sending followup events.
12739 ack_observer.Reset();
12740 touch_event.MovePoint(index, 1, 1);
12741 router->RouteTouchEvent(rwhv_root, &touch_event,
12742 ui::LatencyInfo(ui::SourceEventType::TOUCH));
12743 ack_observer.Wait();
12744
12745 ack_observer.Reset();
12746 touch_event.ReleasePoint(index);
12747 router->RouteTouchEvent(rwhv_root, &touch_event,
12748 ui::LatencyInfo(ui::SourceEventType::TOUCH));
12749 ack_observer.Wait();
sunxd540a9962018-05-24 22:51:0612750 }
12751
Xida Chen84d0845e2018-11-02 20:36:5412752 void GiveItSomeTime(const base::TimeDelta& t) {
12753 base::RunLoop run_loop;
12754 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
12755 FROM_HERE, run_loop.QuitClosure(), t);
12756 run_loop.Run();
12757 }
12758
sunxd540a9962018-05-24 22:51:0612759 // Waits until the parent frame has had enough time to propagate the effective
12760 // touch action to the child frame and the child frame has had enough time to
12761 // process it.
jonrosse9c5a518d2018-06-15 16:13:2212762 void WaitForTouchActionUpdated(
12763 MainThreadFrameObserver* root_thread_observer,
12764 MainThreadFrameObserver* child_thread_observer) {
12765 // Sends an event to the root frame's renderer main thread, upon return the
12766 // root frame should have calculated the new effective touch action for the
12767 // child frame.
12768 root_thread_observer->Wait();
12769 // Sends an event to the child frame's renderer main thread, upon return the
12770 // child frame should have received the effective touch action from parent
12771 // and propagated it.
12772 child_thread_observer->Wait();
12773 // The child's handling of the touch action may lead to further propagation
12774 // back to the parent. This sends an event to the root frame's renderer main
12775 // thread, upon return it should have handled any touch action update.
12776 root_thread_observer->Wait();
sunxd540a9962018-05-24 22:51:0612777 }
Xida Chena964d8a92019-03-15 20:09:1912778
12779 protected:
12780 const bool compositor_touch_action_enabled_;
sunxd540a9962018-05-24 22:51:0612781};
12782
arthursonzogni662d8812019-03-26 11:31:1312783// Flaky on every platform, failing most of the time on Android.
12784// See https://crbug.com/945734
sunxd540a9962018-05-24 22:51:0612785IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTouchActionTest,
arthursonzogni662d8812019-03-26 11:31:1312786 DISABLED_EffectiveTouchActionPropagatesAcrossFrames) {
sunxd540a9962018-05-24 22:51:0612787 GURL main_url(embedded_test_server()->GetURL(
12788 "a.com", "/cross_site_iframe_factory.html?a(b)"));
12789 EXPECT_TRUE(NavigateToURL(shell(), main_url));
12790 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
12791 FrameTreeNode* child = root->child_at(0);
12792 RenderWidgetHostViewBase* rwhv_root = static_cast<RenderWidgetHostViewBase*>(
12793 root->current_frame_host()->GetRenderWidgetHost()->GetView());
12794 RenderWidgetHostViewBase* rwhv_child = static_cast<RenderWidgetHostViewBase*>(
12795 child->current_frame_host()->GetRenderWidgetHost()->GetView());
jonrosse9c5a518d2018-06-15 16:13:2212796 std::unique_ptr<MainThreadFrameObserver> root_thread_observer(
12797 new MainThreadFrameObserver(
12798 root->current_frame_host()->GetRenderWidgetHost()));
12799 root_thread_observer->Wait();
sunxd540a9962018-05-24 22:51:0612800
12801 GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
12802 NavigateFrameToURL(child, b_url);
12803
12804 // Force the renderer to generate a new frame.
12805 EXPECT_TRUE(
12806 ExecuteScript(shell(), "document.body.style.touchAction = 'none'"));
12807 // Waits for the next frame.
jonrosse9c5a518d2018-06-15 16:13:2212808 WaitForHitTestDataOrChildSurfaceReady(child->current_frame_host());
12809 std::unique_ptr<MainThreadFrameObserver> child_thread_observer(
12810 new MainThreadFrameObserver(
12811 child->current_frame_host()->GetRenderWidgetHost()));
sunxd540a9962018-05-24 22:51:0612812
12813 RenderWidgetHostViewChildFrame* child_view =
12814 static_cast<RenderWidgetHostViewChildFrame*>(
12815 child->current_frame_host()->GetRenderWidgetHost()->GetView());
12816 gfx::Point point_inside_child = ToFlooredPoint(
12817 child_view->TransformPointToRootCoordSpaceF(gfx::PointF(+5.f, +5.f)));
12818
12819 RenderWidgetHostInputEventRouter* router =
12820 static_cast<WebContentsImpl*>(web_contents())->GetInputEventRouter();
12821
jonrosse9c5a518d2018-06-15 16:13:2212822 WaitForTouchActionUpdated(root_thread_observer.get(),
12823 child_thread_observer.get());
Xida Chen9d4c1be2018-10-13 00:52:1212824 base::Optional<cc::TouchAction> effective_touch_action;
Xida Chen81f32ee2018-09-13 21:41:2812825 base::Optional<cc::TouchAction> whitelisted_touch_action;
12826 cc::TouchAction expected_touch_action = cc::kTouchActionPan;
sunxd540a9962018-05-24 22:51:0612827 // Gestures are filtered by the intersection of touch-action values of the
12828 // touched element and all its ancestors up to the one that implements the
12829 // gesture. Since iframe allows scrolling, touch action pan restrictions will
12830 // not affect iframe's descendants, so we expect kTouchActionPan instead of
12831 // kTouchActionAuto in iframe's child.
Xida Chen9d4c1be2018-10-13 00:52:1212832 GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
12833 effective_touch_action, whitelisted_touch_action);
Xida Chena964d8a92019-03-15 20:09:1912834 if (!compositor_touch_action_enabled_) {
12835 EXPECT_EQ(expected_touch_action, effective_touch_action.has_value()
12836 ? effective_touch_action.value()
12837 : cc::kTouchActionAuto);
12838 }
Xida Chen81f32ee2018-09-13 21:41:2812839 if (whitelisted_touch_action.has_value())
12840 EXPECT_EQ(expected_touch_action, whitelisted_touch_action.value());
sunxd540a9962018-05-24 22:51:0612841
12842 EXPECT_TRUE(
12843 ExecuteScript(shell(), "document.body.style.touchAction = 'auto'"));
jonrosse9c5a518d2018-06-15 16:13:2212844 WaitForTouchActionUpdated(root_thread_observer.get(),
12845 child_thread_observer.get());
Xida Chen81f32ee2018-09-13 21:41:2812846 expected_touch_action = cc::kTouchActionAuto;
Xida Chen9d4c1be2018-10-13 00:52:1212847 GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
12848 effective_touch_action, whitelisted_touch_action);
Xida Chena964d8a92019-03-15 20:09:1912849 if (compositor_touch_action_enabled_) {
12850 EXPECT_EQ(expected_touch_action, effective_touch_action.has_value()
12851 ? effective_touch_action.value()
12852 : cc::kTouchActionAuto);
12853 }
Xida Chen81f32ee2018-09-13 21:41:2812854 if (whitelisted_touch_action.has_value())
12855 EXPECT_EQ(expected_touch_action, whitelisted_touch_action.value());
sunxd540a9962018-05-24 22:51:0612856}
12857
Eric Stevenson89fdab72019-04-17 20:19:0612858// Flaky on Android. http://crbug.com/951513
12859#if defined(OS_ANDROID)
12860#define MAYBE_EffectiveTouchActionPropagatesAcrossNestedFrames \
12861 DISABLED_EffectiveTouchActionPropagatesAcrossNestedFrames
12862#else
12863#define MAYBE_EffectiveTouchActionPropagatesAcrossNestedFrames \
12864 EffectiveTouchActionPropagatesAcrossNestedFrames
12865#endif
12866
sunxd540a9962018-05-24 22:51:0612867IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTouchActionTest,
Eric Stevenson89fdab72019-04-17 20:19:0612868 MAYBE_EffectiveTouchActionPropagatesAcrossNestedFrames) {
sunxd540a9962018-05-24 22:51:0612869 GURL main_url(embedded_test_server()->GetURL(
12870 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
12871 EXPECT_TRUE(NavigateToURL(shell(), main_url));
12872
12873 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
12874 FrameTreeNode* parent = root->child_at(0);
12875 GURL b_url(embedded_test_server()->GetURL(
12876 "b.com", "/frame_tree/page_with_iframe_in_div.html"));
12877 NavigateFrameToURL(parent, b_url);
12878
12879 ASSERT_EQ(1U, parent->child_count());
12880 EXPECT_EQ(
12881 " Site A ------------ proxies for B C\n"
12882 " +--Site B ------- proxies for A C\n"
12883 " +--Site C -- proxies for A B\n"
12884 "Where A = http://a.com/\n"
12885 " B = http://b.com/\n"
12886 " C = http://bar.com/",
12887 DepictFrameTree(root));
12888
12889 FrameTreeNode* child = root->child_at(0)->child_at(0);
12890 RenderWidgetHostViewBase* rwhv_root = static_cast<RenderWidgetHostViewBase*>(
12891 root->current_frame_host()->GetRenderWidgetHost()->GetView());
12892 RenderWidgetHostViewBase* rwhv_child = static_cast<RenderWidgetHostViewBase*>(
12893 child->current_frame_host()->GetRenderWidgetHost()->GetView());
jonrosse9c5a518d2018-06-15 16:13:2212894 std::unique_ptr<MainThreadFrameObserver> root_thread_observer(
12895 new MainThreadFrameObserver(
12896 root->current_frame_host()->GetRenderWidgetHost()));
12897 root_thread_observer->Wait();
sunxd540a9962018-05-24 22:51:0612898
12899 EXPECT_TRUE(
12900 ExecuteScript(shell(), "document.body.style.touchAction = 'none'"));
12901
12902 // Wait for child frame ready in order to get the correct point inside child.
jonrosse9c5a518d2018-06-15 16:13:2212903 WaitForHitTestDataOrChildSurfaceReady(child->current_frame_host());
12904 std::unique_ptr<MainThreadFrameObserver> child_thread_observer(
12905 new MainThreadFrameObserver(
12906 child->current_frame_host()->GetRenderWidgetHost()));
sunxd540a9962018-05-24 22:51:0612907 RenderWidgetHostViewChildFrame* child_view =
12908 static_cast<RenderWidgetHostViewChildFrame*>(
12909 child->current_frame_host()->GetRenderWidgetHost()->GetView());
12910 gfx::Point point_inside_child = ToFlooredPoint(
12911 child_view->TransformPointToRootCoordSpaceF(gfx::PointF(+5.f, +5.f)));
12912
12913 RenderWidgetHostInputEventRouter* router =
12914 static_cast<WebContentsImpl*>(web_contents())->GetInputEventRouter();
12915
12916 // Child should inherit effective touch action none from root.
jonrosse9c5a518d2018-06-15 16:13:2212917 WaitForTouchActionUpdated(root_thread_observer.get(),
12918 child_thread_observer.get());
Xida Chen9d4c1be2018-10-13 00:52:1212919 base::Optional<cc::TouchAction> effective_touch_action;
Xida Chen81f32ee2018-09-13 21:41:2812920 base::Optional<cc::TouchAction> whitelisted_touch_action;
12921 cc::TouchAction expected_touch_action = cc::kTouchActionPan;
Xida Chen9d4c1be2018-10-13 00:52:1212922 GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
12923 effective_touch_action, whitelisted_touch_action);
Ria Jiang7c160fbf2019-01-02 16:53:2012924 cc::TouchAction effective_touch_action_result =
12925 effective_touch_action.has_value() ? effective_touch_action.value()
12926 : cc::kTouchActionAuto;
12927 // TouchAction might have not been propagated to child frames yet, loop until
12928 // we get the expected touch action value.
Xida Chena964d8a92019-03-15 20:09:1912929 if (!compositor_touch_action_enabled_) {
12930 while (expected_touch_action != effective_touch_action_result) {
12931 GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
12932 effective_touch_action, whitelisted_touch_action);
12933 effective_touch_action_result = effective_touch_action.has_value()
12934 ? effective_touch_action.value()
12935 : cc::kTouchActionAuto;
12936 }
Ria Jiang7c160fbf2019-01-02 16:53:2012937 }
Xida Chen81f32ee2018-09-13 21:41:2812938 if (whitelisted_touch_action.has_value())
12939 EXPECT_EQ(expected_touch_action, whitelisted_touch_action.value());
sunxd540a9962018-05-24 22:51:0612940
12941 // Child should inherit effective touch action none from parent.
12942 EXPECT_TRUE(
12943 ExecuteScript(shell(), "document.body.style.touchAction = 'auto'"));
12944 EXPECT_TRUE(ExecuteScript(
12945 parent,
12946 "document.getElementById('parent-div').style.touchAction = 'none';"));
jonrosse9c5a518d2018-06-15 16:13:2212947 WaitForTouchActionUpdated(root_thread_observer.get(),
12948 child_thread_observer.get());
Xida Chen9d4c1be2018-10-13 00:52:1212949 GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
12950 effective_touch_action, whitelisted_touch_action);
Ria Jiang7c160fbf2019-01-02 16:53:2012951 effective_touch_action_result = effective_touch_action.has_value()
12952 ? effective_touch_action.value()
12953 : cc::kTouchActionAuto;
Xida Chena964d8a92019-03-15 20:09:1912954 if (!compositor_touch_action_enabled_) {
12955 while (expected_touch_action != effective_touch_action_result) {
12956 GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
12957 effective_touch_action, whitelisted_touch_action);
12958 effective_touch_action_result = effective_touch_action.has_value()
12959 ? effective_touch_action.value()
12960 : cc::kTouchActionAuto;
12961 }
Ria Jiang7c160fbf2019-01-02 16:53:2012962 }
Xida Chen81f32ee2018-09-13 21:41:2812963 if (whitelisted_touch_action.has_value())
12964 EXPECT_EQ(expected_touch_action, whitelisted_touch_action.value());
sunxd540a9962018-05-24 22:51:0612965
12966 // Child should inherit effective touch action auto from root and parent.
12967 EXPECT_TRUE(ExecuteScript(
12968 parent,
12969 "document.getElementById('parent-div').style.touchAction = 'auto'"));
jonrosse9c5a518d2018-06-15 16:13:2212970 WaitForTouchActionUpdated(root_thread_observer.get(),
12971 child_thread_observer.get());
Xida Chen81f32ee2018-09-13 21:41:2812972 expected_touch_action = cc::kTouchActionAuto;
Xida Chen9d4c1be2018-10-13 00:52:1212973 GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
12974 effective_touch_action, whitelisted_touch_action);
Ria Jiang7c160fbf2019-01-02 16:53:2012975 effective_touch_action_result = effective_touch_action.has_value()
12976 ? effective_touch_action.value()
12977 : cc::kTouchActionAuto;
Xida Chena964d8a92019-03-15 20:09:1912978 if (!compositor_touch_action_enabled_) {
12979 while (expected_touch_action != effective_touch_action_result) {
12980 GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
12981 effective_touch_action, whitelisted_touch_action);
12982 effective_touch_action_result = effective_touch_action.has_value()
12983 ? effective_touch_action.value()
12984 : cc::kTouchActionAuto;
12985 }
Ria Jiang7c160fbf2019-01-02 16:53:2012986 }
Xida Chen81f32ee2018-09-13 21:41:2812987 if (whitelisted_touch_action.has_value())
12988 EXPECT_EQ(expected_touch_action, whitelisted_touch_action.value());
sunxd540a9962018-05-24 22:51:0612989}
12990
12991IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTouchActionTest,
12992 EffectiveTouchActionPropagatesWhenChildFrameNavigates) {
12993 GURL main_url(embedded_test_server()->GetURL(
12994 "a.com", "/cross_site_iframe_factory.html?a(b)"));
12995 EXPECT_TRUE(NavigateToURL(shell(), main_url));
12996
12997 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
12998 FrameTreeNode* child = root->child_at(0);
12999 GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
13000 NavigateFrameToURL(child, b_url);
13001
13002 EXPECT_EQ(
13003 " Site A ------------ proxies for B\n"
13004 " +--Site B ------- proxies for A\n"
13005 "Where A = http://a.com/\n"
13006 " B = http://b.com/",
13007 DepictFrameTree(root));
13008
13009 RenderWidgetHostViewBase* rwhv_root = static_cast<RenderWidgetHostViewBase*>(
13010 root->current_frame_host()->GetRenderWidgetHost()->GetView());
13011 RenderWidgetHostViewBase* rwhv_child = static_cast<RenderWidgetHostViewBase*>(
13012 child->current_frame_host()->GetRenderWidgetHost()->GetView());
jonrosse9c5a518d2018-06-15 16:13:2213013 std::unique_ptr<MainThreadFrameObserver> root_thread_observer(
13014 new MainThreadFrameObserver(
13015 root->current_frame_host()->GetRenderWidgetHost()));
13016 root_thread_observer->Wait();
sunxd540a9962018-05-24 22:51:0613017
13018 EXPECT_TRUE(
13019 ExecuteScript(shell(), "document.body.style.touchAction = 'none'"));
13020
13021 // Wait for child frame ready in order to get the correct point inside child.
jonrosse9c5a518d2018-06-15 16:13:2213022 WaitForHitTestDataOrChildSurfaceReady(child->current_frame_host());
13023 std::unique_ptr<MainThreadFrameObserver> child_thread_observer(
13024 new MainThreadFrameObserver(
13025 child->current_frame_host()->GetRenderWidgetHost()));
sunxd540a9962018-05-24 22:51:0613026 RenderWidgetHostViewChildFrame* child_view =
13027 static_cast<RenderWidgetHostViewChildFrame*>(
13028 child->current_frame_host()->GetRenderWidgetHost()->GetView());
13029 gfx::Point point_inside_child = gfx::ToFlooredPoint(
13030 child_view->TransformPointToRootCoordSpaceF(gfx::PointF(+5.f, +5.f)));
13031
13032 RenderWidgetHostInputEventRouter* router =
13033 static_cast<WebContentsImpl*>(web_contents())->GetInputEventRouter();
13034 // Child should inherit effective touch action none from root.
jonrosse9c5a518d2018-06-15 16:13:2213035 WaitForTouchActionUpdated(root_thread_observer.get(),
13036 child_thread_observer.get());
Xida Chen9d4c1be2018-10-13 00:52:1213037 base::Optional<cc::TouchAction> effective_touch_action;
Xida Chen81f32ee2018-09-13 21:41:2813038 base::Optional<cc::TouchAction> whitelisted_touch_action;
13039 cc::TouchAction expected_touch_action = cc::kTouchActionPan;
Xida Chen9d4c1be2018-10-13 00:52:1213040 GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
13041 effective_touch_action, whitelisted_touch_action);
Xida Chena964d8a92019-03-15 20:09:1913042 if (!compositor_touch_action_enabled_) {
13043 EXPECT_EQ(expected_touch_action, effective_touch_action.has_value()
13044 ? effective_touch_action.value()
13045 : cc::kTouchActionAuto);
13046 }
Xida Chen81f32ee2018-09-13 21:41:2813047 if (whitelisted_touch_action.has_value())
13048 EXPECT_EQ(expected_touch_action, whitelisted_touch_action.value());
sunxd540a9962018-05-24 22:51:0613049
13050 // After navigation, child should still inherit effective touch action none
13051 // from parent.
13052 GURL new_url(embedded_test_server()->GetURL("c.com", "/title2.html"));
jonrosse9c5a518d2018-06-15 16:13:2213053 // Reset before navigation, as navigation destroys the underlying
13054 // RenderWidgetHost being observed.
13055 child_thread_observer.reset();
sunxd540a9962018-05-24 22:51:0613056 NavigateFrameToURL(child, new_url);
jonrosse9c5a518d2018-06-15 16:13:2213057 WaitForHitTestDataOrChildSurfaceReady(child->current_frame_host());
13058 // Navigation destroys the previous RenderWidgetHost, so we need to begin
13059 // observing the new renderer main thread associated with the child frame.
13060 child_thread_observer.reset(new MainThreadFrameObserver(
13061 child->current_frame_host()->GetRenderWidgetHost()));
13062
sunxd540a9962018-05-24 22:51:0613063 rwhv_child = static_cast<RenderWidgetHostViewBase*>(
13064 child->current_frame_host()->GetRenderWidgetHost()->GetView());
13065
jonrosse9c5a518d2018-06-15 16:13:2213066 WaitForTouchActionUpdated(root_thread_observer.get(),
13067 child_thread_observer.get());
Xida Chen9d4c1be2018-10-13 00:52:1213068 GetTouchActionsForChild(router, rwhv_root, rwhv_child, point_inside_child,
13069 effective_touch_action, whitelisted_touch_action);
Xida Chena964d8a92019-03-15 20:09:1913070 if (!compositor_touch_action_enabled_) {
13071 EXPECT_EQ(expected_touch_action, effective_touch_action.has_value()
13072 ? effective_touch_action.value()
13073 : cc::kTouchActionAuto);
13074 }
Xida Chen81f32ee2018-09-13 21:41:2813075 if (whitelisted_touch_action.has_value())
13076 EXPECT_EQ(expected_touch_action, whitelisted_touch_action.value());
sunxd540a9962018-05-24 22:51:0613077}
13078
Lukasz Anforowicz873e6082018-05-17 00:12:3313079IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
13080 ChildFrameCrashMetrics_KilledWhileVisible) {
13081 // Set-up a frame tree that helps verify what the metrics tracks:
13082 // 1) frames (12 frames are affected if B process gets killed) or
13083 // 2) crashes (simply 1 crash if B process gets killed)?
13084 // 3) widgets (10 b widgets and 1 c widget are affected if B is killed,
13085 // but a sad frame will appear only in 9 widgets - this excludes
13086 // widgets for the b,c(b) part of the frame tree) or
13087 GURL main_url(embedded_test_server()->GetURL(
13088 "a.com", "/cross_site_iframe_factory.html?a(b(b,c(b)),b,b,b,b,b,b,b,b)"));
13089 EXPECT_TRUE(NavigateToURL(shell(), main_url));
13090 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
13091
13092 // Kill the child frame.
13093 base::HistogramTester histograms;
13094 RenderProcessHost* child_process =
13095 root->child_at(0)->current_frame_host()->GetProcess();
13096 RenderProcessHostWatcher crash_observer(
13097 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
13098 child_process->Shutdown(0);
13099 crash_observer.Wait();
13100
13101 // Verify that the expected metrics got logged.
13102 histograms.ExpectUniqueSample(
13103 "Stability.ChildFrameCrash.Visibility",
13104 CrossProcessFrameConnector::CrashVisibility::kCrashedWhileVisible, 9);
13105
13106 // Hide and show the web contents and verify that no more metrics got logged.
13107 web_contents()->UpdateWebContentsVisibility(Visibility::HIDDEN);
13108 web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
13109 histograms.ExpectUniqueSample(
13110 "Stability.ChildFrameCrash.Visibility",
13111 CrossProcessFrameConnector::CrashVisibility::kCrashedWhileVisible, 9);
13112}
13113
13114IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
13115 ChildFrameCrashMetrics_KilledMainFrame) {
13116 GURL main_url(embedded_test_server()->GetURL(
13117 "a.com", "/cross_site_iframe_factory.html?a(a(b(b,c)))"));
13118 EXPECT_TRUE(NavigateToURL(shell(), main_url));
13119 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
13120
13121 // Kill the main frame.
13122 base::HistogramTester histograms;
13123 RenderProcessHost* child_process = root->current_frame_host()->GetProcess();
13124 RenderProcessHostWatcher crash_observer(
13125 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
13126 child_process->Shutdown(0);
13127 crash_observer.Wait();
13128
13129 // Verify that no child frame metrics got logged.
13130 histograms.ExpectTotalCount("Stability.ChildFrameCrash.Visibility", 0);
13131}
13132
13133IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
13134 ChildFrameCrashMetrics_KilledWhileHiddenThenShown) {
13135 // Set-up a frame tree that helps verify what the metrics tracks:
13136 // 1) frames (12 frames are affected if B process gets killed) or
13137 // 2) widgets (10 b widgets and 1 c widget are affected if B is killed) or
13138 // 3) crashes (1 crash if B process gets killed)?
13139 GURL main_url(embedded_test_server()->GetURL(
13140 "a.com", "/cross_site_iframe_factory.html?a(b(b,c),b,b,b,b,b,b,b,b,b)"));
13141 EXPECT_TRUE(NavigateToURL(shell(), main_url));
13142 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
13143
13144 // Hide the web contents (UpdateWebContentsVisibility is called twice to avoid
13145 // hitting the |!did_first_set_visible_| case).
13146 web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
13147 web_contents()->UpdateWebContentsVisibility(Visibility::HIDDEN);
13148
13149 // Kill the subframe.
13150 base::HistogramTester histograms;
13151 RenderProcessHost* child_process =
13152 root->child_at(0)->current_frame_host()->GetProcess();
13153 RenderProcessHostWatcher crash_observer(
13154 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
13155 child_process->Shutdown(0);
13156 crash_observer.Wait();
13157
13158 // Verify that no child frame metrics got logged (yet - while WebContents are
13159 // hidden).
13160 histograms.ExpectTotalCount("Stability.ChildFrameCrash.Visibility", 0);
Alex Moshchuk2b0c91d2018-10-10 18:41:1613161 histograms.ExpectTotalCount(
13162 "Stability.ChildFrameCrash.ShownAfterCrashingReason", 0);
Lukasz Anforowicz873e6082018-05-17 00:12:3313163
Alex Moshchuk2b0c91d2018-10-10 18:41:1613164 // Show the web contents and verify that the expected metrics got logged.
Lukasz Anforowicz873e6082018-05-17 00:12:3313165 web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
13166 histograms.ExpectUniqueSample(
13167 "Stability.ChildFrameCrash.Visibility",
13168 CrossProcessFrameConnector::CrashVisibility::kShownAfterCrashing, 10);
Alex Moshchuk2b0c91d2018-10-10 18:41:1613169 histograms.ExpectUniqueSample(
13170 "Stability.ChildFrameCrash.ShownAfterCrashingReason",
13171 CrossProcessFrameConnector::ShownAfterCrashingReason::kTabWasShown, 10);
Lukasz Anforowicz873e6082018-05-17 00:12:3313172
13173 // Hide and show the web contents again and verify that no more metrics got
13174 // logged.
13175 web_contents()->UpdateWebContentsVisibility(Visibility::HIDDEN);
13176 web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
13177 histograms.ExpectUniqueSample(
13178 "Stability.ChildFrameCrash.Visibility",
13179 CrossProcessFrameConnector::CrashVisibility::kShownAfterCrashing, 10);
Alex Moshchuk2b0c91d2018-10-10 18:41:1613180 histograms.ExpectUniqueSample(
13181 "Stability.ChildFrameCrash.ShownAfterCrashingReason",
13182 CrossProcessFrameConnector::ShownAfterCrashingReason::kTabWasShown, 10);
Lukasz Anforowicz873e6082018-05-17 00:12:3313183}
13184
13185IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
13186 ChildFrameCrashMetrics_NeverShown) {
13187 // Set-up a frame tree that helps verify what the metrics tracks:
13188 // 1) frames (12 frames are affected if B process gets killed) or
13189 // 2) widgets (10 b widgets and 1 c widget are affected if B is killed) or
13190 // 3) crashes (1 crash if B process gets killed)?
13191 GURL main_url(embedded_test_server()->GetURL(
13192 "a.com", "/cross_site_iframe_factory.html?a(b(b,c),b,b,b,b,b,b,b,b,b)"));
13193 EXPECT_TRUE(NavigateToURL(shell(), main_url));
13194 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
13195
13196 // Hide the web contents (UpdateWebContentsVisibility is called twice to avoid
13197 // hitting the |!did_first_set_visible_| case).
13198 web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
13199 web_contents()->UpdateWebContentsVisibility(Visibility::HIDDEN);
13200
13201 // Kill the subframe.
13202 base::HistogramTester histograms;
13203 RenderProcessHost* child_process =
13204 root->child_at(0)->current_frame_host()->GetProcess();
13205 RenderProcessHostWatcher crash_observer(
13206 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
13207 child_process->Shutdown(0);
13208 crash_observer.Wait();
13209
13210 // Navigate away - this will trigger logging of the UMA.
13211 EXPECT_TRUE(NavigateToURL(shell(), GURL("about:blank")));
13212 histograms.ExpectUniqueSample(
13213 "Stability.ChildFrameCrash.Visibility",
13214 CrossProcessFrameConnector::CrashVisibility::kNeverVisibleAfterCrash, 10);
13215}
13216
13217IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
13218 ChildFrameCrashMetrics_ScrolledIntoView) {
13219 GURL main_url(embedded_test_server()->GetURL(
13220 "a.com", "/cross_site_iframe_factory.html?a(b)"));
13221 EXPECT_TRUE(NavigateToURL(shell(), main_url));
13222 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
13223
13224 // Fill the main frame so that the subframe is pushed below the fold (is
13225 // scrolled outside of the current view) and wait until the main frame redraws
13226 // itself (i.e. making sure CPFC::OnUpdateViewportIntersection has arrived).
13227 std::string filling_script = R"(
13228 var frame = document.body.querySelectorAll("iframe")[0];
13229 for (var i = 0; i < 100; i++) {
13230 var p = document.createElement("p");
13231 p.innerText = "blah";
13232 document.body.insertBefore(p, frame);
13233 }
13234 )";
13235 EXPECT_TRUE(ExecuteScript(root, filling_script));
13236 MainThreadFrameObserver main_widget_observer(
13237 root->current_frame_host()->GetRenderWidgetHost());
13238 main_widget_observer.Wait();
13239
13240 // Kill the child frame.
13241 base::HistogramTester histograms;
13242 RenderProcessHost* child_process =
13243 root->child_at(0)->current_frame_host()->GetProcess();
13244 RenderProcessHostWatcher crash_observer(
13245 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
13246 child_process->Shutdown(0);
13247 crash_observer.Wait();
13248
13249 // Verify that no child frame metrics got logged (yet - while the subframe is
13250 // below the fold / is not scrolled into view).
13251 histograms.ExpectTotalCount("Stability.ChildFrameCrash.Visibility", 0);
Alex Moshchuk2b0c91d2018-10-10 18:41:1613252 histograms.ExpectTotalCount(
13253 "Stability.ChildFrameCrash.ShownAfterCrashingReason", 0);
Lukasz Anforowicz873e6082018-05-17 00:12:3313254
13255 // Scroll the subframe into view and wait until the scrolled frame draws
13256 // itself.
13257 std::string scrolling_script = R"(
13258 var frame = document.body.querySelectorAll("iframe")[0];
13259 frame.scrollIntoView();
13260 )";
13261 EXPECT_TRUE(ExecuteScript(root, scrolling_script));
13262 main_widget_observer.Wait();
13263
13264 // Verify that the expected metrics got logged.
13265 histograms.ExpectUniqueSample(
13266 "Stability.ChildFrameCrash.Visibility",
13267 CrossProcessFrameConnector::CrashVisibility::kShownAfterCrashing, 1);
Alex Moshchuk2b0c91d2018-10-10 18:41:1613268 histograms.ExpectUniqueSample(
13269 "Stability.ChildFrameCrash.ShownAfterCrashingReason",
13270 CrossProcessFrameConnector::ShownAfterCrashingReason::
13271 kViewportIntersection,
13272 1);
13273}
13274
13275IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
13276 ChildFrameCrashMetrics_ScrolledIntoViewAfterTabIsShown) {
Alex Moshchuk7b4f0652019-05-30 18:54:4113277 // Disable the feature to mark hidden tabs with sad frames for reload, since
13278 // it makes the scenario for which this test collects metrics impossible.
13279 base::test::ScopedFeatureList feature_list_;
13280 feature_list_.InitAndDisableFeature(
13281 features::kReloadHiddenTabsWithCrashedSubframes);
13282
Alex Moshchuk2b0c91d2018-10-10 18:41:1613283 // Start on a page that has a single iframe, which is positioned out of
13284 // view, and navigate that iframe cross-site.
13285 GURL main_url(
13286 embedded_test_server()->GetURL("a.com", "/iframe_out_of_view.html"));
13287 EXPECT_TRUE(NavigateToURL(shell(), main_url));
13288 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
13289 NavigateFrameToURL(root->child_at(0),
13290 embedded_test_server()->GetURL("b.com", "/title1.html"));
13291
13292 // Hide the web contents (UpdateWebContentsVisibility is called twice to avoid
13293 // hitting the |!did_first_set_visible_| case).
13294 web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
13295 web_contents()->UpdateWebContentsVisibility(Visibility::HIDDEN);
13296
13297 // Kill the child frame.
13298 base::HistogramTester histograms;
13299 RenderProcessHost* child_process =
13300 root->child_at(0)->current_frame_host()->GetProcess();
13301 RenderProcessHostWatcher crash_observer(
13302 child_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
13303 child_process->Shutdown(0);
13304 crash_observer.Wait();
13305
13306 // Verify that no child frame crash metrics got logged yet.
13307 histograms.ExpectTotalCount("Stability.ChildFrameCrash.Visibility", 0);
13308 histograms.ExpectTotalCount(
13309 "Stability.ChildFrameCrash.ShownAfterCrashingReason", 0);
13310
13311 // Show the web contents. The crash metrics still shouldn't be logged, since
13312 // the crashed frame is out of view.
13313 web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
13314 histograms.ExpectTotalCount("Stability.ChildFrameCrash.Visibility", 0);
13315 histograms.ExpectTotalCount(
13316 "Stability.ChildFrameCrash.ShownAfterCrashingReason", 0);
13317
13318 // Scroll the subframe into view and wait until the scrolled frame draws
13319 // itself.
13320 MainThreadFrameObserver main_widget_observer(
13321 root->current_frame_host()->GetRenderWidgetHost());
13322 std::string scrolling_script = R"(
13323 var frame = document.body.querySelector("iframe");
13324 frame.scrollIntoView();
13325 )";
13326 EXPECT_TRUE(ExecuteScript(root, scrolling_script));
13327 main_widget_observer.Wait();
13328
13329 // Verify that the expected metrics got logged.
13330 histograms.ExpectUniqueSample(
13331 "Stability.ChildFrameCrash.Visibility",
13332 CrossProcessFrameConnector::CrashVisibility::kShownAfterCrashing, 1);
13333 histograms.ExpectUniqueSample(
13334 "Stability.ChildFrameCrash.ShownAfterCrashingReason",
13335 CrossProcessFrameConnector::ShownAfterCrashingReason::
13336 kViewportIntersectionAfterTabWasShown,
13337 1);
13338
13339 // Hide and show the web contents again and verify that no more metrics got
13340 // logged.
13341 web_contents()->UpdateWebContentsVisibility(Visibility::HIDDEN);
13342 web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
13343 histograms.ExpectUniqueSample(
13344 "Stability.ChildFrameCrash.Visibility",
13345 CrossProcessFrameConnector::CrashVisibility::kShownAfterCrashing, 1);
13346 histograms.ExpectUniqueSample(
13347 "Stability.ChildFrameCrash.ShownAfterCrashingReason",
13348 CrossProcessFrameConnector::ShownAfterCrashingReason::
13349 kViewportIntersectionAfterTabWasShown,
13350 1);
Lukasz Anforowicz873e6082018-05-17 00:12:3313351}
13352
Alex Moshchuk7b4f0652019-05-30 18:54:4113353// Verify the feature where hidden tabs with crashed subframes are marked for
13354// reload. This avoids showing crashed subframes if a hidden tab is eventually
13355// shown. See https://crbug.com/841572.
13356IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
13357 ReloadHiddenTabWithCrashedSubframe) {
13358 base::test::ScopedFeatureList feature_list_;
13359 feature_list_.InitAndEnableFeature(
13360 features::kReloadHiddenTabsWithCrashedSubframes);
13361
13362 auto crash_process = [](FrameTreeNode* ftn) {
13363 RenderProcessHost* process = ftn->current_frame_host()->GetProcess();
13364 RenderProcessHostWatcher crash_observer(
13365 process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
13366 process->Shutdown(0);
13367 crash_observer.Wait();
13368 EXPECT_FALSE(ftn->current_frame_host()->IsRenderFrameLive());
13369 };
13370
13371 GURL main_url(embedded_test_server()->GetURL(
13372 "a.com", "/cross_site_iframe_factory.html?a(b)"));
13373 EXPECT_TRUE(NavigateToURL(shell(), main_url));
13374 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
13375
13376 // Hide the WebContents (UpdateWebContentsVisibility is called twice to avoid
13377 // hitting the |!did_first_set_visible_| case).
13378 web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
13379 web_contents()->UpdateWebContentsVisibility(Visibility::HIDDEN);
13380 EXPECT_EQ(Visibility::HIDDEN, web_contents()->GetVisibility());
13381
13382 // Kill the b.com subframe's process. This should mark the hidden
13383 // WebContents for reload.
13384 {
13385 base::HistogramTester histograms;
13386 crash_process(root->child_at(0));
13387 histograms.ExpectUniqueSample(
13388 "Stability.ChildFrameCrash.TabMarkedForReload", true, 1);
13389 histograms.ExpectUniqueSample(
13390 "Stability.ChildFrameCrash.TabMarkedForReload.Visibility",
13391 blink::mojom::FrameVisibility::kRenderedInViewport, 1);
13392
13393 // Show the WebContents. This should trigger a reload of the main frame.
13394 web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
13395 EXPECT_TRUE(WaitForLoadStop(web_contents()));
13396 histograms.ExpectUniqueSample(
13397 "Navigation.LoadIfNecessaryType",
13398 NavigationControllerImpl::NeedsReloadType::kCrashedSubframe, 1);
13399 }
13400
13401 // Both frames should now have live renderer processes.
13402 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
13403 EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive());
13404
13405 // Next, try the same with a crashed subframe that's scrolled out of view.
13406 // This should also trigger a reload.
13407 GURL out_of_view_url(
13408 embedded_test_server()->GetURL("a.com", "/iframe_out_of_view.html"));
13409 EXPECT_TRUE(NavigateToURL(shell(), out_of_view_url));
13410 NavigateIframeToURL(web_contents(), "test_iframe",
13411 embedded_test_server()->GetURL("b.com", "/title1.html"));
13412 web_contents()->UpdateWebContentsVisibility(Visibility::HIDDEN);
13413 {
13414 base::HistogramTester histograms;
13415 crash_process(root->child_at(0));
13416 histograms.ExpectUniqueSample(
13417 "Stability.ChildFrameCrash.TabMarkedForReload", true, 1);
13418 histograms.ExpectUniqueSample(
13419 "Stability.ChildFrameCrash.TabMarkedForReload.Visibility",
13420 blink::mojom::FrameVisibility::kRenderedOutOfViewport, 1);
13421
13422 web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
13423 EXPECT_TRUE(WaitForLoadStop(web_contents()));
13424 histograms.ExpectUniqueSample(
13425 "Navigation.LoadIfNecessaryType",
13426 NavigationControllerImpl::NeedsReloadType::kCrashedSubframe, 1);
13427 }
13428 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
13429 EXPECT_TRUE(root->child_at(0)->current_frame_host()->IsRenderFrameLive());
13430
13431 // Now, load a page with where an iframe is hidden with "display:none".
13432 // Ensure that we do not mark the tab for reload in that case.
13433 GURL hidden_iframe_url(
13434 embedded_test_server()->GetURL("a.com", "/page_with_hidden_iframe.html"));
13435 EXPECT_TRUE(NavigateToURL(shell(), hidden_iframe_url));
13436 NavigateIframeToURL(web_contents(), "test_iframe",
13437 embedded_test_server()->GetURL("b.com", "/title1.html"));
13438 RenderFrameProxyHost* proxy_to_parent =
13439 root->child_at(0)->render_manager()->GetProxyToParent();
13440 EXPECT_TRUE(proxy_to_parent->cross_process_frame_connector()->IsHidden());
13441
13442 web_contents()->UpdateWebContentsVisibility(Visibility::HIDDEN);
13443 {
13444 base::HistogramTester histograms;
13445 crash_process(root->child_at(0));
13446 histograms.ExpectUniqueSample(
13447 "Stability.ChildFrameCrash.TabMarkedForReload", false, 1);
13448 }
13449 web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
13450 EXPECT_TRUE(WaitForLoadStop(web_contents()));
13451 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
13452 EXPECT_FALSE(root->child_at(0)->current_frame_host()->IsRenderFrameLive());
13453
13454 // Finally, ensure that the reload policy doesn't trigger for a visible tab,
13455 // even if it becomes hidden and then visible again.
13456 EXPECT_EQ(Visibility::VISIBLE, web_contents()->GetVisibility());
13457 EXPECT_TRUE(NavigateToURL(shell(), main_url));
13458 {
13459 base::HistogramTester histograms;
13460 crash_process(root->child_at(0));
13461 histograms.ExpectUniqueSample(
13462 "Stability.ChildFrameCrash.TabMarkedForReload", false, 1);
13463 }
13464 EXPECT_EQ(Visibility::VISIBLE, web_contents()->GetVisibility());
13465 web_contents()->UpdateWebContentsVisibility(Visibility::HIDDEN);
13466 web_contents()->UpdateWebContentsVisibility(Visibility::VISIBLE);
13467 EXPECT_TRUE(WaitForLoadStop(web_contents()));
13468 EXPECT_TRUE(root->current_frame_host()->IsRenderFrameLive());
13469 EXPECT_FALSE(root->child_at(0)->current_frame_host()->IsRenderFrameLive());
13470}
13471
Alex Moshchukfb979532018-05-19 22:01:5013472// Check that when a frame changes a subframe's size twice and then sends a
13473// postMessage to the subframe, the subframe's onmessage handler sees the new
13474// size. In particular, ensure that the postMessage won't get reordered with
13475// the second resize, which might be throttled if the first resize is still in
13476// progress. See https://crbug.com/828529.
13477IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
13478 ResizeAndCrossProcessPostMessagePreserveOrder) {
13479 GURL main_url(embedded_test_server()->GetURL(
13480 "a.com", "/cross_site_iframe_factory.html?a(b)"));
13481 EXPECT_TRUE(NavigateToURL(shell(), main_url));
13482 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
13483
13484 // Add an onmessage handler to the subframe to send back its width.
13485 EXPECT_TRUE(ExecuteScript(root->child_at(0), R"(
13486 window.addEventListener('message', function(event) {
13487 domAutomationController.send(document.body.clientWidth);
13488 });)"));
13489
13490 // Drop the visual properties ACKs from the child renderer. To do this,
13491 // unsubscribe the child's RenderWidgetHost from its
13492 // RenderFrameMetadataProvider, which ensures that
13493 // DidUpdateVisualProperties() won't be called on it, and the ACK won't be
13494 // reset. This simulates that the ACK for the first resize below does not
13495 // arrive before the second resize IPC arrives from the
13496 // parent, and that the second resize IPC early-exits in
13497 // SynchronizeVisualProperties() due to the pending visual properties ACK.
13498 RenderWidgetHostImpl* rwh =
13499 root->child_at(0)->current_frame_host()->GetRenderWidgetHost();
13500 rwh->render_frame_metadata_provider_.RemoveObserver(rwh);
13501
13502 // Now, resize the subframe twice from the main frame and send it a
13503 // postMessage. The postMessage handler should see the second updated size.
13504 int width = 0;
13505 EXPECT_TRUE(ExecuteScriptAndExtractInt(root, R"(
13506 var f = document.querySelector('iframe');
13507 f.width = 500;
13508 f.offsetTop; // force layout; this sends a resize IPC for width of 500.
13509 f.width = 700;
13510 f.offsetTop; // force layout; this sends a resize IPC for width of 700.
13511 f.contentWindow.postMessage('foo', '*');)", &width));
13512 EXPECT_EQ(width, 700);
13513}
13514
Alex Moshchukaefae1b12018-05-30 20:02:5713515class SitePerProcessAndProcessPerSiteBrowserTest
13516 : public SitePerProcessBrowserTest {
13517 public:
13518 SitePerProcessAndProcessPerSiteBrowserTest() {}
13519
13520 protected:
13521 void SetUpCommandLine(base::CommandLine* command_line) override {
13522 SitePerProcessBrowserTest::SetUpCommandLine(command_line);
13523 command_line->AppendSwitch(switches::kProcessPerSite);
13524 }
13525
13526 private:
13527 DISALLOW_COPY_AND_ASSIGN(SitePerProcessAndProcessPerSiteBrowserTest);
13528};
13529
13530// Verify that when --site-per-process is combined with --process-per-site, a
13531// cross-site, browser-initiated navigation with a generated page transition
13532// does not stay in the old SiteInstance. See https://crbug.com/825411.
13533IN_PROC_BROWSER_TEST_F(SitePerProcessAndProcessPerSiteBrowserTest,
13534 GeneratedTransitionsSwapProcesses) {
13535 EXPECT_TRUE(NavigateToURL(
13536 shell(), embedded_test_server()->GetURL("foo.com", "/title1.html")));
13537 scoped_refptr<SiteInstance> foo_site_instance(
13538 web_contents()->GetSiteInstance());
13539
13540 // Navigate cross-site via a generated transition. This would normally
13541 // happen for search queries.
13542 TestNavigationObserver observer(web_contents());
13543 NavigationController::LoadURLParams params(
13544 embedded_test_server()->GetURL("bar.com", "/title2.html"));
13545 params.transition_type = ui::PAGE_TRANSITION_GENERATED;
13546 web_contents()->GetController().LoadURLWithParams(params);
13547 observer.Wait();
13548
13549 // Ensure the original SiteInstance wasn't reused.
13550 EXPECT_NE(foo_site_instance, web_contents()->GetSiteInstance());
13551
13552 // Ensure the new page can access cookies without getting killed.
13553 EXPECT_TRUE(ExecuteScript(web_contents(), "document.cookie = 'foo=bar';"));
13554 std::string cookie;
13555 EXPECT_TRUE(ExecuteScriptAndExtractString(
13556 web_contents(), "window.domAutomationController.send(document.cookie);",
13557 &cookie));
13558 EXPECT_EQ("foo=bar", cookie);
13559}
13560
Alex Moshchuka5f40232018-06-04 19:25:5313561namespace {
13562
13563// Helper for waiting until next same-document navigation commits in
13564// |web_contents|.
13565class SameDocumentCommitObserver : public WebContentsObserver {
13566 public:
Lukasz Anforowicz5a92f052018-08-15 20:38:4713567 explicit SameDocumentCommitObserver(WebContents* web_contents)
13568 : WebContentsObserver(web_contents) {
Alex Moshchuka5f40232018-06-04 19:25:5313569 EXPECT_TRUE(web_contents);
13570 }
13571
Lukasz Anforowicz5a92f052018-08-15 20:38:4713572 void Wait() { run_loop_.Run(); }
Alex Moshchuka5f40232018-06-04 19:25:5313573
13574 const GURL& last_committed_url() { return last_committed_url_; }
13575
13576 private:
13577 void DidFinishNavigation(NavigationHandle* navigation_handle) override {
13578 if (navigation_handle->IsSameDocument()) {
13579 last_committed_url_ = navigation_handle->GetURL();
Lukasz Anforowicz5a92f052018-08-15 20:38:4713580 run_loop_.Quit();
Alex Moshchuka5f40232018-06-04 19:25:5313581 }
13582 }
13583
13584 GURL last_committed_url_;
Lukasz Anforowicz5a92f052018-08-15 20:38:4713585 base::RunLoop run_loop_;
Alex Moshchuka5f40232018-06-04 19:25:5313586
13587 DISALLOW_COPY_AND_ASSIGN(SameDocumentCommitObserver);
13588};
13589
13590} // namespace
13591
13592// Ensure that a same-document navigation does not cancel an ongoing
13593// cross-process navigation. See https://crbug.com/825677.
13594IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
13595 ReplaceStateDoesNotCancelCrossSiteNavigation) {
13596 GURL url(embedded_test_server()->GetURL("a.com", "/title1.html"));
13597 EXPECT_TRUE(NavigateToURL(shell(), url));
13598 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
13599
13600 // Give the page a beforeunload handler that does a replaceState. Do this
13601 // from setTimeout so that the navigation that triggers beforeunload is
13602 // already started when the replaceState happens.
13603 EXPECT_TRUE(ExecuteScript(root,
13604 "window.onbeforeunload = function (e) {"
13605 " setTimeout(() => {"
13606 " history.replaceState({}, 'footitle', 'foo');"
13607 " }, 0);"
13608 "};\n"));
13609
13610 GURL url2 = embedded_test_server()->GetURL("b.com", "/title1.html");
13611 TestNavigationManager cross_site_navigation(web_contents(), url2);
13612 SameDocumentCommitObserver replace_state_observer(web_contents());
13613
13614 // Start a cross-site navigation. Using a renderer-initiated navigation
13615 // rather than a browser-initiated one is important here, since
13616 // https://crbug.com/825677 was triggered only when replaceState ran while
13617 // having a user gesture, which will be the case here since ExecuteScript
13618 // runs with a user gesture.
danakj824a7ff2019-02-07 20:34:0213619 EXPECT_TRUE(ExecuteScript(root, JsReplace("location.href = $1", url2)));
Alex Moshchuka5f40232018-06-04 19:25:5313620 EXPECT_TRUE(cross_site_navigation.WaitForRequestStart());
13621
13622 // Now wait for the replaceState to commit while the cross-process navigation
13623 // is paused.
13624 replace_state_observer.Wait();
13625 GURL replace_state_url = embedded_test_server()->GetURL("a.com", "/foo");
13626 EXPECT_EQ(replace_state_url, replace_state_observer.last_committed_url());
13627
13628 // The cross-process navigation should not be canceled after the
13629 // replaceState.
13630 ASSERT_TRUE(root->IsLoading());
13631 ASSERT_TRUE(root->navigation_request());
13632
13633 // Resume and finish the cross-process navigation.
13634 cross_site_navigation.ResumeNavigation();
13635 cross_site_navigation.WaitForNavigationFinished();
13636 EXPECT_TRUE(cross_site_navigation.was_successful());
13637 EXPECT_EQ(url2, web_contents()->GetLastCommittedURL());
13638}
13639
Alex Moshchukade42bd2018-06-04 23:37:1813640// Test that a pending frame policy, such as an updated sandbox attribute, does
13641// not take effect after a same-document navigation. See
13642// https://crbug.com/849311.
13643IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
13644 SameDocumentNavigationDoesNotCommitPendingFramePolicy) {
13645 GURL main_url(embedded_test_server()->GetURL(
13646 "a.com", "/cross_site_iframe_factory.html?a(b)"));
13647 EXPECT_TRUE(NavigateToURL(shell(), main_url));
13648 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
13649 FrameTreeNode* subframe = root->child_at(0);
13650
13651 // The subframe should not be sandboxed.
13652 EXPECT_EQ(blink::WebSandboxFlags::kNone,
13653 subframe->pending_frame_policy().sandbox_flags);
13654 EXPECT_EQ(blink::WebSandboxFlags::kNone,
13655 subframe->effective_frame_policy().sandbox_flags);
13656
13657 // Set the "sandbox" attribute on the subframe; pending policy should update.
13658 EXPECT_TRUE(ExecuteScript(
13659 root, "document.querySelector('iframe').sandbox = 'allow-scripts';"));
13660 // "allow-scripts" resets both SandboxFlags::Scripts and
13661 // SandboxFlags::AutomaticFeatures bits per blink::ParseSandboxPolicy().
13662 blink::WebSandboxFlags expected_flags =
13663 blink::WebSandboxFlags::kAll & ~blink::WebSandboxFlags::kScripts &
13664 ~blink::WebSandboxFlags::kAutomaticFeatures;
13665 EXPECT_EQ(expected_flags, subframe->pending_frame_policy().sandbox_flags);
13666 EXPECT_EQ(blink::WebSandboxFlags::kNone,
13667 subframe->effective_frame_policy().sandbox_flags);
13668
13669 // Commit a same-document navigation with replaceState. The new sandbox
13670 // flags should still be pending but not effective.
13671 SameDocumentCommitObserver replace_state_observer(web_contents());
13672 EXPECT_TRUE(
13673 ExecuteScript(subframe, "history.replaceState({}, 'footitle', 'foo');"));
13674 replace_state_observer.Wait();
13675
13676 EXPECT_EQ(expected_flags, subframe->pending_frame_policy().sandbox_flags);
13677 EXPECT_EQ(blink::WebSandboxFlags::kNone,
13678 subframe->effective_frame_policy().sandbox_flags);
13679
13680 // Also try a same-document navigation to a fragment, which also shouldn't
13681 // commit the pending sandbox flags.
13682 GURL fragment_url = GURL(subframe->current_url().spec() + "#foo");
13683 {
13684 SameDocumentCommitObserver fragment_observer(web_contents());
danakj824a7ff2019-02-07 20:34:0213685 EXPECT_TRUE(
13686 ExecuteScript(subframe, JsReplace("location.href=$1", fragment_url)));
Alex Moshchukade42bd2018-06-04 23:37:1813687 fragment_observer.Wait();
13688 EXPECT_EQ(fragment_url, subframe->current_url());
13689 }
13690
13691 EXPECT_EQ(expected_flags, subframe->pending_frame_policy().sandbox_flags);
13692 EXPECT_EQ(blink::WebSandboxFlags::kNone,
13693 subframe->effective_frame_policy().sandbox_flags);
13694}
13695
Alex Moshchukb1f87482018-07-19 01:51:5113696// Ensure that when two cross-site frames have subframes with unique origins,
13697// and those subframes create blob URLs and navigate to them, the blob URLs end
13698// up in different processes. See https://crbug.com/863623.
13699IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
13700 TwoBlobURLsWithNullOriginDontShareProcess) {
13701 GURL main_url(embedded_test_server()->GetURL(
13702 "a.com", "/navigation_controller/page_with_data_iframe.html"));
13703 EXPECT_TRUE(NavigateToURL(shell(), main_url));
13704 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
13705 FrameTreeNode* subframe = root->child_at(0);
13706
13707 // Create a blob URL in the subframe, and navigate to it.
13708 TestNavigationObserver observer(shell()->web_contents());
13709 std::string blob_script =
13710 "var blob = new Blob(['foo'], {type : 'text/html'});"
13711 "var url = URL.createObjectURL(blob);"
13712 "location = url;";
13713 EXPECT_TRUE(ExecuteScript(subframe, blob_script));
13714 observer.Wait();
13715 RenderFrameHostImpl* subframe_rfh = subframe->current_frame_host();
13716 EXPECT_TRUE(subframe_rfh->GetLastCommittedURL().SchemeIsBlob());
13717
13718 // Open a cross-site popup and repeat these steps.
13719 GURL popup_url(embedded_test_server()->GetURL(
13720 "b.com", "/navigation_controller/page_with_data_iframe.html"));
13721 Shell* new_shell = OpenPopup(root, popup_url, "");
13722 FrameTreeNode* popup_root =
13723 static_cast<WebContentsImpl*>(new_shell->web_contents())
13724 ->GetFrameTree()
13725 ->root();
13726 FrameTreeNode* popup_subframe = popup_root->child_at(0);
13727
13728 TestNavigationObserver popup_observer(new_shell->web_contents());
13729 EXPECT_TRUE(ExecuteScript(popup_subframe, blob_script));
13730 popup_observer.Wait();
13731 RenderFrameHostImpl* popup_subframe_rfh =
13732 popup_subframe->current_frame_host();
13733 EXPECT_TRUE(popup_subframe_rfh->GetLastCommittedURL().SchemeIsBlob());
13734
13735 // Ensure that the two blob subframes don't share a process or SiteInstance.
13736 EXPECT_NE(subframe->current_frame_host()->GetSiteInstance(),
13737 popup_subframe->current_frame_host()->GetSiteInstance());
13738 EXPECT_NE(
13739 subframe->current_frame_host()->GetSiteInstance()->GetProcess(),
13740 popup_subframe->current_frame_host()->GetSiteInstance()->GetProcess());
13741 EXPECT_NE(
13742 subframe->current_frame_host()->GetSiteInstance()->GetSiteURL(),
13743 popup_subframe->current_frame_host()->GetSiteInstance()->GetSiteURL());
13744}
13745
Alex Moshchukac99cce2018-08-02 04:48:2313746// Ensure that when a process is about to be destroyed after the last active
13747// frame in it goes away, an attempt to reuse a proxy in that process doesn't
13748// result in a crash. See https://crbug.com/794625.
13749IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
13750 RenderFrameProxyNotRecreatedDuringProcessShutdown) {
13751 GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
13752 EXPECT_TRUE(NavigateToURL(shell(), main_url));
13753 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
13754
13755 GURL popup_url(embedded_test_server()->GetURL(
13756 "b.com", "/title1.html"));
13757 Shell* new_shell = OpenPopup(root, popup_url, "foo");
13758 FrameTreeNode* popup_root =
13759 static_cast<WebContentsImpl*>(new_shell->web_contents())
13760 ->GetFrameTree()
13761 ->root();
13762 auto* rfh = popup_root->current_frame_host();
13763
13764 // Disable the swapout timer to prevent flakiness.
13765 rfh->DisableSwapOutTimerForTesting();
13766
13767 // This will be used to monitor that b.com process exits cleanly.
13768 RenderProcessHostWatcher b_process_observer(
13769 popup_root->current_frame_host()->GetProcess(),
13770 RenderProcessHostWatcher::WATCH_FOR_HOST_DESTRUCTION);
13771
13772 // In the first tab, install a postMessage handler to navigate the popup to a
13773 // hung b.com URL once the first message is received.
13774 GURL hung_b_url(embedded_test_server()->GetURL("b.com", "/hung"));
13775 TestNavigationManager manager(new_shell->web_contents(), hung_b_url);
danakj824a7ff2019-02-07 20:34:0213776 EXPECT_TRUE(ExecuteScript(shell(), JsReplace(R"(
Alex Moshchukac99cce2018-08-02 04:48:2313777 window.done = false;
13778 window.onmessage = () => {
13779 if (!window.done) {
danakj824a7ff2019-02-07 20:34:0213780 window.open($1, 'foo');
Alex Moshchukac99cce2018-08-02 04:48:2313781 window.done = true;
13782 }
danakj824a7ff2019-02-07 20:34:0213783 };)",
13784 hung_b_url)));
Alex Moshchukac99cce2018-08-02 04:48:2313785
13786 // In the popup, install an unload handler to send a lot of postMessages to
13787 // the opener. This keeps the MessageLoop in the b.com process busy after
13788 // navigating away from the current document. In https://crbug.com/794625,
13789 // this was needed so that a subsequent IPC to recreate a proxy arrives
13790 // before the process fully shuts down.
13791 EXPECT_TRUE(ExecuteScript(new_shell, R"(
13792 window.onunload = () => {
13793 for (var i=0; i<10000; i++)
13794 opener.postMessage('hi','*');
13795 })"));
13796
13797 // Navigate popup to a.com. This swaps out the last active frame in the
13798 // b.com process, and hence initiates process shutdown.
13799 TestFrameNavigationObserver commit_observer(popup_root);
13800 GURL another_a_url(embedded_test_server()->GetURL("a.com", "/title3.html"));
danakj824a7ff2019-02-07 20:34:0213801 EXPECT_TRUE(
13802 ExecuteScript(new_shell, JsReplace("location = $1", another_a_url)));
Alex Moshchukac99cce2018-08-02 04:48:2313803 commit_observer.WaitForCommit();
13804
13805 // At this point, popup's original RFH is pending deletion.
13806 EXPECT_FALSE(rfh->is_active());
13807
13808 // When the opener receives a postMessage from the popup's unload handler, it
13809 // should start a navigation back to b.com. Wait for it. This navigation
13810 // creates a speculative RFH which reuses the proxy that was created as part
13811 // of swapping out from |popup_url| to |another_a_url|.
13812 EXPECT_TRUE(manager.WaitForRequestStart());
13813
13814 // Cancel the started navigation (to /hung) in the popup and make sure the
13815 // b.com renderer process exits cleanly without a crash. In
13816 // https://crbug.com/794625, the crash was caused by trying to recreate the
13817 // reused proxy, which had been incorrectly set as non-live.
13818 popup_root->ResetNavigationRequest(false, false);
13819 b_process_observer.Wait();
13820 EXPECT_TRUE(b_process_observer.did_exit_normally());
13821}
13822
Lukasz Anforowicz5a92f052018-08-15 20:38:4713823IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
13824 CommitTimeoutForHungRenderer) {
13825 // Navigate first tab to a.com.
Lukasz Anforowicz4f0593d2018-09-21 18:33:5413826 GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
13827 EXPECT_TRUE(NavigateToURL(shell(), a_url));
Lukasz Anforowicz5a92f052018-08-15 20:38:4713828 RenderProcessHost* a_process =
13829 shell()->web_contents()->GetMainFrame()->GetProcess();
13830
Lukasz Anforowicz4f0593d2018-09-21 18:33:5413831 // Open b.com in a second tab. Using a renderer-initiated navigation is
13832 // important to leave a.com and b.com SiteInstances in the same
Lukasz Anforowicz5a92f052018-08-15 20:38:4713833 // BrowsingInstance (so the b.com -> a.com navigation in the next test step
13834 // will reuse the process associated with the first a.com tab).
Lukasz Anforowicz4f0593d2018-09-21 18:33:5413835 GURL b_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
13836 Shell* new_shell = OpenPopup(shell()->web_contents(), b_url, "newtab");
13837 WebContents* new_contents = new_shell->web_contents();
13838 EXPECT_TRUE(WaitForLoadStop(new_contents));
13839 RenderProcessHost* b_process = new_contents->GetMainFrame()->GetProcess();
Lukasz Anforowicz5a92f052018-08-15 20:38:4713840 EXPECT_NE(a_process, b_process);
13841
13842 // Hang the first tab's renderer.
13843 const char* kHungScript = "setTimeout(function() { for (;;) {}; }, 0);";
13844 EXPECT_TRUE(ExecuteScript(shell()->web_contents(), kHungScript));
13845
13846 // Attempt to navigate the second tab to a.com. This will attempt to reuse
13847 // the hung process.
Mohamed Abdelhalim33b47f62019-05-10 13:03:0413848 NavigationHandleImpl::SetCommitTimeoutForTesting(
Lukasz Anforowicz5a92f052018-08-15 20:38:4713849 base::TimeDelta::FromMilliseconds(100));
Lukasz Anforowicz4f0593d2018-09-21 18:33:5413850 GURL hung_url(embedded_test_server()->GetURL("a.com", "/title3.html"));
13851 UnresponsiveRendererObserver unresponsive_renderer_observer(new_contents);
13852 EXPECT_TRUE(
13853 ExecJs(new_contents, JsReplace("window.location = $1", hung_url)));
Lukasz Anforowicz5a92f052018-08-15 20:38:4713854
13855 // Verify that we will be notified about the unresponsive renderer. Before
13856 // changes in https://crrev.com/c/1089797, the test would hang here forever.
13857 RenderProcessHost* hung_process = unresponsive_renderer_observer.Wait();
13858 EXPECT_EQ(hung_process, a_process);
13859
13860 // Reset the timeout.
Mohamed Abdelhalim33b47f62019-05-10 13:03:0413861 NavigationHandleImpl::SetCommitTimeoutForTesting(base::TimeDelta());
Lukasz Anforowicz5a92f052018-08-15 20:38:4713862}
13863
Lukasz Anforowicz4f0593d2018-09-21 18:33:5413864// This is a regression test for https://crbug.com/881812 which complained that
13865// the hung renderer dialog used to undesirably show up for background tabs
13866// (typically during session restore when many navigations would be happening in
13867// backgrounded processes).
13868IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
Avi Drissmanc3736ed62018-10-31 22:07:0513869 NoCommitTimeoutForInvisibleWebContents) {
Lukasz Anforowicz4f0593d2018-09-21 18:33:5413870 // Navigate first tab to a.com.
13871 GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
13872 EXPECT_TRUE(NavigateToURL(shell(), a_url));
13873 RenderProcessHost* a_process =
13874 shell()->web_contents()->GetMainFrame()->GetProcess();
13875
13876 // Open b.com in a second tab. Using a renderer-initiated navigation is
13877 // important to leave a.com and b.com SiteInstances in the same
13878 // BrowsingInstance (so the b.com -> a.com navigation in the next test step
13879 // will reuse the process associated with the first a.com tab).
13880 GURL b_url(embedded_test_server()->GetURL("b.com", "/title2.html"));
13881 Shell* new_shell = OpenPopup(shell()->web_contents(), b_url, "newtab");
13882 WebContents* new_contents = new_shell->web_contents();
13883 EXPECT_TRUE(WaitForLoadStop(new_contents));
13884 RenderProcessHost* b_process = new_contents->GetMainFrame()->GetProcess();
13885 EXPECT_NE(a_process, b_process);
13886
13887 // Hang the first tab's renderer.
13888 const char* kHungScript = "setTimeout(function() { for (;;) {}; }, 0);";
13889 EXPECT_TRUE(ExecuteScript(shell()->web_contents(), kHungScript));
13890
13891 // Hide the second tab. This should prevent reporting of hangs in this tab
13892 // (see https://crbug.com/881812).
13893 new_contents->WasHidden();
13894 EXPECT_EQ(Visibility::HIDDEN, new_contents->GetVisibility());
13895
13896 // Attempt to navigate the second tab to a.com. This will attempt to reuse
13897 // the hung process.
13898 base::TimeDelta kTimeout = base::TimeDelta::FromMilliseconds(100);
Mohamed Abdelhalim33b47f62019-05-10 13:03:0413899 NavigationHandleImpl::SetCommitTimeoutForTesting(kTimeout);
Lukasz Anforowicz4f0593d2018-09-21 18:33:5413900 GURL hung_url(embedded_test_server()->GetURL("a.com", "/title3.html"));
13901 UnresponsiveRendererObserver unresponsive_renderer_observer(new_contents);
13902 EXPECT_TRUE(
13903 ExecJs(new_contents, JsReplace("window.location = $1", hung_url)));
13904
13905 // Verify that we will not be notified about the unresponsive renderer.
13906 // Before changes in https://crrev.com/c/1089797, the test would get notified
13907 // and therefore |hung_process| would be non-null.
13908 RenderProcessHost* hung_process =
13909 unresponsive_renderer_observer.Wait(kTimeout * 10);
13910 EXPECT_FALSE(hung_process);
13911
13912 // Reset the timeout.
Mohamed Abdelhalim33b47f62019-05-10 13:03:0413913 NavigationHandleImpl::SetCommitTimeoutForTesting(base::TimeDelta());
Lukasz Anforowicz4f0593d2018-09-21 18:33:5413914}
13915
Lucas Furukawa Gadanif9ea4fc2018-08-03 23:57:1313916// Tests that an inner WebContents will reattach to its outer WebContents after
13917// a navigation that causes a process swap.
13918IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest, ProcessSwapOnInnerContents) {
13919 GURL main_url(embedded_test_server()->GetURL(
13920 "a.com", "/cross_site_iframe_factory.html?a(a)"));
13921 EXPECT_TRUE(NavigateToURL(shell(), main_url));
13922
13923 FrameTreeNode* child_frame =
13924 web_contents()->GetFrameTree()->root()->child_at(0);
13925 WebContentsImpl* inner_contents =
13926 static_cast<WebContentsImpl*>(CreateAndAttachInnerContents(
13927 ToRenderFrameHost(child_frame).render_frame_host()));
13928 FrameTreeNode* inner_contents_root = inner_contents->GetFrameTree()->root();
13929 RenderFrameProxyHost* outer_proxy =
13930 inner_contents_root->render_manager()->GetProxyToOuterDelegate();
13931 CrossProcessFrameConnector* outer_connector =
13932 outer_proxy->cross_process_frame_connector();
13933 EXPECT_NE(nullptr, outer_connector->get_view_for_testing());
13934
13935 GURL a_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
13936 NavigateFrameToURL(inner_contents_root, a_url);
13937 SiteInstance* a_site_instance =
13938 inner_contents->GetMainFrame()->GetSiteInstance();
13939 RenderProcessHost* a_process = a_site_instance->GetProcess();
13940 RenderWidgetHostViewChildFrame* a_view =
13941 outer_connector->get_view_for_testing();
13942
13943 GURL b_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
13944 NavigateFrameToURL(inner_contents_root, b_url);
13945 SiteInstance* b_site_instance =
13946 inner_contents->GetMainFrame()->GetSiteInstance();
13947 RenderProcessHost* b_process = b_site_instance->GetProcess();
13948 RenderWidgetHostViewChildFrame* b_view =
13949 outer_connector->get_view_for_testing();
13950
13951 // Ensure that the SiteInstances have changed, we've completed a process swap
13952 // and reattached the inner WebContents creating a new RenderWidgetHostView.
13953 EXPECT_NE(a_site_instance, b_site_instance);
13954 EXPECT_NE(a_process, b_process);
13955 EXPECT_NE(nullptr, a_view);
13956 EXPECT_NE(nullptr, b_view);
13957 EXPECT_NE(a_view, b_view);
13958}
13959
Alex Moshchuk5f8671e2018-10-19 02:10:1113960// Check that a web frame can't navigate a remote subframe to a file: URL. The
13961// frame should stay at the old URL, and the navigation attempt should produce
13962// a console error message. See https://crbug.com/894399.
13963IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
13964 FileURLBlockedWithConsoleErrorInRemoteFrameNavigation) {
13965 GURL main_url(embedded_test_server()->GetURL(
13966 "a.com", "/cross_site_iframe_factory.html?a(b)"));
13967 EXPECT_TRUE(NavigateToURL(shell(), main_url));
13968
13969 FrameTreeNode* child = web_contents()->GetFrameTree()->root()->child_at(0);
13970 GURL original_frame_url(child->current_frame_host()->GetLastCommittedURL());
13971 EXPECT_EQ("b.com", original_frame_url.host());
13972
13973 ConsoleObserverDelegate console_delegate(
13974 web_contents(), "Not allowed to load local resource: file:*");
13975 web_contents()->SetDelegate(&console_delegate);
13976
13977 GURL file_url("file:///");
13978 EXPECT_TRUE(
13979 ExecJs(web_contents(),
13980 JsReplace("document.querySelector('iframe').src = $1", file_url)));
13981 console_delegate.Wait();
13982
13983 // The iframe should've stayed at the original URL.
13984 EXPECT_EQ(original_frame_url,
13985 child->current_frame_host()->GetLastCommittedURL());
13986}
13987
W. James MacLeand973a55b2018-11-29 21:39:1313988// Touchscreen DoubleTapZoom is only supported on Android & ChromeOS at present.
13989#if defined(OS_CHROMEOS) || defined(OS_ANDROID)
13990namespace {
13991
13992class SitePerProcessDoubleTapZoomBrowserTest
13993 : public SitePerProcessBrowserTest {
13994 public:
13995 SitePerProcessDoubleTapZoomBrowserTest() {}
13996
13997 protected:
13998 void SetUpCommandLine(base::CommandLine* command_line) override {
13999 SitePerProcessBrowserTest::SetUpCommandLine(command_line);
14000 feature_list_.InitAndEnableFeature(features::kEnableVizHitTestDrawQuad);
14001 }
14002
14003 base::test::ScopedFeatureList feature_list_;
14004};
14005
14006void EnableDoubleTapZoomInRenderView(FrameTreeNode* node) {
14007 content::RenderViewHost* rvh =
14008 node->current_frame_host()->GetRenderViewHost();
14009 content::WebPreferences web_prefs = rvh->GetWebkitPreferences();
14010 if (web_prefs.double_tap_to_zoom_enabled)
14011 return;
14012 web_prefs.double_tap_to_zoom_enabled = true;
14013 rvh->UpdateWebkitPreferences(web_prefs);
14014}
14015
14016} // namespace
14017
14018IN_PROC_BROWSER_TEST_F(SitePerProcessDoubleTapZoomBrowserTest,
14019 TouchscreenAnimateDoubleTapZoomInOOPIF) {
14020 GURL main_url(embedded_test_server()->GetURL(
14021 "a.com", "/cross_site_iframe_factory.html?a(b)"));
14022 EXPECT_TRUE(NavigateToURL(shell(), main_url));
14023
14024 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
14025 ASSERT_EQ(1u, root->child_count());
14026 FrameTreeNode* child_b = root->child_at(0);
14027 ASSERT_TRUE(child_b);
14028
14029 // Enable double-tap zoom. This must be done separately for the main frame and
14030 // for the oopif frame since RenderViewHost::UpdateWebkitPreferences() only
14031 // sends the IPC to its own RenderView.
14032 EnableDoubleTapZoomInRenderView(root);
14033 EnableDoubleTapZoomInRenderView(child_b);
14034
14035 RenderFrameSubmissionObserver observer_a(root);
14036 // We need to observe a root frame submission to pick up the initial page
14037 // scale factor.
14038 observer_a.WaitForAnyFrameSubmission();
14039 float original_page_scale =
14040 observer_a.LastRenderFrameMetadata().page_scale_factor;
14041
14042 // Must do this before it's safe to use the coordinate transform functions.
14043 WaitForHitTestDataOrChildSurfaceReady(child_b->current_frame_host());
14044
14045 // Select a tap point inside the OOPIF.
14046 gfx::PointF tap_position =
14047 child_b->current_frame_host()
14048 ->GetRenderWidgetHost()
14049 ->GetView()
14050 ->TransformPointToRootCoordSpaceF(gfx::PointF(10, 10));
14051
14052 // Generate a double-tap.
14053 std::string actions_template = R"HTML(
14054 [{
14055 "source" : "touch",
14056 "actions" : [
14057 { "name": "pointerDown", "x": %f, "y": %f},
14058 { "name": "pointerUp"},
Lan Weia80bbf22019-02-22 02:08:1514059 { "name": "pause", "duration": 50 },
W. James MacLeand973a55b2018-11-29 21:39:1314060 { "name": "pointerDown", "x": %f, "y": %f},
14061 { "name": "pointerUp"}
14062 ]
14063 }]
14064 )HTML";
14065 std::string double_tap_actions_json =
14066 base::StringPrintf(actions_template.c_str(), tap_position.x(),
14067 tap_position.y(), tap_position.x(), tap_position.y());
14068 base::JSONReader json_reader;
Avi Drissman57e5d7df2019-03-21 19:12:5314069 base::Optional<base::Value> params =
14070 json_reader.ReadToValue(double_tap_actions_json);
14071 ASSERT_TRUE(params.has_value()) << json_reader.GetErrorMessage();
14072 ActionsParser actions_parser(std::move(params.value()));
W. James MacLeand973a55b2018-11-29 21:39:1314073
14074 ASSERT_TRUE(actions_parser.ParsePointerActionSequence());
14075 auto synthetic_gesture_doubletap =
14076 SyntheticGesture::Create(actions_parser.gesture_params());
14077
14078 // Queue the event and wait for it to be acked.
14079 InputEventAckWaiter ack_waiter(
14080 child_b->current_frame_host()->GetRenderWidgetHost(),
14081 blink::WebInputEvent::kGestureDoubleTap);
14082 auto* host = static_cast<RenderWidgetHostImpl*>(
14083 root->current_frame_host()->GetRenderWidgetHost());
14084 host->QueueSyntheticGesture(
14085 std::move(synthetic_gesture_doubletap),
14086 base::BindOnce([](SyntheticGesture::Result result) {
14087 EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
14088 }));
14089 // Waiting for the ack on the child frame ensures the event actually routed
14090 // through the oopif.
14091 ack_waiter.Wait();
14092
14093 // Wait for page scale to change. We'll assume the OOPIF is scaled up by
14094 // at least 10%.
14095 float target_scale = 1.1f * original_page_scale;
14096 float new_page_scale = original_page_scale;
14097 do {
14098 observer_a.WaitForAnyFrameSubmission();
14099 new_page_scale = observer_a.LastRenderFrameMetadata().page_scale_factor;
14100 } while (new_page_scale < target_scale);
14101}
14102#endif // defined(OS_CHROMEOS) || defined(OS_ANDROID)
14103
Ehsan Karamad192a8da2018-10-21 03:48:0814104class CrossProcessNavigationObjectElementTest
14105 : public SitePerProcessBrowserTest,
14106 public testing::WithParamInterface<
14107 std::tuple<std::string, std::string, std::string>> {};
14108
14109// This test verifies the correctness of rendering fallback in <object> when the
14110// a cross-origin navigation leads to a 404 error. Assuming the page's origin
14111// is "a.com", the test cases are:
14112// 1- Navigating an <object> from "a.com" to invalid "b.com" resource. In this
14113// case the load fails for a provisional frame and at that time there is no
14114// proxy to parent.
14115// 2- Navigating an <object> from "b.com" to invalid "b.com". Since navigation
14116// is not cross-origin the failure happens for a non-provisional frame.
14117// 3- Navigation an <object> from "b.com" to invalid "c.com". The load fails for
14118// a provisional frame, and at that time there is a proxy to parent.
14119IN_PROC_BROWSER_TEST_P(CrossProcessNavigationObjectElementTest, FallbackShown) {
14120 const GURL main_url = embedded_test_server()->GetURL(
14121 base::StringPrintf("%s.com", std::get<0>(GetParam()).c_str()),
14122 "/page_with_object_fallback.html");
14123 const GURL object_valid_url = embedded_test_server()->GetURL(
14124 base::StringPrintf("%s.com", std::get<1>(GetParam()).c_str()),
14125 "/title1.html");
14126 const GURL object_invalid_url = embedded_test_server()->GetURL(
14127 base::StringPrintf("%s.com", std::get<2>(GetParam()).c_str()),
14128 "/does-not-exist-throws-404.html");
14129
14130 ASSERT_TRUE(NavigateToURL(shell(), main_url));
14131
14132 // Load the contents of <object> (first navigation which is to a valid
14133 // existing resource) and wait for 'load' event on <object>.
14134 std::string result;
14135 ASSERT_TRUE(ExecuteScriptAndExtractString(
danakj824a7ff2019-02-07 20:34:0214136 web_contents(), JsReplace("setUrl($1, true);", object_valid_url),
Ehsan Karamad192a8da2018-10-21 03:48:0814137 &result));
14138 ASSERT_EQ("OBJECT_LOAD", result);
14139
14140 // Verify fallback content is not shown.
14141 bool fallback_visible = true;
14142 ASSERT_TRUE(ExecuteScriptAndExtractBool(
14143 web_contents(), "fallbackVisible(true)", &fallback_visible));
14144 ASSERT_FALSE(fallback_visible);
14145
14146 // Navigate the <object>'s frame to invalid origin. Make sure we do not report
14147 // the 'load' event (the 404 content loads inside the <object>'s frame and the
14148 // 'load' event might fire before fallback is detected).
14149 fallback_visible = false;
danakj824a7ff2019-02-07 20:34:0214150 ASSERT_TRUE(
14151 ExecuteScriptAndExtractBool(web_contents(),
14152 JsReplace("setUrl($1, false);"
14153 "notifyWhenFallbackShown();",
14154 object_invalid_url),
14155 &fallback_visible));
Ehsan Karamad192a8da2018-10-21 03:48:0814156 ASSERT_TRUE(fallback_visible);
14157}
14158
Victor Costanaa7b61f2019-02-13 07:57:5714159INSTANTIATE_TEST_SUITE_P(SitePerProcess,
14160 CrossProcessNavigationObjectElementTest,
14161 testing::Values(std::make_tuple("a", "a", "b"),
14162 std::make_tuple("a", "b", "b"),
14163 std::make_tuple("a", "b", "c")));
Ehsan Karamad192a8da2018-10-21 03:48:0814164
David Bokan469e7e42018-11-05 14:50:2914165class ScrollingIntegrationTest : public SitePerProcessBrowserTest {
14166 public:
14167 ScrollingIntegrationTest() = default;
14168 ~ScrollingIntegrationTest() override = default;
14169
14170 void DoScroll(const gfx::Point& point,
14171 const gfx::Vector2d& distance,
14172 SyntheticGestureParams::GestureSourceType source) {
14173 SyntheticSmoothScrollGestureParams params;
14174 params.gesture_source_type = source;
14175 params.anchor = gfx::PointF(point);
14176 params.distances.push_back(-distance);
14177 params.precise_scrolling_deltas = true;
14178
14179 auto gesture = std::make_unique<SyntheticSmoothScrollGesture>(params);
14180
14181 // Runs until we get the SyntheticGestureCompleted callback
14182 base::RunLoop run_loop;
14183 GetRenderWidgetHostImpl()->QueueSyntheticGesture(
14184 std::move(gesture),
14185 base::BindLambdaForTesting([&](SyntheticGesture::Result result) {
14186 EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
14187 run_loop.Quit();
14188 }));
14189 run_loop.Run();
14190 }
14191
14192 double GetScrollTop() {
14193 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
14194 ->GetFrameTree()
14195 ->root();
14196 return EvalJs(root, "window.scrollY").ExtractDouble();
14197 }
14198
14199 void WaitForVerticalScroll() {
14200 RenderFrameSubmissionObserver frame_observer(shell()->web_contents());
14201 gfx::Vector2dF default_scroll_offset;
14202 while (frame_observer.LastRenderFrameMetadata()
14203 .root_scroll_offset.value_or(default_scroll_offset)
14204 .y() <= 0) {
14205 frame_observer.WaitForMetadataChange();
14206 }
14207 }
14208
14209 RenderWidgetHostImpl* GetRenderWidgetHostImpl() {
14210 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
14211 ->GetFrameTree()
14212 ->root();
14213 return root->current_frame_host()->GetRenderWidgetHost();
14214 }
14215};
14216
14217// Tests basic scrolling after navigating to a new origin works. Guards against
14218// bugs like https://crbug.com/899234 which are caused by invalid
14219// initialization due to the cross-origin provisional frame swap.
14220IN_PROC_BROWSER_TEST_F(ScrollingIntegrationTest,
14221 ScrollAfterCrossOriginNavigation) {
14222 // Navigate to the a.com domain first.
14223 GURL url_domain_a(
14224 embedded_test_server()->GetURL("a.com", "/simple_page.html"));
14225 EXPECT_TRUE(NavigateToURL(shell(), url_domain_a));
14226
14227 // Now navigate to baz.com, this should cause a cross-origin navigation which
14228 // will load into a provisional frame and then swap in as a local main frame.
14229 // This test ensures all the correct initialization takes place in the
14230 // renderer so that a basic scrolling smoke test works.
14231 GURL url_domain_b(embedded_test_server()->GetURL(
14232 "baz.com", "/scrollable_page_with_iframe.html"));
14233 EXPECT_TRUE(NavigateToURL(shell(), url_domain_b));
14234 ASSERT_TRUE(WaitForLoadStop(shell()->web_contents()));
14235
14236 {
14237 // TODO(bokan): We currently don't have a good way to know when the
14238 // compositor's scrolling layers are ready after changes on the main thread.
14239 // We wait a timeout but that's really a hack. Fixing is tracked in
14240 // https://crbug.com/897520
14241 base::RunLoop run_loop;
14242 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
14243 FROM_HERE, run_loop.QuitClosure(),
14244 base::TimeDelta::FromMilliseconds(3000));
14245 run_loop.Run();
14246 }
14247
14248 SyntheticGestureParams::GestureSourceType source;
14249
14250// TODO(bokan): Mac doesn't support touch events and for an unknown reason,
14251// Android doesn't like mouse wheel here. https://crbug.com/897520.
14252#if defined(OS_ANDROID)
14253 source = SyntheticGestureParams::TOUCH_INPUT;
14254#else
14255 source = SyntheticGestureParams::TOUCHPAD_INPUT;
14256#endif
14257
14258 // Perform the scroll (below the iframe), ensure it's correctly processed.
14259 DoScroll(gfx::Point(100, 110), gfx::Vector2d(0, 500), source);
14260 WaitForVerticalScroll();
14261 EXPECT_GT(GetScrollTop(), 0);
14262}
14263
Ehsan Karamada03e1582018-12-05 23:07:5614264#if !defined(OS_ANDROID)
14265// This test verifies that after occluding a WebContents the RAF inside a
14266// cross-process child frame is throttled.
14267IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
14268 OccludedRenderWidgetThrottlesRAF) {
14269 GURL main_url(embedded_test_server()->GetURL(
14270 "a.com", "/cross_site_iframe_factory.html?a(b)"));
14271 EXPECT_TRUE(NavigateToURL(shell(), main_url));
14272 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
14273 FrameTreeNode* subframe = root->child_at(0);
14274 GURL page_with_raf_counter =
14275 embedded_test_server()->GetURL("a.com", "/page_with_raf_counter.html");
14276 NavigateFrameToURL(subframe, page_with_raf_counter);
14277
14278 // Initially page is visible - wait some time and then ensure a good number of
14279 // rafs have been generated.
14280 auto wait_for_half_a_second = []() {
14281 base::RunLoop run_loop;
14282 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
14283 FROM_HERE, run_loop.QuitClosure(),
14284 base::TimeDelta::FromMilliseconds(500));
14285 run_loop.Run();
14286 };
14287
14288 ASSERT_TRUE(ExecuteScript(subframe, "reset_count();"));
14289 wait_for_half_a_second();
14290 int32_t default_raf_count = EvalJs(subframe, "raf_count").ExtractInt();
14291 // On a 60 fps we should expect more than 30 counts - however purely for
14292 // sanity checking and avoiding unnecessary flakes adding a comparison for a
14293 // much lower value. This verifies that we did get *some* rAFs.
14294 EXPECT_GT(default_raf_count, 5);
14295 web_contents()->WasOccluded();
14296 ASSERT_TRUE(ExecuteScript(subframe, "reset_count();"));
14297 wait_for_half_a_second();
14298 int32_t raf_count = EvalJs(subframe, "raf_count").ExtractInt();
14299 // If the frame is throttled, we should expect 0 rAFs.
14300 EXPECT_EQ(raf_count, 0);
14301 // Sanity-check: unoccluding will reverse the effect.
14302 web_contents()->WasShown();
14303 ASSERT_TRUE(ExecuteScript(subframe, "reset_count();"));
14304 wait_for_half_a_second();
14305 ASSERT_TRUE(ExecuteScriptAndExtractInt(
14306 subframe, "window.domAutomationController.send(raf_count)", &raf_count));
14307 EXPECT_GT(raf_count, 5);
14308}
14309#endif
Lukasz Anforowicz1da30d7c2018-12-21 00:44:2714310
14311// Test that a renderer locked to origin A will be terminated if it tries to
14312// commit a navigation to origin B. See also https://crbug.com/770239.
14313IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
14314 CommittedOriginIncompatibleWithOriginLock) {
14315 GURL start_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
14316 EXPECT_TRUE(NavigateToURL(shell(), start_url));
14317 FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
14318 ->GetFrameTree()
14319 ->root();
14320
14321 // Setup an URL which will never commit, allowing this test to send its own,
14322 // malformed, commit message.
14323 GURL another_url(embedded_test_server()->GetURL("b.com", "/hung"));
14324
14325 // Use LoadURL, as the test shouldn't wait for navigation commit.
14326 NavigationController& controller = shell()->web_contents()->GetController();
14327 controller.LoadURL(another_url, Referrer(), ui::PAGE_TRANSITION_LINK,
14328 std::string());
14329 EXPECT_TRUE(controller.GetPendingEntry());
14330 EXPECT_EQ(another_url, controller.GetPendingEntry()->GetURL());
Camille Lamy59025352019-05-09 11:25:5314331 NavigationRequest* navigation_request = root->navigation_request();
14332 ASSERT_TRUE(navigation_request);
Lukasz Anforowicz1da30d7c2018-12-21 00:44:2714333
14334 RenderProcessHostKillWaiter kill_waiter(
14335 root->current_frame_host()->GetProcess());
14336
Nasko Oskovfd52b6f2019-02-06 19:21:1514337 // Create commit params with the same URL as the start one, so URL checks
14338 // pass, but use a different origin than the origin lock of the process.
Lukasz Anforowicz1da30d7c2018-12-21 00:44:2714339 std::unique_ptr<FrameHostMsg_DidCommitProvisionalLoad_Params> params =
14340 std::make_unique<FrameHostMsg_DidCommitProvisionalLoad_Params>();
14341 params->nav_entry_id = 0;
14342 params->did_create_new_entry = false;
Nasko Oskovfd52b6f2019-02-06 19:21:1514343 params->url = start_url;
Lukasz Anforowicz1da30d7c2018-12-21 00:44:2714344 params->transition = ui::PAGE_TRANSITION_LINK;
14345 params->should_update_history = false;
14346 params->gesture = NavigationGestureAuto;
14347 params->method = "GET";
Nasko Oskovfd52b6f2019-02-06 19:21:1514348 params->page_state = PageState::CreateFromURL(start_url);
Camille Lamy59025352019-05-09 11:25:5314349 params->navigation_token =
14350 root->navigation_request()->commit_params().navigation_token;
Nasko Oskovfd52b6f2019-02-06 19:21:1514351
Lukasz Anforowicz1da30d7c2018-12-21 00:44:2714352 // Use an origin mismatched with the origin lock.
14353 params->origin = url::Origin::Create(another_url);
14354 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
14355 int process_id = root->current_frame_host()->GetProcess()->GetID();
14356 EXPECT_EQ("http://a.com/", policy->GetOriginLock(process_id));
14357 EXPECT_EQ(start_url.host(), policy->GetOriginLock(process_id).host());
14358 EXPECT_NE(another_url.host(), policy->GetOriginLock(process_id).host());
Nasko Oskovfd52b6f2019-02-06 19:21:1514359
Camille Lamy59025352019-05-09 11:25:5314360 // Transfer the NavigationRequest ownership to the RenderFrameHost. The test
14361 // for NavigationRequest match happens before the check of origin lock and
14362 // needs to be successful.
14363 root->TransferNavigationRequestOwnership(root->current_frame_host());
14364 root->current_frame_host()->OnCrossDocumentCommitProcessed(
14365 navigation_request, blink::mojom::CommitResult::Ok);
14366
Lukasz Anforowicz1da30d7c2018-12-21 00:44:2714367 // Simulate a commit IPC.
14368 service_manager::mojom::InterfaceProviderPtr interface_provider;
Oksana Zhuravlova8b88e572019-01-07 21:54:0014369 blink::mojom::DocumentInterfaceBrokerPtrInfo
14370 document_interface_broker_content;
14371 blink::mojom::DocumentInterfaceBrokerPtrInfo document_interface_broker_blink;
Lukasz Anforowicz1da30d7c2018-12-21 00:44:2714372 static_cast<mojom::FrameHost*>(root->current_frame_host())
Oksana Zhuravlova8b88e572019-01-07 21:54:0014373 ->DidCommitProvisionalLoad(
14374 std::move(params),
14375 mojom::DidCommitProvisionalLoadInterfaceParams::New(
14376 mojo::MakeRequest(&interface_provider),
14377 mojo::MakeRequest(&document_interface_broker_content),
14378 mojo::MakeRequest(&document_interface_broker_blink)));
Lukasz Anforowicz1da30d7c2018-12-21 00:44:2714379
14380 // When the IPC message is received and validation fails, the process is
14381 // terminated. However, the notification for that should be processed in a
14382 // separate task of the message loop, so ensure that the process is still
14383 // considered alive.
14384 EXPECT_TRUE(
14385 root->current_frame_host()->GetProcess()->IsInitializedAndNotDead());
14386
14387 EXPECT_EQ(bad_message::RFH_INVALID_ORIGIN_ON_COMMIT, kill_waiter.Wait());
14388}
14389
Ehsan Karamad830c8fc2019-01-28 18:29:2514390// This test verifies that plugin elements containing cross-process-frames do
14391// not become unresponsive during style changes. (see https://crbug.com/781880).
14392IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
14393 PluginElementResponsiveInCrossProcessNavigations) {
14394 GURL main_frame_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
14395 ASSERT_TRUE(NavigateToURL(shell(), main_frame_url));
14396 GURL cross_origin(embedded_test_server()->GetURL("b.com", "/title1.html"));
14397 std::string msg =
14398 EvalJsWithManualReply(
14399 shell(), JsReplace("var object = document.createElement('object');"
14400 "document.body.appendChild(object);"
14401 "object.data = $1;"
14402 "object.type='text/html';"
14403 "object.notify = true;"
14404 "object.onload = () => {"
14405 " if (!object.notify) return;"
14406 " object.notify = false;"
14407 " window.domAutomationController.send('done');"
14408 "};",
danakj824a7ff2019-02-07 20:34:0214409 cross_origin))
Ehsan Karamad830c8fc2019-01-28 18:29:2514410 .ExtractString();
14411 ASSERT_EQ("done", msg);
14412 // To track the frame's visibility an EmbeddedContentView is needed. The
14413 // following steps make sure the visibility is tracked properly on the browser
14414 // side.
14415 auto* frame_connector = web_contents()
14416 ->GetFrameTree()
14417 ->root()
14418 ->child_at(0)
14419 ->render_manager()
14420 ->GetProxyToParent()
14421 ->cross_process_frame_connector();
14422 ASSERT_FALSE(frame_connector->IsHidden());
14423 ASSERT_TRUE(ExecJs(
14424 shell(), "document.querySelector('object').style.display = 'none';"));
14425 while (!frame_connector->IsHidden()) {
14426 base::RunLoop run_loop;
14427 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
14428 FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
14429 run_loop.Run();
14430 }
14431 ASSERT_TRUE(ExecJs(
14432 shell(), "document.querySelector('object').style.display = 'block';"));
14433 while (frame_connector->IsHidden()) {
14434 base::RunLoop run_loop;
14435 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
14436 FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
14437 run_loop.Run();
14438 }
14439}
14440
arthursonzogni6aff2922019-02-06 12:59:5614441// Pending navigations must be canceled when a frame becomes pending deletion.
14442//
14443// 1) Initial state: A(B).
14444// 2) Navigation from B to C. The server is slow to respond.
14445// 3) Deletion of B.
14446IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
14447 NavigationCommitInIframePendingDeletionAB) {
14448 GURL url_a(embedded_test_server()->GetURL(
14449 "a.com", "/cross_site_iframe_factory.html?a(b)"));
14450 GURL url_c(embedded_test_server()->GetURL("c.com", "/hung"));
14451
14452 // 1) Initial state: A(B).
14453 EXPECT_TRUE(NavigateToURL(shell(), url_a));
14454 RenderFrameHostImpl* rfh_a = web_contents()->GetMainFrame();
14455 RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
14456
14457 // RFH B has an unload handler.
danakjcdee06c2019-02-08 18:04:0714458 auto detach_filter_b = base::MakeRefCounted<DropMessageFilter>(
14459 FrameMsgStart, FrameHostMsg_Detach::ID);
arthursonzogni6aff2922019-02-06 12:59:5614460 rfh_b->GetProcess()->AddFilter(detach_filter_b.get());
14461 EXPECT_TRUE(ExecJs(rfh_b, "onunload=function(){}"));
14462
14463 // 2) Navigation from B to C. The server is slow to respond.
14464 TestNavigationManager navigation_observer(web_contents(), url_c);
14465 EXPECT_TRUE(ExecJs(rfh_b, JsReplace("location.href=$1;", url_c)));
14466 EXPECT_TRUE(navigation_observer.WaitForRequestStart());
14467 RenderFrameHostImpl* rfh_c =
14468 rfh_b->frame_tree_node()->render_manager()->speculative_frame_host();
14469
14470 EXPECT_EQ(RenderFrameHostImpl::UnloadState::NotRun, rfh_a->unload_state_);
14471 EXPECT_EQ(RenderFrameHostImpl::UnloadState::NotRun, rfh_b->unload_state_);
14472 EXPECT_EQ(RenderFrameHostImpl::UnloadState::NotRun, rfh_c->unload_state_);
14473
14474 // 3) Deletion of B. The unload handler takes times to execute.
14475 RenderFrameDeletedObserver delete_b(rfh_b), delete_c(rfh_c);
14476 EXPECT_TRUE(
14477 ExecJs(rfh_a, JsReplace("document.querySelector('iframe').remove();")));
14478 EXPECT_FALSE(delete_b.deleted());
14479 EXPECT_TRUE(delete_c.deleted()); // The speculative RFH is deleted.
14480 EXPECT_EQ(RenderFrameHostImpl::UnloadState::NotRun, rfh_a->unload_state_);
14481 EXPECT_EQ(RenderFrameHostImpl::UnloadState::InProgress, rfh_b->unload_state_);
14482
14483 // The navigation has been canceled.
14484 navigation_observer.WaitForNavigationFinished();
14485 EXPECT_FALSE(navigation_observer.was_successful());
14486
14487 // |rfh_b| will complete its deletion at some point:
14488 EXPECT_FALSE(delete_b.deleted());
14489 rfh_b->OnDetach();
14490 EXPECT_TRUE(delete_b.deleted());
14491}
14492
14493// Pending navigations must be canceled when a frame becomes pending deletion.
14494//
14495// 1) Initial state: A(B(C)).
14496// 2) Navigation from C to D. The server is slow to respond.
14497// 3) Deletion of B.
14498IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
14499 NavigationCommitInIframePendingDeletionABC) {
14500 GURL url_a(embedded_test_server()->GetURL(
14501 "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
14502 GURL url_d(embedded_test_server()->GetURL("d.com", "/hung"));
14503
14504 // 1) Initial state: A(B(C)).
14505 EXPECT_TRUE(NavigateToURL(shell(), url_a));
14506 RenderFrameHostImpl* rfh_a = web_contents()->GetMainFrame();
14507 RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
14508 RenderFrameHostImpl* rfh_c = rfh_b->child_at(0)->current_frame_host();
14509
14510 // RFH C has an unload handler.
danakjcdee06c2019-02-08 18:04:0714511 auto detach_filter_c = base::MakeRefCounted<DropMessageFilter>(
14512 FrameMsgStart, FrameHostMsg_Detach::ID);
arthursonzogni6aff2922019-02-06 12:59:5614513 rfh_c->GetProcess()->AddFilter(detach_filter_c.get());
14514 EXPECT_TRUE(ExecJs(rfh_c, "onunload=function(){}"));
14515
14516 // 2) Navigation from C to D. The server is slow to respond.
14517 TestNavigationManager navigation_observer(web_contents(), url_d);
14518 EXPECT_TRUE(ExecJs(rfh_c, JsReplace("location.href=$1;", url_d)));
14519 EXPECT_TRUE(navigation_observer.WaitForRequestStart());
14520 RenderFrameHostImpl* rfh_d =
14521 rfh_c->frame_tree_node()->render_manager()->speculative_frame_host();
14522
14523 EXPECT_EQ(RenderFrameHostImpl::UnloadState::NotRun, rfh_a->unload_state_);
14524 EXPECT_EQ(RenderFrameHostImpl::UnloadState::NotRun, rfh_b->unload_state_);
14525 EXPECT_EQ(RenderFrameHostImpl::UnloadState::NotRun, rfh_c->unload_state_);
14526 EXPECT_EQ(RenderFrameHostImpl::UnloadState::NotRun, rfh_d->unload_state_);
14527
14528 // 3) Deletion of D. The unload handler takes times to execute.
14529 RenderFrameDeletedObserver delete_b(rfh_b), delete_c(rfh_c), delete_d(rfh_d);
14530 EXPECT_TRUE(
14531 ExecJs(rfh_a, JsReplace("document.querySelector('iframe').remove();")));
14532 EXPECT_FALSE(delete_b.deleted());
14533 EXPECT_FALSE(delete_c.deleted());
14534 EXPECT_TRUE(delete_d.deleted()); // The speculative RFH is deleted.
14535 EXPECT_EQ(RenderFrameHostImpl::UnloadState::NotRun, rfh_a->unload_state_);
14536 EXPECT_EQ(RenderFrameHostImpl::UnloadState::Completed, rfh_b->unload_state_);
14537 EXPECT_EQ(RenderFrameHostImpl::UnloadState::InProgress, rfh_c->unload_state_);
14538
14539 // The navigation has been canceled.
14540 navigation_observer.WaitForNavigationFinished();
14541 EXPECT_FALSE(navigation_observer.was_successful());
14542
14543 // |rfh_b| and |rfh_c| will complete their deletion at some point:
14544 EXPECT_FALSE(delete_b.deleted());
14545 EXPECT_FALSE(delete_c.deleted());
14546 rfh_c->OnDetach();
14547 EXPECT_TRUE(delete_b.deleted());
14548 EXPECT_TRUE(delete_c.deleted());
14549}
14550
arthursonzognid9831792019-02-11 10:24:1714551// A same document commit from the renderer process is received while the
14552// RenderFrameHost is pending deletion.
14553IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
14554 SameDocumentCommitWhilePendingDeletion) {
14555 GURL url(embedded_test_server()->GetURL(
14556 "a.com", "/cross_site_iframe_factory.html?a(b)"));
14557 EXPECT_TRUE(NavigateToURL(shell(), url));
14558 RenderFrameHostImpl* rfh_a = web_contents()->GetMainFrame();
14559 RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
14560
14561 // Frame B has a unload handler. The browser process needs to wait before
14562 // deleting it.
14563 EXPECT_TRUE(ExecJs(rfh_b, "onunload=function(){}"));
14564
14565 RenderFrameDeletedObserver deleted_observer(rfh_b);
14566 DidStartNavigationObserver did_start_navigation_observer(web_contents());
14567
14568 // Start a same-document navigation on B.
14569 ExecuteScriptAsync(rfh_b, "location.href='#fragment'");
14570
14571 // Simulate A deleting B.
14572 // It starts before receiving the same-document navigation. The detach ACK is
14573 // received after.
14574 rfh_b->DetachFromProxy();
14575 deleted_observer.WaitUntilDeleted();
14576
14577 // The navigation was ignored.
14578 EXPECT_FALSE(did_start_navigation_observer.observed());
14579}
14580
arthursonzogni03f76152019-02-12 10:35:2014581// An history navigation from the renderer process is received while the
14582// RenderFrameHost is pending deletion.
14583IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
14584 HistoryNavigationWhilePendingDeletion) {
14585 GURL url_ab(embedded_test_server()->GetURL(
14586 "a.com", "/cross_site_iframe_factory.html?a(b)"));
14587 GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html"));
14588
14589 EXPECT_TRUE(NavigateToURL(shell(), url_ab));
14590 RenderFrameHostImpl* rfh_a = web_contents()->GetMainFrame();
14591 RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
14592 NavigateFrameToURL(rfh_b->frame_tree_node(), url_c);
14593 RenderFrameHostImpl* rfh_c = rfh_a->child_at(0)->current_frame_host();
14594
14595 // Frame C has a unload handler. The browser process needs to wait before
14596 // deleting it.
14597 EXPECT_TRUE(ExecJs(rfh_c, "onunload=function(){}"));
14598
14599 RenderFrameDeletedObserver deleted_observer(rfh_c);
14600 TestNavigationManager navigation_observer(web_contents(), url_ab);
14601
14602 // History navigation on C.
14603 ExecuteScriptAsync(rfh_c, "history.back();");
14604
14605 // Simulate A deleting C.
14606 // It starts before receiving the history navigation. The detach ACK is
14607 // received after.
14608 rfh_c->DetachFromProxy();
14609 deleted_observer.WaitUntilDeleted();
14610
14611 // The NavigationController won't be able to find the subframe to navigate
14612 // since it was just detached, so it should fall back to navigating the main
14613 // frame
14614 navigation_observer.WaitForNavigationFinished();
14615 EXPECT_TRUE(navigation_observer.was_successful());
14616}
14617
arthursonzognib3a21182019-02-18 16:35:5114618// One frame navigates using window.open while it is pending deletion. The two
14619// frames lives in different processes.
14620// See https://crbug.com/932087.
14621IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
14622 OpenUrlToRemoteFramePendingDeletion) {
14623 GURL url_ab(embedded_test_server()->GetURL(
14624 "a.com", "/cross_site_iframe_factory.html?a(b)"));
14625 GURL url_c(embedded_test_server()->GetURL("c.com", "/title1.html"));
14626
14627 EXPECT_TRUE(NavigateToURL(shell(), url_ab));
14628 RenderFrameHostImpl* rfh_a = web_contents()->GetMainFrame();
14629 RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
14630
14631 // Frame B has a unload handler. The browser process needs to wait before
14632 // deleting it.
14633 EXPECT_TRUE(ExecJs(rfh_b, "onunload=function(){}"));
14634 RenderFrameDeletedObserver deleted_observer(rfh_b);
14635
14636 // window.open from A in B to url_c.
14637 DidStartNavigationObserver did_start_navigation_observer(web_contents());
14638 EXPECT_TRUE(ExecuteScript(rfh_b, "window.name = 'name';"));
14639 ExecuteScriptAsync(rfh_a, JsReplace("window.open($1, 'name');", url_c));
14640
14641 // Simulate A deleting C.
14642 // It starts before receiving the navigation. The detach ACK is
14643 // received after.
14644 rfh_b->DetachFromProxy();
14645 deleted_observer.WaitUntilDeleted();
14646
14647 EXPECT_FALSE(did_start_navigation_observer.observed());
14648}
14649
W. James MacLeanfee5b4f2019-02-07 20:20:3514650// This test verifies that when scrolling an OOPIF in a pinched-zoomed page,
14651// that the scroll-delta matches the distance between TouchStart/End as seen
14652// by the oopif, i.e. the oopif content 'sticks' to the finger during scrolling.
14653// The relation is not exact, but should be close.
14654IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
14655 ScrollOopifInPinchZoomedPage) {
14656 GURL main_url(embedded_test_server()->GetURL(
14657 "a.com", "/cross_site_iframe_factory.html?a(b)"));
14658 EXPECT_TRUE(NavigateToURL(shell(), main_url));
14659
14660 FrameTreeNode* root = web_contents()->GetFrameTree()->root();
14661 ASSERT_EQ(1u, root->child_count());
14662 FrameTreeNode* child = root->child_at(0);
14663 ASSERT_TRUE(child);
14664
14665 EXPECT_EQ(
14666 " Site A ------------ proxies for B\n"
14667 " +--Site B ------- proxies for A\n"
14668 "Where A = http://a.com/\n"
14669 " B = http://b.com/",
14670 DepictFrameTree(root));
14671
14672 // Make B scrollable. The call to document.write will erase the html inside
14673 // the OOPIF, leaving just a vertical column of 'Hello's.
14674 std::string script =
14675 "var s = '<div>Hello</div>\\n';\n"
14676 "document.write(s.repeat(200));";
14677 EXPECT_TRUE(ExecuteScript(child, script));
14678
14679 RenderFrameSubmissionObserver observer_a(root);
14680 RenderFrameSubmissionObserver observer_b(child);
14681
14682 // We need to observe a root frame submission to pick up the initial page
14683 // scale factor.
14684 observer_a.WaitForAnyFrameSubmission();
14685
14686 const float kPageScaleDelta = 2.f;
14687 // On desktop systems we expect |current_page_scale| to be 1.f, but on
14688 // Android it will typically be less than 1.f, and may take on arbitrary
14689 // values.
14690 float original_page_scale =
14691 observer_a.LastRenderFrameMetadata().page_scale_factor;
14692 float target_page_scale = original_page_scale * kPageScaleDelta;
14693
14694 SyntheticPinchGestureParams params;
14695 auto* host = static_cast<RenderWidgetHostImpl*>(
14696 root->current_frame_host()->GetRenderWidgetHost());
14697 RenderWidgetHostViewBase* root_view = host->GetView();
14698 RenderWidgetHostViewBase* child_view =
14699 static_cast<RenderWidgetHostImpl*>(
14700 child->current_frame_host()->GetRenderWidgetHost())
14701 ->GetView();
14702 gfx::Rect bounds(root_view->GetViewBounds().size());
14703 // The synthetic gesture code expects a location in root-view coordinates.
14704 params.anchor = gfx::PointF(bounds.CenterPoint().x(), 70.f);
14705 // In SyntheticPinchGestureParams, |scale_factor| is really a delta.
14706 params.scale_factor = kPageScaleDelta;
14707#if defined(OS_MACOSX)
14708 auto synthetic_pinch_gesture =
14709 std::make_unique<SyntheticTouchpadPinchGesture>(params);
14710#else
14711 auto synthetic_pinch_gesture =
14712 std::make_unique<SyntheticTouchscreenPinchGesture>(params);
14713#endif
14714
14715 // Send pinch gesture and verify we receive the ack.
14716 {
14717 InputEventAckWaiter ack_waiter(host,
14718 blink::WebInputEvent::kGesturePinchEnd);
14719 host->QueueSyntheticGesture(
14720 std::move(synthetic_pinch_gesture),
14721 base::BindOnce([](SyntheticGesture::Result result) {
14722 EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
14723 }));
14724 ack_waiter.Wait();
14725 }
14726
14727 // Make sure all the page scale values behave as expected.
14728 const float kScaleTolerance = 0.07f;
14729 observer_a.WaitForPageScaleFactor(target_page_scale, kScaleTolerance);
14730 observer_b.WaitForExternalPageScaleFactor(target_page_scale, kScaleTolerance);
14731 float final_page_scale =
14732 observer_a.LastRenderFrameMetadata().page_scale_factor;
14733
14734 // Verify scroll position of OOPIF.
14735 float initial_child_scroll = EvalJs(child, "window.scrollY").ExtractDouble();
14736
14737 // Send touch-initiated gesture scroll sequence to OOPIF.
14738 // TODO(wjmaclean): GetViewBounds() is broken for OOPIFs when PSF != 1.f, so
14739 // we calculate it manually. This will need to be update when GetViewBounds()
14740 // in RenderWidgetHostViewBase is fixed. See https://crbug.com/928825.
14741 auto child_bounds = child_view->GetViewBounds();
14742 gfx::PointF child_upper_left =
14743 child_view->TransformPointToRootCoordSpaceF(gfx::PointF(0, 0));
14744 gfx::PointF child_lower_right = child_view->TransformPointToRootCoordSpaceF(
14745 gfx::PointF(child_bounds.width(), child_bounds.height()));
14746 gfx::PointF scroll_start_location_in_screen =
14747 gfx::PointF((child_upper_left.x() + child_lower_right.x()) / 2.f,
14748 child_lower_right.y() - 10);
14749 const float kScrollDelta = 100.f;
14750 gfx::PointF scroll_end_location_in_screen =
14751 scroll_start_location_in_screen + gfx::Vector2dF(0, -kScrollDelta);
14752
14753 // Create touch move sequence with discrete touch moves. Include a brief
14754 // pause at the end to avoid the scroll flinging.
14755 std::string actions_template = R"HTML(
14756 [{
14757 "source" : "touch",
14758 "actions" : [
14759 { "name": "pointerDown", "x": %f, "y": %f},
14760 { "name": "pointerMove", "x": %f, "y": %f},
Lan Weia80bbf22019-02-22 02:08:1514761 { "name": "pause", "duration": 300 },
W. James MacLeanfee5b4f2019-02-07 20:20:3514762 { "name": "pointerUp"}
14763 ]
14764 }]
14765 )HTML";
14766 std::string touch_move_sequence_json = base::StringPrintf(
14767 actions_template.c_str(), scroll_start_location_in_screen.x(),
14768 scroll_start_location_in_screen.y(), scroll_end_location_in_screen.x(),
14769 scroll_end_location_in_screen.y());
14770 base::JSONReader json_reader;
Avi Drissman57e5d7df2019-03-21 19:12:5314771 base::Optional<base::Value> touch_params =
14772 json_reader.ReadToValue(touch_move_sequence_json);
14773 ASSERT_TRUE(touch_params.has_value()) << json_reader.GetErrorMessage();
14774 ActionsParser actions_parser(std::move(touch_params.value()));
W. James MacLeanfee5b4f2019-02-07 20:20:3514775
14776 ASSERT_TRUE(actions_parser.ParsePointerActionSequence());
14777 auto synthetic_scroll_gesture =
14778 SyntheticGesture::Create(actions_parser.gesture_params());
14779
14780 {
14781 auto* child_host = static_cast<RenderWidgetHostImpl*>(
14782 child->current_frame_host()->GetRenderWidgetHost());
14783 InputEventAckWaiter ack_waiter(child_host,
14784 blink::WebInputEvent::kGestureScrollEnd);
14785 host->QueueSyntheticGesture(
14786 std::move(synthetic_scroll_gesture),
14787 base::BindOnce([](SyntheticGesture::Result result) {
14788 EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
14789 }));
14790 ack_waiter.Wait();
14791 }
14792
14793 // Verify new scroll position of OOPIF, should match touch sequence delta.
14794 float expected_scroll_delta = kScrollDelta / final_page_scale;
14795 float actual_scroll_delta =
14796 EvalJs(child, "window.scrollY").ExtractDouble() - initial_child_scroll;
14797
14798 const float kScrollTolerance = 0.2f;
14799 EXPECT_GT((1.f + kScrollTolerance) * expected_scroll_delta,
14800 actual_scroll_delta);
14801 EXPECT_LT((1.f - kScrollTolerance) * expected_scroll_delta,
14802 actual_scroll_delta);
14803}
14804
Alex Moshchuk77beeff92019-05-29 22:14:4714805// Check that if a frame starts a navigation, and the frame's current process
14806// dies before the response for the navigation comes back, the response will
14807// not trigger a process kill and will be allowed to commit in a new process.
14808// See https://crbug.com/968259.
14809IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
14810 ProcessDiesBeforeCrossSiteNavigationCompletes) {
14811 GURL first_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
14812 EXPECT_TRUE(NavigateToURL(shell(), first_url));
14813 scoped_refptr<SiteInstanceImpl> first_site_instance(
14814 web_contents()->GetMainFrame()->GetSiteInstance());
14815
14816 // Start a cross-site navigation and proceed only up to the request start.
14817 GURL second_url(embedded_test_server()->GetURL("b.com", "/title1.html"));
14818 TestNavigationManager delayer(web_contents(), second_url);
14819 EXPECT_TRUE(ExecuteScript(shell(), JsReplace("location = $1", second_url)));
14820 EXPECT_TRUE(delayer.WaitForRequestStart());
14821
14822 // Terminate the current a.com process.
14823 RenderProcessHost* first_process =
14824 web_contents()->GetMainFrame()->GetProcess();
14825 RenderProcessHostWatcher crash_observer(
14826 first_process, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
14827 EXPECT_TRUE(first_process->Shutdown(0));
14828 crash_observer.Wait();
14829 EXPECT_FALSE(web_contents()->GetMainFrame()->IsRenderFrameLive());
14830
14831 // Resume the cross-site navigation and ensure it commits in a new
14832 // SiteInstance and process.
14833 delayer.WaitForNavigationFinished();
14834 EXPECT_TRUE(web_contents()->GetMainFrame()->IsRenderFrameLive());
14835 EXPECT_NE(web_contents()->GetMainFrame()->GetProcess(), first_process);
14836 EXPECT_NE(web_contents()->GetMainFrame()->GetSiteInstance(),
14837 first_site_instance);
14838 EXPECT_EQ(second_url, web_contents()->GetMainFrame()->GetLastCommittedURL());
14839}
14840
Ehsan Karamad39407082019-02-19 23:38:1914841class FeaturePolicyPropagationToAuxiliaryBrowsingContextTest
14842 : public SitePerProcessFeaturePolicyJavaScriptBrowserTest,
14843 public testing::WithParamInterface<std::tuple<
14844 const char* /* Whether or not <iframe> is sandbox or can escape it */,
14845 bool /* <iframe> same origin? */,
14846 bool /* opened window same origin? */,
14847 const char* /* window feature in window.open() */>> {
14848 protected:
14849 void SetUpCommandLine(base::CommandLine* command_line) override {
14850 SitePerProcessFeaturePolicyJavaScriptBrowserTest::SetUpCommandLine(
14851 command_line);
14852 feature_list_.InitAndEnableFeature(features::kFeaturePolicyForSandbox);
14853 }
14854
14855 private:
14856 base::test::ScopedFeatureList feature_list_;
14857};
14858
14859// This test verifies the correct propagation of FeaturePolicy from an *opener*
14860// to the opened auxiliary browsing context. This test runs for both cross and
14861// same origin frames.
14862IN_PROC_BROWSER_TEST_P(FeaturePolicyPropagationToAuxiliaryBrowsingContextTest,
14863 PropagationForDifferentOrigins) {
14864 EXPECT_TRUE(NavigateToURL(
14865 shell(), embedded_test_server()->GetURL(
14866 "a.com", "/feature_policy_window_open_embedder.html")));
14867 const GURL same_origin_child = embedded_test_server()->GetURL(
14868 "a.com", "/feature_policy_window_open_embedded.html");
14869 const GURL cross_origin_child = embedded_test_server()->GetURL(
14870 "b.com", "/feature_policy_window_open_embedded.html");
14871 const GURL cross_origin_window_url =
14872 embedded_test_server()->GetURL("c.com", "/title1.html");
14873 const GURL same_origin_window_url = GURL("about:blank");
14874 const std::string kScriptCheckPolicy =
14875 "document.featurePolicy.allowsFeature('sync-xhr')";
14876 // Test parameters
Ehsan Karamad34d10682019-02-26 20:55:0414877 const std::string iframe_type = std::get<0>(GetParam());
14878 const std::string iframe_src =
14879 (std::get<1>(GetParam()) ? same_origin_child : cross_origin_child).spec();
14880 const std::string window_url =
14881 (std::get<2>(GetParam()) ? same_origin_window_url
14882 : cross_origin_window_url)
14883 .spec();
14884 const std::string window_feature = std::get<3>(GetParam());
Ehsan Karamad39407082019-02-19 23:38:1914885 SCOPED_TRACE(testing::Message() << " <iframe> Type: " << iframe_type
14886 << " <iframe> Source: " << iframe_src
14887 << " window URL: " << window_url
14888 << " window Feature: " << window_feature);
14889 // TODO(ekaramad): Modify the test expectation once we resolve the following
14890 // issues in feature policies:
14891 // - "rel='noopener'" might end up resetting the feature policies.
14892 // - A new feature to escape feature policies is introduced (similar to the
14893 // sandbox version "allow-popups-to-escape-sandbox".
14894 // For now, only the sandbox-escaping case should be allowed to have a new
14895 // (default) feature policy state.
Ehsan Karamad34d10682019-02-26 20:55:0414896 bool expected_feature_state_in_auxiliary_browsing_context =
14897 (iframe_type == "sandboxed-escaping");
Ehsan Karamad39407082019-02-19 23:38:1914898 ShellAddedObserver shell_added_observer;
14899 ASSERT_TRUE(
14900 ExecJs(shell(), JsReplace("test($1, $2, $3, $4)", iframe_type, iframe_src,
14901 window_url, window_feature)));
14902 auto* new_shell = shell_added_observer.GetShell();
14903 while (new_shell->web_contents()->GetLastCommittedURL() != GURL(window_url)) {
14904 base::RunLoop run_loop;
14905 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
14906 FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
14907 run_loop.Run();
14908 }
14909 ASSERT_TRUE(WaitForLoadStop(new_shell->web_contents()));
14910 ASSERT_EQ(new_shell->web_contents()->GetLastCommittedURL(), window_url);
Ehsan Karamad34d10682019-02-26 20:55:0414911 EXPECT_EQ(expected_feature_state_in_auxiliary_browsing_context,
14912 EvalJs(new_shell, kScriptCheckPolicy));
Ehsan Karamad39407082019-02-19 23:38:1914913 bool browser_side_feature_state =
14914 static_cast<RenderFrameHostImpl*>(
14915 new_shell->web_contents()->GetMainFrame())
14916 ->feature_policy()
14917 ->IsFeatureEnabled(blink::mojom::FeaturePolicyFeature::kSyncXHR);
Ehsan Karamad34d10682019-02-26 20:55:0414918 EXPECT_EQ(expected_feature_state_in_auxiliary_browsing_context,
Ehsan Karamad39407082019-02-19 23:38:1914919 browser_side_feature_state);
Ehsan Karamad39407082019-02-19 23:38:1914920 new_shell->Close();
14921}
14922
Staphany Park7dcbebc2019-02-28 06:59:4414923INSTANTIATE_TEST_SUITE_P(SitePerProcess,
14924 FeaturePolicyPropagationToAuxiliaryBrowsingContextTest,
14925 testing::Combine(
14926 /* iframe_type */
14927 testing::ValuesIn({"sandboxed", "notsandboxed",
14928 "sandboxed-escaping"}),
14929 /* iframe_same_origin */
14930 testing::Bool(),
14931 /* window_same_origin */
14932 testing::Bool(),
14933 /* window feature */
14934 testing::ValuesIn({"", "noopener"})));
Ehsan Karamad44fc72112019-02-26 18:15:4714935
14936enum class InnerWebContentsAttachChildFrameOriginType {
14937 kSameOriginAboutBlank,
14938 kSameOriginOther,
14939 kCrossOrigin
14940};
14941
14942class InnerWebContentsAttachTest
14943 : public SitePerProcessBrowserTest,
14944 public testing::WithParamInterface<
14945 std::tuple<InnerWebContentsAttachChildFrameOriginType,
14946 bool /* original frame has beforeunload handlers */,
14947 bool /* user proceeds with attaching */>> {
14948 public:
14949 InnerWebContentsAttachTest() {}
14950 ~InnerWebContentsAttachTest() override {}
14951
14952 protected:
14953 // Helper class to initiate and conclude a frame preparation process for
14954 // attaching an inner WebContents.
14955 class PrepareFrameJob {
14956 public:
14957 PrepareFrameJob(RenderFrameHostImpl* original_render_frame_host,
14958 bool proceed_through_beforeunload) {
14959 auto* web_contents =
14960 WebContents::FromRenderFrameHost(original_render_frame_host);
14961 // Need user gesture for 'beforeunload' to fire.
14962 PrepContentsForBeforeUnloadTest(web_contents);
14963 // Simulate user choosing to stay on the page after beforeunload fired.
David Benjaminf62c6662019-03-21 20:25:0414964 SetShouldProceedOnBeforeUnload(Shell::FromWebContents(web_contents),
14965 true /* always_proceed */,
14966 proceed_through_beforeunload);
Ehsan Karamad44fc72112019-02-26 18:15:4714967 RenderFrameHost::PrepareForInnerWebContentsAttachCallback callback =
14968 base::BindOnce(&PrepareFrameJob::OnPrepare, base::Unretained(this));
14969 original_render_frame_host->PrepareForInnerWebContentsAttach(
14970 std::move(callback));
14971 }
14972 virtual ~PrepareFrameJob() {}
14973
14974 void WaitForPreparedFrame() {
14975 if (did_call_prepare_)
14976 return;
14977 run_loop_.Run();
14978 }
14979
14980 RenderFrameHostImpl* prepared_frame() const {
14981 return new_render_frame_host_;
14982 }
14983
14984 private:
14985 void OnPrepare(RenderFrameHost* render_frame_host) {
14986 did_call_prepare_ = true;
14987 new_render_frame_host_ =
14988 static_cast<RenderFrameHostImpl*>(render_frame_host);
14989 if (run_loop_.running())
14990 run_loop_.Quit();
14991 }
14992
14993 bool did_call_prepare_ = false;
14994 RenderFrameHostImpl* new_render_frame_host_ = nullptr;
14995 base::RunLoop run_loop_;
14996
14997 DISALLOW_COPY_AND_ASSIGN(PrepareFrameJob);
14998 };
14999
15000 void SetUpCommandLine(base::CommandLine* command_line) override {
15001 SitePerProcessBrowserTest::SetUpCommandLine(command_line);
15002 feature_list_.InitAndEnableFeature(
15003 features::kMimeHandlerViewInCrossProcessFrame);
15004 }
15005
15006 private:
15007 base::test::ScopedFeatureList feature_list_;
15008
15009 DISALLOW_COPY_AND_ASSIGN(InnerWebContentsAttachTest);
15010};
15011
15012// This is a test for the FrameTreeNode preparation process for various types
15013// of outer WebContents RenderFrameHosts; essentially when connecting two
15014// WebContents through a frame in a WebPage it is possible that the frame itself
15015// has a nontrivial document (other than about:blank) with a beforeunload
15016// handler, or even it is a cross-process frame. For such cases the frame first
15017// needs to be sanitized to be later consumed by the WebContents attaching API.
15018IN_PROC_BROWSER_TEST_P(InnerWebContentsAttachTest, PrepareFrame) {
15019 ASSERT_TRUE(
15020 NavigateToURL(shell(), embedded_test_server()->GetURL(
15021 "a.com", "/page_with_object_fallback.html")));
15022 InnerWebContentsAttachChildFrameOriginType child_frame_origin_type =
15023 std::get<0>(GetParam());
15024 bool test_beforeunload = std::get<1>(GetParam());
15025 bool proceed_through_beforeunload = std::get<2>(GetParam());
15026 GURL child_frame_url =
15027 child_frame_origin_type ==
15028 InnerWebContentsAttachChildFrameOriginType::kSameOriginAboutBlank
15029 ? GURL(url::kAboutBlankURL)
15030 : child_frame_origin_type ==
15031 InnerWebContentsAttachChildFrameOriginType::kSameOriginOther
15032 ? embedded_test_server()->GetURL("a.com", "/title1.html")
15033 : embedded_test_server()->GetURL("b.com", "/title1.html");
15034 SCOPED_TRACE(testing::Message()
15035 << " Child frame URL:" << child_frame_url.spec()
15036 << " 'beforeunload' modal shown: " << test_beforeunload
15037 << " proceed through'beforeunload': "
15038 << proceed_through_beforeunload);
15039 auto* child_node = web_contents()->GetFrameTree()->root()->child_at(0);
15040 NavigateFrameToURL(child_node, child_frame_url);
15041 if (test_beforeunload) {
15042 EXPECT_TRUE(ExecJs(child_node,
15043 "window.addEventListener('beforeunload', (e) => {"
15044 "e.returnValue = ''; return e; });"));
15045 }
15046 auto* original_child_frame = child_node->current_frame_host();
15047 RenderFrameDeletedObserver original_child_frame_observer(
15048 original_child_frame);
15049 PrepareFrameJob prepare_job(original_child_frame,
15050 proceed_through_beforeunload);
15051 if (test_beforeunload)
15052 WaitForAppModalDialog(shell());
15053 prepare_job.WaitForPreparedFrame();
15054 auto* new_render_frame_host = prepare_job.prepared_frame();
15055 bool did_prepare_frame = new_render_frame_host;
15056 bool same_frame_used = (new_render_frame_host == original_child_frame);
15057 // If a frame was not prepared, then it has to be due to beforeunload being
15058 // dismissed.
15059 ASSERT_TRUE(did_prepare_frame ||
15060 (test_beforeunload && !proceed_through_beforeunload));
15061 // If the original frame is in the same SiteInstance as its parent, then it
15062 // can be reused; otherwise a new frame is expected here.
15063 bool is_same_origin =
15064 child_frame_origin_type !=
15065 InnerWebContentsAttachChildFrameOriginType::kCrossOrigin;
15066 if (!is_same_origin && did_prepare_frame) {
15067 // For the cross-origin case we expect the original RenderFrameHost to go
15068 // away during preparation.
15069 original_child_frame_observer.WaitUntilDeleted();
15070 }
15071 ASSERT_TRUE(!did_prepare_frame || (is_same_origin == same_frame_used));
15072 ASSERT_TRUE(!did_prepare_frame ||
15073 (original_child_frame_observer.deleted() != is_same_origin));
15074 // Finally, try the WebContents attach API and make sure we are doing OK.
15075 if (new_render_frame_host)
15076 CreateAndAttachInnerContents(new_render_frame_host);
15077}
15078
Staphany Park7dcbebc2019-02-28 06:59:4415079INSTANTIATE_TEST_SUITE_P(
Ehsan Karamad44fc72112019-02-26 18:15:4715080 SitePerProcess,
15081 InnerWebContentsAttachTest,
15082 testing::Combine(
15083 testing::ValuesIn(
15084 {InnerWebContentsAttachChildFrameOriginType::kSameOriginAboutBlank,
15085 InnerWebContentsAttachChildFrameOriginType::kSameOriginOther,
15086 InnerWebContentsAttachChildFrameOriginType::kCrossOrigin}),
15087 testing::Bool(),
15088 testing::Bool()));
W. James MacLean2dc75ed42019-03-06 14:31:0915089
arthursonzogni509e8af2019-05-29 14:03:1615090// This checks what process is used when an iframe is navigated to about:blank.
15091// The new document should be loaded in the process of its initiator.
15092//
15093// Test case:
15094// 1. Navigate to A1(B2).
15095// 2. B2 navigates itself to B3 = about:blank. Process B is used.
15096// 3. A1 makes B3 to navigate to A4 = about:blank. Process A is used.
15097IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
15098 SameAndCrossProcessIframeAboutBlankNavigation) {
15099 // 1. Navigate to A1(B2).
15100 GURL a1_url(embedded_test_server()->GetURL(
15101 "a.com", "/cross_site_iframe_factory.html?a(b)"));
15102 EXPECT_TRUE(NavigateToURL(shell(), a1_url));
15103 RenderFrameHostImpl* a1_rfh = web_contents()->GetMainFrame();
15104 RenderFrameHostImpl* b2_rfh = a1_rfh->child_at(0)->current_frame_host();
15105
15106 // 2. B2 navigates itself to B3 = about:blank. Process B is used.
15107 {
15108 scoped_refptr<SiteInstance> b2_site_instance = b2_rfh->GetSiteInstance();
15109 TestNavigationManager navigation_manager(web_contents(),
15110 GURL("about:blank"));
15111 EXPECT_TRUE(ExecJs(b2_rfh, "location.href = 'about:blank';"));
15112 navigation_manager.WaitForNavigationFinished();
15113
15114 RenderFrameHostImpl* b3_rfh = a1_rfh->child_at(0)->current_frame_host();
15115 DCHECK_EQ(b3_rfh->GetSiteInstance(), b2_site_instance);
15116 DCHECK_NE(a1_rfh->GetProcess(), b3_rfh->GetProcess());
15117 }
15118
15119 // 3. A1 makes B3 to navigate to A4 = about:blank. Process A is used.
15120 {
15121 TestNavigationManager navigation_manager(web_contents(),
15122 GURL("about:blank"));
15123 EXPECT_TRUE(ExecJs(a1_rfh, R"(
15124 document.querySelector("iframe").src = "about:blank";
15125 )"));
15126 navigation_manager.WaitForNavigationFinished();
15127
15128 RenderFrameHostImpl* b4_rfh = a1_rfh->child_at(0)->current_frame_host();
15129 DCHECK_EQ(a1_rfh->GetSiteInstance(), b4_rfh->GetSiteInstance());
15130 }
15131}
15132
[email protected]9b159a52013-10-03 17:24:5515133} // namespace content