File: chrome_render_widget_host_browsertests.cc

package info (click to toggle)
chromium 138.0.7204.157-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 6,071,864 kB
  • sloc: cpp: 34,936,859; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,967; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (248 lines) | stat: -rw-r--r-- 11,618 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/command_line.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/test/test_timeouts.h"
#include "build/build_config.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/location_bar/location_bar.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/omnibox/browser/omnibox_view.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/focused_node_details.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/fake_frame_widget.h"
#include "content/public/test/test_utils.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "url/gurl.h"

class ActiveRenderWidgetHostBrowserTest : public InProcessBrowserTest {
 public:
  ActiveRenderWidgetHostBrowserTest() = default;

  ActiveRenderWidgetHostBrowserTest(const ActiveRenderWidgetHostBrowserTest&) =
      delete;
  ActiveRenderWidgetHostBrowserTest& operator=(
      const ActiveRenderWidgetHostBrowserTest&) = delete;

  ~ActiveRenderWidgetHostBrowserTest() override = default;

  void SetUpCommandLine(base::CommandLine* command_line) override {
    content::IsolateAllSitesForTesting(command_line);
  }

  void SetUpOnMainThread() override {
    host_resolver()->AddRule("*", "127.0.0.1");

    // Add content/test/data for cross_site_iframe_factory.html
    embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");

    ASSERT_TRUE(embedded_test_server()->Start());
  }
};

IN_PROC_BROWSER_TEST_F(ActiveRenderWidgetHostBrowserTest,
                       DocumentIsActiveAndFocused) {
  GURL main_url(embedded_test_server()->GetURL(
      "a.com", "/cross_site_iframe_factory.html?a(b(c),d)"));

  //  Site A ------------ proxies for B C D
  //    |--Site B ------- proxies for A C D
  //    |    +--Site C -- proxies for A B D
  //    +--Site D ------- proxies for A B C
  // Where A = http://a.com/
  //       B = http://b.com/
  //       C = http://c.com/
  //       D = http://d.com/
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url));

  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();
  content::RenderFrameHost* main_frame_a = web_contents->GetPrimaryMainFrame();
  content::RenderFrameHost* child_frame_b = ChildFrameAt(main_frame_a, 0);
  ASSERT_NE(nullptr, child_frame_b);
  content::RenderFrameHost* child_frame_d = ChildFrameAt(main_frame_a, 1);
  ASSERT_NE(nullptr, child_frame_d);
  content::RenderFrameHost* child_frame_c = ChildFrameAt(child_frame_b, 0);
  ASSERT_NE(nullptr, child_frame_c);

  EXPECT_NE(main_frame_a->GetSiteInstance(), child_frame_b->GetSiteInstance());
  EXPECT_NE(main_frame_a->GetSiteInstance(), child_frame_d->GetSiteInstance());
  EXPECT_NE(child_frame_b->GetSiteInstance(), child_frame_c->GetSiteInstance());

  // Helper function to check document.hasFocus() for a given frame.
  // hasFocus internally calls FocusController::IsDocumentFocused which
  // return true only iff document is  active and focused.
  auto document_is_active_and_focused =
      [](content::RenderFrameHost* rfh) -> bool {
    return EvalJs(rfh, "document.hasFocus()").ExtractBool();
  };

  // Helper function to check a property of document.activeElement in the
  // specified frame.
  auto verify_active_element_property = [](content::RenderFrameHost* rfh,
                                           const std::string& property,
                                           const std::string& expected_value) {
    std::string script = base::StringPrintf(
        "document.activeElement.%s.toLowerCase();", property.c_str());
    EXPECT_EQ(expected_value, EvalJs(rfh, script));
  };

  // The main_frame_a should have a focus to start with.
  EXPECT_EQ(main_frame_a, web_contents->GetFocusedFrame());
  EXPECT_TRUE(document_is_active_and_focused(main_frame_a));
  EXPECT_FALSE(document_is_active_and_focused(child_frame_b));
  EXPECT_FALSE(document_is_active_and_focused(child_frame_c));
  EXPECT_FALSE(document_is_active_and_focused(child_frame_d));
  verify_active_element_property(main_frame_a, "tagName", "body");

  // After focusing child_frame_b, document.hasFocus() should return
  // true for child_frame_b and all its ancestor frames.
  EXPECT_TRUE(ExecJs(child_frame_b, "window.focus();"));
  EXPECT_EQ(child_frame_b, web_contents->GetFocusedFrame());
  EXPECT_TRUE(document_is_active_and_focused(main_frame_a));
  EXPECT_TRUE(document_is_active_and_focused(child_frame_b));
  EXPECT_FALSE(document_is_active_and_focused(child_frame_c));
  EXPECT_FALSE(document_is_active_and_focused(child_frame_d));
  verify_active_element_property(main_frame_a, "tagName", "iframe");
  verify_active_element_property(main_frame_a, "src",
                                 child_frame_b->GetLastCommittedURL().spec());

  // After focusing child_frame_c, document.hasFocus() should return
  // true for child_frame_c and all its ancestor frames.
  EXPECT_TRUE(ExecJs(child_frame_c, "window.focus();"));
  EXPECT_EQ(child_frame_c, web_contents->GetFocusedFrame());
  EXPECT_TRUE(document_is_active_and_focused(main_frame_a));
  EXPECT_TRUE(document_is_active_and_focused(child_frame_b));
  EXPECT_TRUE(document_is_active_and_focused(child_frame_c));
  EXPECT_FALSE(document_is_active_and_focused(child_frame_d));
  verify_active_element_property(main_frame_a, "tagName", "iframe");
  // Check document.activeElement in main_frame_a.  It should still
  // point to <iframe> for the b.com frame, since Blink computes the
  // focused iframe element by walking the parent chain of the focused
  // frame until it hits the current frame.  This logic should still
  // work with remote frames.
  verify_active_element_property(main_frame_a, "src",
                                 child_frame_b->GetLastCommittedURL().spec());

  // After focusing child_frame_d, document.hasFocus() should return
  // true for child_frame_d and all its ancestor frames.
  EXPECT_TRUE(ExecJs(child_frame_d, "window.focus();"));
  EXPECT_EQ(child_frame_d, web_contents->GetFocusedFrame());
  EXPECT_TRUE(document_is_active_and_focused(main_frame_a));
  EXPECT_FALSE(document_is_active_and_focused(child_frame_b));
  EXPECT_FALSE(document_is_active_and_focused(child_frame_c));
  EXPECT_TRUE(document_is_active_and_focused(child_frame_d));
  verify_active_element_property(main_frame_a, "tagName", "iframe");
  verify_active_element_property(main_frame_a, "src",
                                 child_frame_d->GetLastCommittedURL().spec());

  // After focusing main_frame_a, document.hasFocus() should return
  // true for main_frame_a and since it's a root of tree, all its
  // descendants should return false. On the renderer side, both the
  // 'active' and 'focus' states for blink::FocusController will be
  // true.
  EXPECT_TRUE(ExecJs(main_frame_a, "window.focus();"));
  EXPECT_EQ(main_frame_a, web_contents->GetFocusedFrame());
  EXPECT_TRUE(document_is_active_and_focused(main_frame_a));
  EXPECT_FALSE(document_is_active_and_focused(child_frame_b));
  EXPECT_FALSE(document_is_active_and_focused(child_frame_c));
  EXPECT_FALSE(document_is_active_and_focused(child_frame_d));
  verify_active_element_property(main_frame_a, "tagName", "body");

  // Focus the URL bar.
  OmniboxView* omnibox =
      browser()->window()->GetLocationBar()->GetOmniboxView();
  // Give the omnibox focus.
  omnibox->SetFocus(/*is_user_initiated=*/true);
  base::RunLoop().RunUntilIdle();
  EXPECT_EQ(main_frame_a, web_contents->GetFocusedFrame());

  // `omnibox->SetFocus()` should call blur event on main_frame_a and
  // deactivate the active render widget, but on Mac calling
  // `omnibox->SetFocus()` function doesn't invoke
  // RWHI::SetActive(false). As a result, `blink::FocusController`'s
  // 'active' state maintains the previous value of false.
  //
  // This table sums up `blink::FocusController`'s 'active' and 'focus'
  // states on different platforms after focusing the omnibox:
  //
  // |        | Linux |  Mac  | Windows |
  // | active | false | true  | false   |
  // | focus  | false | false | false   |
  //
  // Since `document.hasFocus()` only returns true iff the document is
  // both active and focus, the test still expects
  // `document.hasFocus()` to be false on all platforms.
  //
  // Note that there is no separate API to test active state of the
  // document. Instead, Mac's active behavior is separately tested in
  // `ActiveRenderWidgetHostBrowserTest.FocusOmniBox`.
  EXPECT_FALSE(document_is_active_and_focused(main_frame_a));
  EXPECT_FALSE(document_is_active_and_focused(child_frame_b));
  EXPECT_FALSE(document_is_active_and_focused(child_frame_c));
  EXPECT_FALSE(document_is_active_and_focused(child_frame_d));
  // body tag is active by default.
  verify_active_element_property(main_frame_a, "tagName", "body");
  verify_active_element_property(child_frame_b, "tagName", "body");
  verify_active_element_property(child_frame_c, "tagName", "body");
  verify_active_element_property(child_frame_d, "tagName", "body");
}

// This test verifies that on Mac, moving the focus from webcontents to Omnibox
// doesn't change the 'active' state and old value of the active state is
// retained.
//
// FakeFrameWidget has Optional<bool> 'active' state which is
// uninitialised at the beginning. omnibox->SetFocus() invokes
// RWHI::SetActive(false) for webcontents and there is a IPC call to
// renderer which changes 'active' state to false.
//
// On Mac, calling omnibox->SetFocus function doesn't invoke
// RWHI::SetActive(false). Hence there is no IPC call to renderer and
// 'active' state maintains old value.
IN_PROC_BROWSER_TEST_F(ActiveRenderWidgetHostBrowserTest, FocusOmniBox) {
  GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url));

  content::WebContents* web_contents =
      browser()->tab_strip_model()->GetActiveWebContents();

  content::RenderFrameHost* main_frame = web_contents->GetPrimaryMainFrame();
  EXPECT_EQ(main_frame, web_contents->GetFocusedFrame());

  mojo::PendingAssociatedReceiver<blink::mojom::FrameWidget>
      blink_frame_widget_receiver =
          content::BindFakeFrameWidgetInterfaces(main_frame);
  content::FakeFrameWidget fake_frame_widget(
      std::move(blink_frame_widget_receiver));

  // Main frame is already focused at this point and now focus URL bar.
  OmniboxView* omnibox =
      browser()->window()->GetLocationBar()->GetOmniboxView();
  // Give the omnibox focus.
  omnibox->SetFocus(/*is_user_initiated=*/true);

  base::RunLoop().RunUntilIdle();
#if BUILDFLAG(IS_MAC)
  // On MacOS, calling omnibox->SetFocus function doesn't invoke
  // RWHI::SetActive. Hence there is no IPC call to renderer and
  // FakeFrameWidget's 'active' state remains uninitialised.
  EXPECT_EQ(fake_frame_widget.GetActive(), std::nullopt);
#else
  EXPECT_EQ(fake_frame_widget.GetActive(), false);
#endif
}