1 // 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.
5 #include "content/browser/renderer_host/render_widget_host_view_aura.h"
7 #include "base/basictypes.h"
8 #include "base/message_loop.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "content/browser/renderer_host/render_widget_host_delegate.h"
11 #include "content/browser/renderer_host/render_widget_host_impl.h"
12 #include "content/common/input_messages.h"
13 #include "content/common/view_messages.h"
14 #include "content/public/browser/render_widget_host_view.h"
15 #include "content/public/test/mock_render_process_host.h"
16 #include "content/public/test/test_browser_context.h"
17 #include "ipc/ipc_test_sink.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "ui/aura/client/aura_constants.h"
20 #include "ui/aura/env.h"
21 #include "ui/aura/root_window.h"
22 #include "ui/aura/test/aura_test_helper.h"
23 #include "ui/aura/test/test_cursor_client.h"
24 #include "ui/aura/test/test_screen.h"
25 #include "ui/aura/test/test_window_delegate.h"
26 #include "ui/aura/window.h"
27 #include "ui/aura/window_observer.h"
28 #include "ui/base/events/event.h"
29 #include "ui/base/events/event_utils.h"
30 #include "ui/base/ui_base_types.h"
34 class MockRenderWidgetHostDelegate
: public RenderWidgetHostDelegate
{
36 MockRenderWidgetHostDelegate() {}
37 virtual ~MockRenderWidgetHostDelegate() {}
40 // Simple observer that keeps track of changes to a window for tests.
41 class TestWindowObserver
: public aura::WindowObserver
{
43 explicit TestWindowObserver(aura::Window
* window_to_observe
)
44 : window_(window_to_observe
) {
45 window_
->AddObserver(this);
47 virtual ~TestWindowObserver() {
49 window_
->RemoveObserver(this);
52 bool destroyed() const { return destroyed_
; }
54 // aura::WindowObserver overrides:
55 virtual void OnWindowDestroyed(aura::Window
* window
) OVERRIDE
{
56 CHECK_EQ(window
, window_
);
62 // Window that we're observing, or NULL if it's been destroyed.
63 aura::Window
* window_
;
65 // Was |window_| destroyed?
68 DISALLOW_COPY_AND_ASSIGN(TestWindowObserver
);
71 class RenderWidgetHostViewAuraTest
: public testing::Test
{
73 RenderWidgetHostViewAuraTest() {}
75 virtual void SetUp() {
76 aura_test_helper_
.reset(new aura::test::AuraTestHelper(&message_loop_
));
77 aura_test_helper_
->SetUp();
79 browser_context_
.reset(new TestBrowserContext
);
80 MockRenderProcessHost
* process_host
=
81 new MockRenderProcessHost(browser_context_
.get());
83 sink_
= &process_host
->sink();
85 parent_host_
= new RenderWidgetHostImpl(
86 &delegate_
, process_host
, MSG_ROUTING_NONE
);
87 parent_view_
= static_cast<RenderWidgetHostViewAura
*>(
88 RenderWidgetHostView::CreateViewForWidget(parent_host_
));
89 parent_view_
->InitAsChild(NULL
);
90 parent_view_
->GetNativeView()->SetDefaultParentByRootWindow(
91 aura_test_helper_
->root_window(), gfx::Rect());
93 widget_host_
= new RenderWidgetHostImpl(
94 &delegate_
, process_host
, MSG_ROUTING_NONE
);
96 view_
= static_cast<RenderWidgetHostViewAura
*>(
97 RenderWidgetHostView::CreateViewForWidget(widget_host_
));
100 virtual void TearDown() {
106 parent_view_
->Destroy();
109 browser_context_
.reset();
110 aura_test_helper_
->TearDown();
112 message_loop_
.DeleteSoon(FROM_HERE
, browser_context_
.release());
113 message_loop_
.RunUntilIdle();
117 base::MessageLoopForUI message_loop_
;
118 scoped_ptr
<aura::test::AuraTestHelper
> aura_test_helper_
;
119 scoped_ptr
<BrowserContext
> browser_context_
;
120 MockRenderWidgetHostDelegate delegate_
;
122 // Tests should set these to NULL if they've already triggered their
124 RenderWidgetHostImpl
* parent_host_
;
125 RenderWidgetHostViewAura
* parent_view_
;
127 // Tests should set these to NULL if they've already triggered their
129 RenderWidgetHostImpl
* widget_host_
;
130 RenderWidgetHostViewAura
* view_
;
132 IPC::TestSink
* sink_
;
135 DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewAuraTest
);
140 // Checks that a fullscreen view has the correct show-state and receives the
142 TEST_F(RenderWidgetHostViewAuraTest
, FocusFullscreen
) {
143 view_
->InitAsFullscreen(parent_view_
);
144 aura::Window
* window
= view_
->GetNativeView();
145 ASSERT_TRUE(window
!= NULL
);
146 EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN
,
147 window
->GetProperty(aura::client::kShowStateKey
));
149 // Check that we requested and received the focus.
150 EXPECT_TRUE(window
->HasFocus());
152 // Check that we'll also say it's okay to activate the window when there's an
153 // ActivationClient defined.
154 EXPECT_TRUE(view_
->ShouldActivate());
157 // Checks that a fullscreen view is destroyed when it loses the focus.
158 TEST_F(RenderWidgetHostViewAuraTest
, DestroyFullscreenOnBlur
) {
159 view_
->InitAsFullscreen(parent_view_
);
160 aura::Window
* window
= view_
->GetNativeView();
161 ASSERT_TRUE(window
!= NULL
);
162 ASSERT_TRUE(window
->HasFocus());
164 // After we create and focus another window, the RWHVA's window should be
166 TestWindowObserver
observer(window
);
167 aura::test::TestWindowDelegate delegate
;
168 scoped_ptr
<aura::Window
> sibling(new aura::Window(&delegate
));
169 sibling
->Init(ui::LAYER_TEXTURED
);
171 window
->parent()->AddChild(sibling
.get());
173 ASSERT_TRUE(sibling
->HasFocus());
174 ASSERT_TRUE(observer
.destroyed());
180 // Checks that IME-composition-event state is maintained correctly.
181 TEST_F(RenderWidgetHostViewAuraTest
, SetCompositionText
) {
182 view_
->InitAsChild(NULL
);
185 ui::CompositionText composition_text
;
186 composition_text
.text
= ASCIIToUTF16("|a|b");
189 composition_text
.underlines
.push_back(
190 ui::CompositionUnderline(0, 3, 0xff000000, true));
192 // Non-focused segment
193 composition_text
.underlines
.push_back(
194 ui::CompositionUnderline(3, 4, 0xff000000, false));
196 const ui::CompositionUnderlines
& underlines
= composition_text
.underlines
;
198 // Caret is at the end. (This emulates Japanese MSIME 2007 and later)
199 composition_text
.selection
= ui::Range(4);
201 sink_
->ClearMessages();
202 view_
->SetCompositionText(composition_text
);
203 EXPECT_TRUE(view_
->has_composition_text_
);
205 const IPC::Message
* msg
=
206 sink_
->GetFirstMessageMatching(ViewMsg_ImeSetComposition::ID
);
207 ASSERT_TRUE(msg
!= NULL
);
209 ViewMsg_ImeSetComposition::Param params
;
210 ViewMsg_ImeSetComposition::Read(msg
, ¶ms
);
212 EXPECT_EQ(composition_text
.text
, params
.a
);
214 ASSERT_EQ(underlines
.size(), params
.b
.size());
215 for (size_t i
= 0; i
< underlines
.size(); ++i
) {
216 EXPECT_EQ(underlines
[i
].start_offset
, params
.b
[i
].startOffset
);
217 EXPECT_EQ(underlines
[i
].end_offset
, params
.b
[i
].endOffset
);
218 EXPECT_EQ(underlines
[i
].color
, params
.b
[i
].color
);
219 EXPECT_EQ(underlines
[i
].thick
, params
.b
[i
].thick
);
222 EXPECT_EQ(4, params
.c
) << "Should be the same to the caret pos";
223 EXPECT_EQ(4, params
.d
) << "Should be the same to the caret pos";
226 view_
->ImeCancelComposition();
227 EXPECT_FALSE(view_
->has_composition_text_
);
230 // Checks that touch-event state is maintained correctly.
231 TEST_F(RenderWidgetHostViewAuraTest
, TouchEventState
) {
232 view_
->InitAsChild(NULL
);
235 // Start with no touch-event handler in the renderer.
236 widget_host_
->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, false));
237 EXPECT_FALSE(widget_host_
->ShouldForwardTouchEvent());
239 ui::TouchEvent
press(ui::ET_TOUCH_PRESSED
,
242 ui::EventTimeForNow());
243 ui::TouchEvent
move(ui::ET_TOUCH_MOVED
,
246 ui::EventTimeForNow());
247 ui::TouchEvent
release(ui::ET_TOUCH_RELEASED
,
250 ui::EventTimeForNow());
252 view_
->OnTouchEvent(&press
);
253 EXPECT_FALSE(press
.handled());
254 EXPECT_EQ(WebKit::WebInputEvent::TouchStart
, view_
->touch_event_
.type
);
255 EXPECT_EQ(1U, view_
->touch_event_
.touchesLength
);
256 EXPECT_EQ(WebKit::WebTouchPoint::StatePressed
,
257 view_
->touch_event_
.touches
[0].state
);
259 view_
->OnTouchEvent(&move
);
260 EXPECT_FALSE(move
.handled());
261 EXPECT_EQ(WebKit::WebInputEvent::TouchMove
, view_
->touch_event_
.type
);
262 EXPECT_EQ(1U, view_
->touch_event_
.touchesLength
);
263 EXPECT_EQ(WebKit::WebTouchPoint::StateMoved
,
264 view_
->touch_event_
.touches
[0].state
);
266 view_
->OnTouchEvent(&release
);
267 EXPECT_FALSE(release
.handled());
268 EXPECT_EQ(WebKit::WebInputEvent::TouchEnd
, view_
->touch_event_
.type
);
269 EXPECT_EQ(0U, view_
->touch_event_
.touchesLength
);
271 // Now install some touch-event handlers and do the same steps. The touch
272 // events should now be consumed. However, the touch-event state should be
273 // updated as before.
274 widget_host_
->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true));
275 EXPECT_TRUE(widget_host_
->ShouldForwardTouchEvent());
277 view_
->OnTouchEvent(&press
);
278 EXPECT_TRUE(press
.stopped_propagation());
279 EXPECT_EQ(WebKit::WebInputEvent::TouchStart
, view_
->touch_event_
.type
);
280 EXPECT_EQ(1U, view_
->touch_event_
.touchesLength
);
281 EXPECT_EQ(WebKit::WebTouchPoint::StatePressed
,
282 view_
->touch_event_
.touches
[0].state
);
284 view_
->OnTouchEvent(&move
);
285 EXPECT_TRUE(move
.stopped_propagation());
286 EXPECT_EQ(WebKit::WebInputEvent::TouchMove
, view_
->touch_event_
.type
);
287 EXPECT_EQ(1U, view_
->touch_event_
.touchesLength
);
288 EXPECT_EQ(WebKit::WebTouchPoint::StateMoved
,
289 view_
->touch_event_
.touches
[0].state
);
291 view_
->OnTouchEvent(&release
);
292 EXPECT_TRUE(release
.stopped_propagation());
293 EXPECT_EQ(WebKit::WebInputEvent::TouchEnd
, view_
->touch_event_
.type
);
294 EXPECT_EQ(0U, view_
->touch_event_
.touchesLength
);
296 // Now start a touch event, and remove the event-handlers before the release.
297 view_
->OnTouchEvent(&press
);
298 EXPECT_TRUE(press
.stopped_propagation());
299 EXPECT_EQ(WebKit::WebInputEvent::TouchStart
, view_
->touch_event_
.type
);
300 EXPECT_EQ(1U, view_
->touch_event_
.touchesLength
);
301 EXPECT_EQ(WebKit::WebTouchPoint::StatePressed
,
302 view_
->touch_event_
.touches
[0].state
);
304 widget_host_
->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, false));
305 EXPECT_FALSE(widget_host_
->ShouldForwardTouchEvent());
307 ui::TouchEvent
move2(ui::ET_TOUCH_MOVED
, gfx::Point(20, 20), 0,
308 base::Time::NowFromSystemTime() - base::Time());
309 view_
->OnTouchEvent(&move2
);
310 EXPECT_FALSE(move2
.handled());
311 EXPECT_EQ(WebKit::WebInputEvent::TouchMove
, view_
->touch_event_
.type
);
312 EXPECT_EQ(1U, view_
->touch_event_
.touchesLength
);
313 EXPECT_EQ(WebKit::WebTouchPoint::StateMoved
,
314 view_
->touch_event_
.touches
[0].state
);
316 ui::TouchEvent
release2(ui::ET_TOUCH_RELEASED
, gfx::Point(20, 20), 0,
317 base::Time::NowFromSystemTime() - base::Time());
318 view_
->OnTouchEvent(&release2
);
319 EXPECT_FALSE(release2
.handled());
320 EXPECT_EQ(WebKit::WebInputEvent::TouchEnd
, view_
->touch_event_
.type
);
321 EXPECT_EQ(0U, view_
->touch_event_
.touchesLength
);
324 // Checks that touch-events are queued properly when there is a touch-event
325 // handler on the page.
326 TEST_F(RenderWidgetHostViewAuraTest
, TouchEventSyncAsync
) {
327 view_
->InitAsChild(NULL
);
330 widget_host_
->OnMessageReceived(ViewHostMsg_HasTouchEventHandlers(0, true));
331 EXPECT_TRUE(widget_host_
->ShouldForwardTouchEvent());
333 ui::TouchEvent
press(ui::ET_TOUCH_PRESSED
,
336 ui::EventTimeForNow());
337 ui::TouchEvent
move(ui::ET_TOUCH_MOVED
,
340 ui::EventTimeForNow());
341 ui::TouchEvent
release(ui::ET_TOUCH_RELEASED
,
344 ui::EventTimeForNow());
346 view_
->OnTouchEvent(&press
);
347 EXPECT_TRUE(press
.stopped_propagation());
348 EXPECT_EQ(WebKit::WebInputEvent::TouchStart
, view_
->touch_event_
.type
);
349 EXPECT_EQ(1U, view_
->touch_event_
.touchesLength
);
350 EXPECT_EQ(WebKit::WebTouchPoint::StatePressed
,
351 view_
->touch_event_
.touches
[0].state
);
353 view_
->OnTouchEvent(&move
);
354 EXPECT_TRUE(move
.stopped_propagation());
355 EXPECT_EQ(WebKit::WebInputEvent::TouchMove
, view_
->touch_event_
.type
);
356 EXPECT_EQ(1U, view_
->touch_event_
.touchesLength
);
357 EXPECT_EQ(WebKit::WebTouchPoint::StateMoved
,
358 view_
->touch_event_
.touches
[0].state
);
360 // Send the same move event. Since the point hasn't moved, it won't affect the
361 // queue. However, the view should consume the event.
362 view_
->OnTouchEvent(&move
);
363 EXPECT_TRUE(move
.stopped_propagation());
364 EXPECT_EQ(WebKit::WebInputEvent::TouchMove
, view_
->touch_event_
.type
);
365 EXPECT_EQ(1U, view_
->touch_event_
.touchesLength
);
366 EXPECT_EQ(WebKit::WebTouchPoint::StateMoved
,
367 view_
->touch_event_
.touches
[0].state
);
369 view_
->OnTouchEvent(&release
);
370 EXPECT_TRUE(release
.stopped_propagation());
371 EXPECT_EQ(WebKit::WebInputEvent::TouchEnd
, view_
->touch_event_
.type
);
372 EXPECT_EQ(0U, view_
->touch_event_
.touchesLength
);
375 TEST_F(RenderWidgetHostViewAuraTest
, PhysicalBackingSizeWithScale
) {
376 view_
->InitAsChild(NULL
);
377 view_
->GetNativeView()->SetDefaultParentByRootWindow(
378 parent_view_
->GetNativeView()->GetRootWindow(), gfx::Rect());
379 sink_
->ClearMessages();
380 view_
->SetSize(gfx::Size(100, 100));
381 EXPECT_EQ("100x100", view_
->GetPhysicalBackingSize().ToString());
382 EXPECT_EQ(1u, sink_
->message_count());
383 EXPECT_EQ(ViewMsg_Resize::ID
, sink_
->GetMessageAt(0)->type());
385 const IPC::Message
* msg
= sink_
->GetMessageAt(0);
386 EXPECT_EQ(ViewMsg_Resize::ID
, msg
->type());
387 ViewMsg_Resize::Param params
;
388 ViewMsg_Resize::Read(msg
, ¶ms
);
389 EXPECT_EQ("100x100", params
.a
.new_size
.ToString()); // dip size
391 params
.a
.physical_backing_size
.ToString()); // backing size
394 widget_host_
->ResetSizeAndRepaintPendingFlags();
395 sink_
->ClearMessages();
397 aura_test_helper_
->test_screen()->SetDeviceScaleFactor(2.0f
);
398 EXPECT_EQ("200x200", view_
->GetPhysicalBackingSize().ToString());
399 // Extra ScreenInfoChanged message for |parent_view_|.
400 EXPECT_EQ(1u, sink_
->message_count());
402 const IPC::Message
* msg
= sink_
->GetMessageAt(0);
403 EXPECT_EQ(ViewMsg_Resize::ID
, msg
->type());
404 ViewMsg_Resize::Param params
;
405 ViewMsg_Resize::Read(msg
, ¶ms
);
406 EXPECT_EQ(2.0f
, params
.a
.screen_info
.deviceScaleFactor
);
407 EXPECT_EQ("100x100", params
.a
.new_size
.ToString()); // dip size
409 params
.a
.physical_backing_size
.ToString()); // backing size
412 widget_host_
->ResetSizeAndRepaintPendingFlags();
413 sink_
->ClearMessages();
415 aura_test_helper_
->test_screen()->SetDeviceScaleFactor(1.0f
);
416 // Extra ScreenInfoChanged message for |parent_view_|.
417 EXPECT_EQ(1u, sink_
->message_count());
418 EXPECT_EQ("100x100", view_
->GetPhysicalBackingSize().ToString());
420 const IPC::Message
* msg
= sink_
->GetMessageAt(0);
421 EXPECT_EQ(ViewMsg_Resize::ID
, msg
->type());
422 ViewMsg_Resize::Param params
;
423 ViewMsg_Resize::Read(msg
, ¶ms
);
424 EXPECT_EQ(1.0f
, params
.a
.screen_info
.deviceScaleFactor
);
425 EXPECT_EQ("100x100", params
.a
.new_size
.ToString()); // dip size
427 params
.a
.physical_backing_size
.ToString()); // backing size
431 // Checks that InputMsg_CursorVisibilityChange IPC messages are dispatched
432 // to the renderer at the correct times.
433 TEST_F(RenderWidgetHostViewAuraTest
, CursorVisibilityChange
) {
434 view_
->InitAsChild(NULL
);
435 view_
->GetNativeView()->SetDefaultParentByRootWindow(
436 parent_view_
->GetNativeView()->GetRootWindow(), gfx::Rect());
437 view_
->SetSize(gfx::Size(100, 100));
439 aura::test::TestCursorClient
cursor_client(
440 parent_view_
->GetNativeView()->GetRootWindow());
442 cursor_client
.AddObserver(view_
);
444 // Expect a message the first time the cursor is shown.
446 sink_
->ClearMessages();
447 cursor_client
.ShowCursor();
448 EXPECT_EQ(1u, sink_
->message_count());
449 EXPECT_TRUE(sink_
->GetUniqueMessageMatching(
450 InputMsg_CursorVisibilityChange::ID
));
452 // No message expected if the renderer already knows the cursor is visible.
453 sink_
->ClearMessages();
454 cursor_client
.ShowCursor();
455 EXPECT_EQ(0u, sink_
->message_count());
457 // Hiding the cursor should send a message.
458 sink_
->ClearMessages();
459 cursor_client
.HideCursor();
460 EXPECT_EQ(1u, sink_
->message_count());
461 EXPECT_TRUE(sink_
->GetUniqueMessageMatching(
462 InputMsg_CursorVisibilityChange::ID
));
464 // No message expected if the renderer already knows the cursor is invisible.
465 sink_
->ClearMessages();
466 cursor_client
.HideCursor();
467 EXPECT_EQ(0u, sink_
->message_count());
469 // No messages should be sent while the view is invisible.
471 sink_
->ClearMessages();
472 cursor_client
.ShowCursor();
473 EXPECT_EQ(0u, sink_
->message_count());
474 cursor_client
.HideCursor();
475 EXPECT_EQ(0u, sink_
->message_count());
477 // Show the view. Since the cursor was invisible when the view was hidden,
478 // no message should be sent.
479 sink_
->ClearMessages();
481 EXPECT_FALSE(sink_
->GetUniqueMessageMatching(
482 InputMsg_CursorVisibilityChange::ID
));
484 // No message expected if the renderer already knows the cursor is invisible.
485 sink_
->ClearMessages();
486 cursor_client
.HideCursor();
487 EXPECT_EQ(0u, sink_
->message_count());
489 // Showing the cursor should send a message.
490 sink_
->ClearMessages();
491 cursor_client
.ShowCursor();
492 EXPECT_EQ(1u, sink_
->message_count());
493 EXPECT_TRUE(sink_
->GetUniqueMessageMatching(
494 InputMsg_CursorVisibilityChange::ID
));
496 // No messages should be sent while the view is invisible.
498 sink_
->ClearMessages();
499 cursor_client
.HideCursor();
500 EXPECT_EQ(0u, sink_
->message_count());
502 // Show the view. Since the cursor was visible when the view was hidden,
503 // a message is expected to be sent.
504 sink_
->ClearMessages();
506 EXPECT_TRUE(sink_
->GetUniqueMessageMatching(
507 InputMsg_CursorVisibilityChange::ID
));
509 cursor_client
.RemoveObserver(view_
);
512 } // namespace content