Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(666)

Side by Side Diff: components/web_view/frame_apptest.cc

Issue 1677293002: Bye bye Mandoline (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: moar Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « components/web_view/frame.cc ('k') | components/web_view/frame_connection.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 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
5 #include "components/web_view/frame.h"
6
7 #include <stdint.h>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/macros.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/run_loop.h"
16 #include "base/test/test_timeouts.h"
17 #include "base/time/time.h"
18 #include "build/build_config.h"
19 #include "components/mus/public/cpp/window_observer.h"
20 #include "components/mus/public/cpp/window_tree_connection.h"
21 #include "components/mus/public/cpp/window_tree_delegate.h"
22 #include "components/mus/public/cpp/window_tree_host_factory.h"
23 #include "components/mus/public/interfaces/window_tree_host.mojom.h"
24 #include "components/web_view/frame_connection.h"
25 #include "components/web_view/frame_tree.h"
26 #include "components/web_view/frame_tree_delegate.h"
27 #include "components/web_view/frame_user_data.h"
28 #include "components/web_view/test_frame_tree_delegate.h"
29 #include "mojo/shell/public/cpp/application_test_base.h"
30
31 using mus::Window;
32 using mus::WindowTreeConnection;
33
34 namespace web_view {
35
36 namespace {
37
38 base::RunLoop* current_run_loop = nullptr;
39
40 void TimeoutRunLoop(const base::Closure& timeout_task, bool* timeout) {
41 CHECK(current_run_loop);
42 *timeout = true;
43 timeout_task.Run();
44 }
45
46 bool DoRunLoopWithTimeout() {
47 if (current_run_loop != nullptr)
48 return false;
49
50 bool timeout = false;
51 base::RunLoop run_loop;
52 base::MessageLoop::current()->PostDelayedTask(
53 FROM_HERE, base::Bind(&TimeoutRunLoop, run_loop.QuitClosure(), &timeout),
54 TestTimeouts::action_timeout());
55
56 current_run_loop = &run_loop;
57 current_run_loop->Run();
58 current_run_loop = nullptr;
59 return !timeout;
60 }
61
62 void QuitRunLoop() {
63 current_run_loop->Quit();
64 current_run_loop = nullptr;
65 }
66
67 } // namespace
68
69 void OnGotIdCallback(base::RunLoop* run_loop) {
70 run_loop->Quit();
71 }
72
73 // Creates a new FrameConnection. This runs a nested message loop until the
74 // content handler id is obtained.
75 scoped_ptr<FrameConnection> CreateFrameConnection(mojo::Shell* shell,
76 const std::string& url) {
77 scoped_ptr<FrameConnection> frame_connection(new FrameConnection);
78 mojo::URLRequestPtr request(mojo::URLRequest::New());
79 request->url = mojo::String::From(url);
80 base::RunLoop run_loop;
81 frame_connection->Init(shell, std::move(request),
82 base::Bind(&OnGotIdCallback, &run_loop));
83 run_loop.Run();
84 return frame_connection;
85 }
86
87 class TestFrameClient : public mojom::FrameClient {
88 public:
89 TestFrameClient()
90 : connect_count_(0), last_dispatch_load_event_frame_id_(0) {}
91 ~TestFrameClient() override {}
92
93 int connect_count() const { return connect_count_; }
94
95 mojo::Array<mojom::FrameDataPtr> connect_frames() {
96 return std::move(connect_frames_);
97 }
98
99 mojo::Array<mojom::FrameDataPtr> adds() { return std::move(adds_); }
100
101 // Sets a callback to run once OnConnect() is received.
102 void set_on_connect_callback(const base::Closure& closure) {
103 on_connect_callback_ = closure;
104 }
105
106 void set_on_loading_state_changed_callback(const base::Closure& closure) {
107 on_loading_state_changed_callback_ = closure;
108 }
109
110 void set_on_dispatch_load_event_callback(const base::Closure& closure) {
111 on_dispatch_load_event_callback_ = closure;
112 }
113
114 mojom::Frame* server_frame() { return server_frame_.get(); }
115
116 mojo::InterfaceRequest<mojom::Frame> GetServerFrameRequest() {
117 return GetProxy(&server_frame_);
118 }
119
120 void last_loading_state_changed_notification(uint32_t* frame_id,
121 bool* loading) const {
122 *frame_id = last_loading_state_changed_notification_.frame_id;
123 *loading = last_loading_state_changed_notification_.loading;
124 }
125
126 uint32_t last_dispatch_load_event_frame_id() const {
127 return last_dispatch_load_event_frame_id_;
128 }
129
130 base::TimeTicks last_navigation_start_time() const {
131 return last_navigation_start_time_;
132 }
133
134 // mojom::FrameClient:
135 void OnConnect(mojom::FramePtr frame,
136 uint32_t change_id,
137 uint32_t window_id,
138 mojom::WindowConnectType window_connect_type,
139 mojo::Array<mojom::FrameDataPtr> frames,
140 int64_t navigation_start_time_ticks,
141 const OnConnectCallback& callback) override {
142 connect_count_++;
143 connect_frames_ = std::move(frames);
144 if (frame)
145 server_frame_ = std::move(frame);
146 callback.Run();
147 if (!on_connect_callback_.is_null())
148 on_connect_callback_.Run();
149
150 last_navigation_start_time_ =
151 base::TimeTicks::FromInternalValue(navigation_start_time_ticks);
152 }
153 void OnFrameAdded(uint32_t change_id, mojom::FrameDataPtr frame) override {
154 adds_.push_back(std::move(frame));
155 }
156 void OnFrameRemoved(uint32_t change_id, uint32_t frame_id) override {}
157 void OnFrameClientPropertyChanged(uint32_t frame_id,
158 const mojo::String& name,
159 mojo::Array<uint8_t> new_data) override {}
160 void OnPostMessageEvent(uint32_t source_frame_id,
161 uint32_t target_frame_id,
162 mojom::HTMLMessageEventPtr event) override {}
163 void OnWillNavigate(const mojo::String& origin,
164 const OnWillNavigateCallback& callback) override {
165 callback.Run();
166 }
167 void OnFrameLoadingStateChanged(uint32_t frame_id, bool loading) override {
168 last_loading_state_changed_notification_.frame_id = frame_id;
169 last_loading_state_changed_notification_.loading = loading;
170
171 if (!on_loading_state_changed_callback_.is_null())
172 on_loading_state_changed_callback_.Run();
173 }
174 void OnDispatchFrameLoadEvent(uint32_t frame_id) override {
175 last_dispatch_load_event_frame_id_ = frame_id;
176
177 if (!on_dispatch_load_event_callback_.is_null())
178 on_dispatch_load_event_callback_.Run();
179 }
180 void Find(int32_t request_id,
181 const mojo::String& search_text,
182 mojom::FindOptionsPtr options,
183 bool wrap_within_frame,
184 const FindCallback& callback) override {}
185 void StopFinding(bool clear_selection) override {}
186 void HighlightFindResults(int32_t request_id,
187 const mojo::String& search_test,
188 mojom::FindOptionsPtr options,
189 bool reset) override {}
190 void StopHighlightingFindResults() override {}
191
192 private:
193 struct LoadingStateChangedNotification {
194 LoadingStateChangedNotification() : frame_id(0), loading(false) {}
195 ~LoadingStateChangedNotification() {}
196
197 uint32_t frame_id;
198 bool loading;
199 };
200
201 int connect_count_;
202 mojo::Array<mojom::FrameDataPtr> connect_frames_;
203 mojom::FramePtr server_frame_;
204 mojo::Array<mojom::FrameDataPtr> adds_;
205 base::Closure on_connect_callback_;
206 base::Closure on_loading_state_changed_callback_;
207 base::Closure on_dispatch_load_event_callback_;
208 LoadingStateChangedNotification last_loading_state_changed_notification_;
209 uint32_t last_dispatch_load_event_frame_id_;
210 base::TimeTicks last_navigation_start_time_;
211
212 DISALLOW_COPY_AND_ASSIGN(TestFrameClient);
213 };
214
215 class FrameTest;
216
217 // WindowAndFrame maintains the Window and TestFrameClient associated with
218 // a single FrameClient. In other words this maintains the data structures
219 // needed to represent a client side frame. To obtain one use
220 // FrameTest::WaitForViewAndFrame().
221 class WindowAndFrame : public mus::WindowTreeDelegate {
222 public:
223 ~WindowAndFrame() override {
224 if (window_)
225 delete window_->connection();
226 }
227
228 // The Window associated with the frame.
229 mus::Window* window() { return window_; }
230 TestFrameClient* test_frame_client() { return &test_frame_tree_client_; }
231 mojom::Frame* server_frame() {
232 return test_frame_tree_client_.server_frame();
233 }
234
235 private:
236 friend class FrameTest;
237
238 WindowAndFrame()
239 : window_(nullptr), frame_client_binding_(&test_frame_tree_client_) {}
240
241 void set_window(Window* window) { window_ = window; }
242
243 // Runs a message loop until the window and frame data have been received.
244 void WaitForViewAndFrame() { run_loop_.Run(); }
245
246 mojo::InterfaceRequest<mojom::Frame> GetServerFrameRequest() {
247 return test_frame_tree_client_.GetServerFrameRequest();
248 }
249
250 mojom::FrameClientPtr GetFrameClientPtr() {
251 return frame_client_binding_.CreateInterfacePtrAndBind();
252 }
253
254 void Bind(mojo::InterfaceRequest<mojom::FrameClient> request) {
255 ASSERT_FALSE(frame_client_binding_.is_bound());
256 test_frame_tree_client_.set_on_connect_callback(
257 base::Bind(&WindowAndFrame::OnGotConnect, base::Unretained(this)));
258 frame_client_binding_.Bind(std::move(request));
259 }
260
261 void OnGotConnect() { QuitRunLoopIfNecessary(); }
262
263 void QuitRunLoopIfNecessary() {
264 if (window_ && test_frame_tree_client_.connect_count())
265 run_loop_.Quit();
266 }
267
268 // Overridden from WindowTreeDelegate:
269 void OnEmbed(Window* root) override {
270 window_ = root;
271 QuitRunLoopIfNecessary();
272 }
273 void OnConnectionLost(WindowTreeConnection* connection) override {
274 window_ = nullptr;
275 }
276
277 mus::Window* window_;
278 base::RunLoop run_loop_;
279 TestFrameClient test_frame_tree_client_;
280 mojo::Binding<mojom::FrameClient> frame_client_binding_;
281
282 DISALLOW_COPY_AND_ASSIGN(WindowAndFrame);
283 };
284
285 class FrameTest : public mojo::test::ApplicationTestBase,
286 public mojo::ShellClient,
287 public mus::WindowTreeDelegate,
288 public mojo::InterfaceFactory<mus::mojom::WindowTreeClient>,
289 public mojo::InterfaceFactory<mojom::FrameClient> {
290 public:
291 FrameTest() : most_recent_connection_(nullptr), window_manager_(nullptr) {}
292
293 WindowTreeConnection* most_recent_connection() {
294 return most_recent_connection_;
295 }
296
297 protected:
298 WindowTreeConnection* window_manager() { return window_manager_; }
299 TestFrameTreeDelegate* frame_tree_delegate() {
300 return frame_tree_delegate_.get();
301 }
302 FrameTree* frame_tree() { return frame_tree_.get(); }
303 WindowAndFrame* root_window_and_frame() {
304 return root_window_and_frame_.get();
305 }
306
307 scoped_ptr<WindowAndFrame> NavigateFrameWithStartTime(
308 WindowAndFrame* window_and_frame,
309 base::TimeTicks navigation_start_time) {
310 mojo::URLRequestPtr request(mojo::URLRequest::New());
311 request->url = mojo::String::From(shell_url());
312 request->originating_time_ticks = navigation_start_time.ToInternalValue();
313 window_and_frame->server_frame()->RequestNavigate(
314 mojom::NavigationTargetType::EXISTING_FRAME,
315 window_and_frame->window()->id(), std::move(request));
316 return WaitForViewAndFrame();
317 }
318
319 scoped_ptr<WindowAndFrame> NavigateFrame(WindowAndFrame* window_and_frame) {
320 return NavigateFrameWithStartTime(window_and_frame, base::TimeTicks());
321 }
322
323 // Creates a new shared frame as a child of |parent|.
324 scoped_ptr<WindowAndFrame> CreateChildWindowAndFrame(WindowAndFrame* parent) {
325 mus::Window* child_frame_window =
326 parent->window()->connection()->NewWindow();
327 parent->window()->AddChild(child_frame_window);
328
329 scoped_ptr<WindowAndFrame> window_and_frame(new WindowAndFrame);
330 window_and_frame->set_window(child_frame_window);
331
332 mojo::Map<mojo::String, mojo::Array<uint8_t>> client_properties;
333 client_properties.mark_non_null();
334 parent->server_frame()->OnCreatedFrame(
335 window_and_frame->GetServerFrameRequest(),
336 window_and_frame->GetFrameClientPtr(), child_frame_window->id(),
337 std::move(client_properties));
338 frame_tree_delegate()->WaitForCreateFrame();
339 return HasFatalFailure() ? nullptr : std::move(window_and_frame);
340 }
341
342 // Runs a message loop until the data necessary to represent to a client side
343 // frame has been obtained.
344 scoped_ptr<WindowAndFrame> WaitForViewAndFrame() {
345 DCHECK(!window_and_frame_);
346 window_and_frame_.reset(new WindowAndFrame);
347 window_and_frame_->WaitForViewAndFrame();
348 return std::move(window_and_frame_);
349 }
350
351 private:
352 // ApplicationTestBase:
353 mojo::ShellClient* GetShellClient() override { return this; }
354
355 // mojo::ShellClient implementation.
356 bool AcceptConnection(mojo::Connection* connection) override {
357 connection->AddService<mus::mojom::WindowTreeClient>(this);
358 connection->AddService<mojom::FrameClient>(this);
359 return true;
360 }
361
362 // Overridden from WindowTreeDelegate:
363 void OnEmbed(Window* root) override {
364 most_recent_connection_ = root->connection();
365 QuitRunLoop();
366 }
367 void OnConnectionLost(WindowTreeConnection* connection) override {}
368
369 // Overridden from testing::Test:
370 void SetUp() override {
371 ApplicationTestBase::SetUp();
372
373 mus::CreateWindowTreeHost(shell(), this, &host_, nullptr);
374
375 ASSERT_TRUE(DoRunLoopWithTimeout());
376 std::swap(window_manager_, most_recent_connection_);
377
378 // Creates a FrameTree, which creates a single frame. Wait for the
379 // FrameClient to be connected to.
380 frame_tree_delegate_.reset(new TestFrameTreeDelegate(shell()));
381 scoped_ptr<FrameConnection> frame_connection =
382 CreateFrameConnection(shell(), shell_url());
383 mojom::FrameClient* frame_client = frame_connection->frame_client();
384 mus::mojom::WindowTreeClientPtr window_tree_client =
385 frame_connection->GetWindowTreeClient();
386 mus::Window* frame_root_view = window_manager()->NewWindow();
387 (*window_manager()->GetRoots().begin())->AddChild(frame_root_view);
388 frame_tree_.reset(new FrameTree(
389 0u, frame_root_view, std::move(window_tree_client),
390 frame_tree_delegate_.get(), frame_client, std::move(frame_connection),
391 Frame::ClientPropertyMap(), base::TimeTicks::Now()));
392 root_window_and_frame_ = WaitForViewAndFrame();
393 }
394
395 // Overridden from testing::Test:
396 void TearDown() override {
397 root_window_and_frame_.reset();
398 frame_tree_.reset();
399 frame_tree_delegate_.reset();
400 ApplicationTestBase::TearDown();
401 }
402
403 // Overridden from mojo::InterfaceFactory<mus::mojom::WindowTreeClient>:
404 void Create(
405 mojo::Connection* connection,
406 mojo::InterfaceRequest<mus::mojom::WindowTreeClient> request) override {
407 if (window_and_frame_) {
408 mus::WindowTreeConnection::Create(
409 window_and_frame_.get(), std::move(request),
410 mus::WindowTreeConnection::CreateType::DONT_WAIT_FOR_EMBED);
411 } else {
412 mus::WindowTreeConnection::Create(
413 this, std::move(request),
414 mus::WindowTreeConnection::CreateType::DONT_WAIT_FOR_EMBED);
415 }
416 }
417
418 // Overridden from mojo::InterfaceFactory<mojom::FrameClient>:
419 void Create(mojo::Connection* connection,
420 mojo::InterfaceRequest<mojom::FrameClient> request) override {
421 ASSERT_TRUE(window_and_frame_);
422 window_and_frame_->Bind(std::move(request));
423 }
424
425 scoped_ptr<TestFrameTreeDelegate> frame_tree_delegate_;
426 scoped_ptr<FrameTree> frame_tree_;
427 scoped_ptr<WindowAndFrame> root_window_and_frame_;
428
429 mus::mojom::WindowTreeHostPtr host_;
430
431 // Used to receive the most recent window manager loaded by an embed action.
432 WindowTreeConnection* most_recent_connection_;
433 // The Window Manager connection held by the window manager (app running at
434 // the
435 // root window).
436 WindowTreeConnection* window_manager_;
437
438 scoped_ptr<WindowAndFrame> window_and_frame_;
439
440 MOJO_DISALLOW_COPY_AND_ASSIGN(FrameTest);
441 };
442
443 // Verifies the FrameData supplied to the root FrameClient::OnConnect().
444 TEST_F(FrameTest, RootFrameClientConnectData) {
445 mojo::Array<mojom::FrameDataPtr> frames =
446 root_window_and_frame()->test_frame_client()->connect_frames();
447 ASSERT_EQ(1u, frames.size());
448 EXPECT_EQ(root_window_and_frame()->window()->id(), frames[0]->frame_id);
449 EXPECT_EQ(0u, frames[0]->parent_id);
450 }
451
452 // Verifies the FrameData supplied to a child FrameClient::OnConnect().
453 // Crashes on linux_chromium_rel_ng only. http://crbug.com/567337
454 #if defined(OS_LINUX)
455 #define MAYBE_ChildFrameClientConnectData DISABLED_ChildFrameClientConnectData
456 #else
457 #define MAYBE_ChildFrameClientConnectData ChildFrameClientConnectData
458 #endif
459 TEST_F(FrameTest, MAYBE_ChildFrameClientConnectData) {
460 scoped_ptr<WindowAndFrame> child_view_and_frame(
461 CreateChildWindowAndFrame(root_window_and_frame()));
462 ASSERT_TRUE(child_view_and_frame);
463 // Initially created child frames don't get OnConnect().
464 EXPECT_EQ(0, child_view_and_frame->test_frame_client()->connect_count());
465
466 scoped_ptr<WindowAndFrame> navigated_child_view_and_frame =
467 NavigateFrame(child_view_and_frame.get());
468
469 mojo::Array<mojom::FrameDataPtr> frames_in_child =
470 navigated_child_view_and_frame->test_frame_client()->connect_frames();
471 EXPECT_EQ(child_view_and_frame->window()->id(),
472 navigated_child_view_and_frame->window()->id());
473 // We expect 2 frames. One for the root, one for the child.
474 ASSERT_EQ(2u, frames_in_child.size());
475 EXPECT_EQ(frame_tree()->root()->id(), frames_in_child[0]->frame_id);
476 EXPECT_EQ(0u, frames_in_child[0]->parent_id);
477 EXPECT_EQ(navigated_child_view_and_frame->window()->id(),
478 frames_in_child[1]->frame_id);
479 EXPECT_EQ(frame_tree()->root()->id(), frames_in_child[1]->parent_id);
480 }
481
482 TEST_F(FrameTest, OnViewEmbeddedInFrameDisconnected) {
483 scoped_ptr<WindowAndFrame> child_view_and_frame(
484 CreateChildWindowAndFrame(root_window_and_frame()));
485 ASSERT_TRUE(child_view_and_frame);
486
487 scoped_ptr<WindowAndFrame> navigated_child_view_and_frame =
488 NavigateFrame(child_view_and_frame.get());
489
490 // Delete the WindowTreeConnection for the child, which should trigger
491 // notification.
492 delete navigated_child_view_and_frame->window()->connection();
493 ASSERT_EQ(1u, frame_tree()->root()->children().size());
494 ASSERT_NO_FATAL_FAILURE(frame_tree_delegate()->WaitForFrameDisconnected(
495 frame_tree()->root()->children()[0]));
496 ASSERT_EQ(1u, frame_tree()->root()->children().size());
497 }
498
499 TEST_F(FrameTest, NotifyRemoteParentWithLoadingState) {
500 scoped_ptr<WindowAndFrame> child_view_and_frame(
501 CreateChildWindowAndFrame(root_window_and_frame()));
502 uint32_t child_frame_id = child_view_and_frame->window()->id();
503
504 {
505 base::RunLoop run_loop;
506 root_window_and_frame()
507 ->test_frame_client()
508 ->set_on_loading_state_changed_callback(run_loop.QuitClosure());
509
510 child_view_and_frame->server_frame()->LoadingStateChanged(true, .5);
511
512 run_loop.Run();
513
514 uint32_t frame_id = 0;
515 bool loading = false;
516 root_window_and_frame()
517 ->test_frame_client()
518 ->last_loading_state_changed_notification(&frame_id, &loading);
519 EXPECT_EQ(child_frame_id, frame_id);
520 EXPECT_TRUE(loading);
521 }
522 {
523 base::RunLoop run_loop;
524 root_window_and_frame()
525 ->test_frame_client()
526 ->set_on_loading_state_changed_callback(run_loop.QuitClosure());
527
528 ASSERT_TRUE(child_view_and_frame);
529 ASSERT_TRUE(child_view_and_frame->server_frame());
530
531 child_view_and_frame->server_frame()->LoadingStateChanged(false, 1);
532
533 run_loop.Run();
534
535 uint32_t frame_id = 0;
536 bool loading = false;
537 root_window_and_frame()
538 ->test_frame_client()
539 ->last_loading_state_changed_notification(&frame_id, &loading);
540 EXPECT_EQ(child_frame_id, frame_id);
541 EXPECT_FALSE(loading);
542 }
543 }
544
545 TEST_F(FrameTest, NotifyRemoteParentWithLoadEvent) {
546 scoped_ptr<WindowAndFrame> child_view_and_frame(
547 CreateChildWindowAndFrame(root_window_and_frame()));
548 uint32_t child_frame_id = child_view_and_frame->window()->id();
549
550 base::RunLoop run_loop;
551 root_window_and_frame()
552 ->test_frame_client()
553 ->set_on_dispatch_load_event_callback(run_loop.QuitClosure());
554
555 child_view_and_frame->server_frame()->DispatchLoadEventToParent();
556
557 run_loop.Run();
558
559 uint32_t frame_id = root_window_and_frame()
560 ->test_frame_client()
561 ->last_dispatch_load_event_frame_id();
562 EXPECT_EQ(child_frame_id, frame_id);
563 }
564
565 TEST_F(FrameTest, PassAlongNavigationStartTime) {
566 scoped_ptr<WindowAndFrame> child_view_and_frame(
567 CreateChildWindowAndFrame(root_window_and_frame()));
568 ASSERT_TRUE(child_view_and_frame);
569
570 base::TimeTicks navigation_start_time = base::TimeTicks::FromInternalValue(1);
571 scoped_ptr<WindowAndFrame> navigated_child_view_and_frame =
572 NavigateFrameWithStartTime(child_view_and_frame.get(),
573 navigation_start_time);
574 EXPECT_EQ(navigation_start_time,
575 navigated_child_view_and_frame->test_frame_client()
576 ->last_navigation_start_time());
577 }
578
579 } // namespace web_view
OLDNEW
« no previous file with comments | « components/web_view/frame.cc ('k') | components/web_view/frame_connection.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698