OLD | NEW |
| (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 | |
OLD | NEW |