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 "base/basictypes.h"
7 #include "base/shared_memory.h"
8 #include "base/string_util.h"
9 #include "base/utf_string_conversions.h"
10 #include "content/common/intents_messages.h"
11 #include "content/common/view_messages.h"
12 #include "content/public/browser/native_web_keyboard_event.h"
13 #include "content/public/browser/web_ui_controller_factory.h"
14 #include "content/public/common/bindings_policy.h"
15 #include "content/public/common/url_constants.h"
16 #include "content/public/test/render_view_test.h"
17 #include "content/renderer/render_view_impl.h"
18 #include "content/shell/shell_content_browser_client.h"
19 #include "content/shell/shell_content_client.h"
20 #include "content/test/mock_keyboard.h"
21 #include "net/base/net_errors.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebData.h"
24 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebHTTPBody.h"
25 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
26 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebURLError.h"
27 #include "third_party/WebKit/Source/WebKit/chromium/public/WebHistoryItem.h"
28 #include "third_party/WebKit/Source/WebKit/chromium/public/WebIntentServiceInfo.h"
29 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
30 #include "third_party/WebKit/Source/WebKit/chromium/public/WebWindowFeatures.h"
31 #include "ui/base/keycodes/keyboard_codes.h"
32 #include "ui/base/range/range.h"
33 #include "ui/gfx/codec/jpeg_codec.h"
34 #include "webkit/glue/glue_serialize.h"
35 #include "webkit/glue/web_io_operators.h"
37 #if defined(OS_LINUX) && !defined(USE_AURA)
38 #include "ui/base/gtk/event_synthesis_gtk.h"
42 #include "ui/base/events/event.h"
45 #if defined(USE_AURA) && defined(USE_X11)
47 #include "ui/base/events/event_constants.h"
48 #include "ui/base/keycodes/keyboard_code_conversion.h"
49 #include "ui/base/x/x11_util.h"
52 using WebKit::WebFrame
;
53 using WebKit::WebInputEvent
;
54 using WebKit::WebMouseEvent
;
55 using WebKit::WebString
;
56 using WebKit::WebTextDirection
;
57 using WebKit::WebURLError
;
62 #if defined(USE_AURA) && defined(USE_X11)
63 // Converts MockKeyboard::Modifiers to ui::EventFlags.
64 int ConvertMockKeyboardModifier(MockKeyboard::Modifiers modifiers
) {
65 static struct ModifierMap
{
66 MockKeyboard::Modifiers src
;
69 { MockKeyboard::LEFT_SHIFT
, ui::EF_SHIFT_DOWN
},
70 { MockKeyboard::RIGHT_SHIFT
, ui::EF_SHIFT_DOWN
},
71 { MockKeyboard::LEFT_CONTROL
, ui::EF_CONTROL_DOWN
},
72 { MockKeyboard::RIGHT_CONTROL
, ui::EF_CONTROL_DOWN
},
73 { MockKeyboard::LEFT_ALT
, ui::EF_ALT_DOWN
},
74 { MockKeyboard::RIGHT_ALT
, ui::EF_ALT_DOWN
},
77 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(kModifierMap
); ++i
) {
78 if (kModifierMap
[i
].src
& modifiers
) {
79 flags
|= kModifierMap
[i
].dst
;
86 class WebUITestWebUIControllerFactory
: public WebUIControllerFactory
{
88 virtual WebUIController
* CreateWebUIControllerForURL(
89 WebUI
* web_ui
, const GURL
& url
) const OVERRIDE
{
92 virtual WebUI::TypeID
GetWebUIType(BrowserContext
* browser_context
,
93 const GURL
& url
) const OVERRIDE
{
94 return WebUI::kNoWebUI
;
96 virtual bool UseWebUIForURL(BrowserContext
* browser_context
,
97 const GURL
& url
) const OVERRIDE
{
98 return GetContentClient()->HasWebUIScheme(url
);
100 virtual bool UseWebUIBindingsForURL(BrowserContext
* browser_context
,
101 const GURL
& url
) const OVERRIDE
{
102 return GetContentClient()->HasWebUIScheme(url
);
104 virtual bool IsURLAcceptableForWebUI(
105 BrowserContext
* browser_context
,
107 bool data_urls_allowed
) const OVERRIDE
{
112 class WebUITestClient
: public ShellContentClient
{
117 virtual bool HasWebUIScheme(const GURL
& url
) const OVERRIDE
{
118 return url
.SchemeIs(chrome::kChromeUIScheme
);
122 class WebUITestBrowserClient
: public ShellContentBrowserClient
{
124 WebUITestBrowserClient() {}
126 virtual WebUIControllerFactory
* GetWebUIControllerFactory() OVERRIDE
{
131 WebUITestWebUIControllerFactory factory_
;
136 class RenderViewImplTest
: public RenderViewTest
{
138 RenderViewImplTest() {
139 // Attach a pseudo keyboard device to this object.
140 mock_keyboard_
.reset(new MockKeyboard());
143 RenderViewImpl
* view() {
144 return static_cast<RenderViewImpl
*>(view_
);
147 // Sends IPC messages that emulates a key-press event.
148 int SendKeyEvent(MockKeyboard::Layout layout
,
150 MockKeyboard::Modifiers modifiers
,
153 // Retrieve the Unicode character for the given tuple (keyboard-layout,
154 // key-code, and modifiers).
155 // Exit when a keyboard-layout driver cannot assign a Unicode character to
156 // the tuple to prevent sending an invalid key code to the RenderView object.
157 CHECK(mock_keyboard_
.get());
159 int length
= mock_keyboard_
->GetCharacters(layout
, key_code
, modifiers
,
164 // Create IPC messages from Windows messages and send them to our
166 // A keyboard event of Windows consists of three Windows messages:
167 // WM_KEYDOWN, WM_CHAR, and WM_KEYUP.
168 // WM_KEYDOWN and WM_KEYUP sends virtual-key codes. On the other hand,
169 // WM_CHAR sends a composed Unicode character.
170 MSG msg1
= { NULL
, WM_KEYDOWN
, key_code
, 0 };
171 #if defined(USE_AURA)
172 ui::KeyEvent
evt1(msg1
, false);
173 NativeWebKeyboardEvent
keydown_event(&evt1
);
175 NativeWebKeyboardEvent
keydown_event(msg1
);
177 SendNativeKeyEvent(keydown_event
);
179 MSG msg2
= { NULL
, WM_CHAR
, (*output
)[0], 0 };
180 #if defined(USE_AURA)
181 ui::KeyEvent
evt2(msg2
, true);
182 NativeWebKeyboardEvent
char_event(&evt2
);
184 NativeWebKeyboardEvent
char_event(msg2
);
186 SendNativeKeyEvent(char_event
);
188 MSG msg3
= { NULL
, WM_KEYUP
, key_code
, 0 };
189 #if defined(USE_AURA)
190 ui::KeyEvent
evt3(msg3
, false);
191 NativeWebKeyboardEvent
keyup_event(&evt3
);
193 NativeWebKeyboardEvent
keyup_event(msg3
);
195 SendNativeKeyEvent(keyup_event
);
198 #elif defined(USE_AURA) && defined(USE_X11)
199 // We ignore |layout|, which means we are only testing the layout of the
200 // current locale. TODO(mazda): fix this to respect |layout|.
202 const int flags
= ConvertMockKeyboardModifier(modifiers
);
205 InitXKeyEventForTesting(ui::ET_KEY_PRESSED
,
206 static_cast<ui::KeyboardCode
>(key_code
),
209 ui::KeyEvent
event1(&xevent1
, false);
210 NativeWebKeyboardEvent
keydown_event(&event1
);
211 SendNativeKeyEvent(keydown_event
);
214 InitXKeyEventForTesting(ui::ET_KEY_PRESSED
,
215 static_cast<ui::KeyboardCode
>(key_code
),
218 ui::KeyEvent
event2(&xevent2
, true);
219 NativeWebKeyboardEvent
char_event(&event2
);
220 SendNativeKeyEvent(char_event
);
223 InitXKeyEventForTesting(ui::ET_KEY_RELEASED
,
224 static_cast<ui::KeyboardCode
>(key_code
),
227 ui::KeyEvent
event3(&xevent3
, false);
228 NativeWebKeyboardEvent
keyup_event(&event3
);
229 SendNativeKeyEvent(keyup_event
);
231 long c
= GetCharacterFromKeyCode(static_cast<ui::KeyboardCode
>(key_code
),
233 output
->assign(1, static_cast<char16
>(c
));
235 #elif defined(OS_LINUX)
236 // We ignore |layout|, which means we are only testing the layout of the
237 // current locale. TODO(estade): fix this to respect |layout|.
238 std::vector
<GdkEvent
*> events
;
239 ui::SynthesizeKeyPressEvents(
240 NULL
, static_cast<ui::KeyboardCode
>(key_code
),
241 modifiers
& (MockKeyboard::LEFT_CONTROL
| MockKeyboard::RIGHT_CONTROL
),
242 modifiers
& (MockKeyboard::LEFT_SHIFT
| MockKeyboard::RIGHT_SHIFT
),
243 modifiers
& (MockKeyboard::LEFT_ALT
| MockKeyboard::RIGHT_ALT
),
246 guint32 unicode_key
= 0;
247 for (size_t i
= 0; i
< events
.size(); ++i
) {
248 // Only send the up/down events for key press itself (skip the up/down
249 // events for the modifier keys).
250 if ((i
+ 1) == (events
.size() / 2) || i
== (events
.size() / 2)) {
251 unicode_key
= gdk_keyval_to_unicode(events
[i
]->key
.keyval
);
252 NativeWebKeyboardEvent
webkit_event(events
[i
]);
253 SendNativeKeyEvent(webkit_event
);
255 // Need to add a char event after the key down.
256 if (webkit_event
.type
== WebKit::WebInputEvent::RawKeyDown
) {
257 NativeWebKeyboardEvent char_event
= webkit_event
;
258 char_event
.type
= WebKit::WebInputEvent::Char
;
259 char_event
.skip_in_browser
= true;
260 SendNativeKeyEvent(char_event
);
263 gdk_event_free(events
[i
]);
266 output
->assign(1, static_cast<char16
>(unicode_key
));
275 scoped_ptr
<MockKeyboard
> mock_keyboard_
;
278 // Test that we get form state change notifications when input fields change.
279 TEST_F(RenderViewImplTest
, DISABLED_OnNavStateChanged
) {
280 // Don't want any delay for form state sync changes. This will still post a
281 // message so updates will get coalesced, but as soon as we spin the message
282 // loop, it will generate an update.
283 view()->set_send_content_state_immediately(true);
285 LoadHTML("<input type=\"text\" id=\"elt_text\"></input>");
287 // We should NOT have gotten a form state change notification yet.
288 EXPECT_FALSE(render_thread_
->sink().GetFirstMessageMatching(
289 ViewHostMsg_UpdateState::ID
));
290 render_thread_
->sink().ClearMessages();
292 // Change the value of the input. We should have gotten an update state
293 // notification. We need to spin the message loop to catch this update.
294 ExecuteJavaScript("document.getElementById('elt_text').value = 'foo';");
295 ProcessPendingMessages();
296 EXPECT_TRUE(render_thread_
->sink().GetUniqueMessageMatching(
297 ViewHostMsg_UpdateState::ID
));
300 TEST_F(RenderViewImplTest
, OnNavigationHttpPost
) {
301 ViewMsg_Navigate_Params nav_params
;
303 // An http url will trigger a resource load so cannot be used here.
304 nav_params
.url
= GURL("data:text/html,<div>Page</div>");
305 nav_params
.navigation_type
= ViewMsg_Navigate_Type::NORMAL
;
306 nav_params
.transition
= PAGE_TRANSITION_TYPED
;
307 nav_params
.page_id
= -1;
308 nav_params
.is_post
= true;
311 const unsigned char* raw_data
= reinterpret_cast<const unsigned char*>(
313 const unsigned int length
= 11;
314 const std::vector
<unsigned char> post_data(raw_data
, raw_data
+ length
);
315 nav_params
.browser_initiated_post_data
= post_data
;
317 view()->OnNavigate(nav_params
);
318 ProcessPendingMessages();
320 const IPC::Message
* frame_navigate_msg
=
321 render_thread_
->sink().GetUniqueMessageMatching(
322 ViewHostMsg_FrameNavigate::ID
);
323 EXPECT_TRUE(frame_navigate_msg
);
325 ViewHostMsg_FrameNavigate::Param host_nav_params
;
326 ViewHostMsg_FrameNavigate::Read(frame_navigate_msg
, &host_nav_params
);
327 EXPECT_TRUE(host_nav_params
.a
.is_post
);
329 // Check post data sent to browser matches
330 EXPECT_FALSE(host_nav_params
.a
.content_state
.empty());
331 const WebKit::WebHistoryItem item
= webkit_glue::HistoryItemFromString(
332 host_nav_params
.a
.content_state
);
333 WebKit::WebHTTPBody body
= item
.httpBody();
334 WebKit::WebHTTPBody::Element element
;
335 bool successful
= body
.elementAt(0, element
);
336 EXPECT_TRUE(successful
);
337 EXPECT_EQ(WebKit::WebHTTPBody::Element::TypeData
, element
.type
);
338 EXPECT_EQ(length
, element
.data
.size());
339 EXPECT_EQ(0, memcmp(raw_data
, element
.data
.data(), length
));
342 TEST_F(RenderViewImplTest
, DecideNavigationPolicy
) {
343 WebUITestClient client
;
344 WebUITestBrowserClient browser_client
;
345 ContentClient
* old_client
= GetContentClient();
346 ContentBrowserClient
* old_browser_client
= GetContentClient()->browser();
348 SetContentClient(&client
);
349 GetContentClient()->set_browser_for_testing(&browser_client
);
350 client
.set_renderer_for_testing(old_client
->renderer());
352 // Navigations to normal HTTP URLs can be handled locally.
353 WebKit::WebURLRequest
request(GURL("http://foo.com"));
354 WebKit::WebNavigationPolicy policy
= view()->decidePolicyForNavigation(
357 WebKit::WebNavigationTypeLinkClicked
,
359 WebKit::WebNavigationPolicyCurrentTab
,
361 EXPECT_EQ(WebKit::WebNavigationPolicyCurrentTab
, policy
);
363 // Verify that form posts to WebUI URLs will be sent to the browser process.
364 WebKit::WebURLRequest
form_request(GURL("chrome://foo"));
365 form_request
.setHTTPMethod("POST");
366 policy
= view()->decidePolicyForNavigation(
369 WebKit::WebNavigationTypeFormSubmitted
,
371 WebKit::WebNavigationPolicyCurrentTab
,
373 EXPECT_EQ(WebKit::WebNavigationPolicyIgnore
, policy
);
375 // Verify that popup links to WebUI URLs also are sent to browser.
376 WebKit::WebURLRequest
popup_request(GURL("chrome://foo"));
377 policy
= view()->decidePolicyForNavigation(
380 WebKit::WebNavigationTypeLinkClicked
,
382 WebKit::WebNavigationPolicyNewForegroundTab
,
384 EXPECT_EQ(WebKit::WebNavigationPolicyIgnore
, policy
);
386 GetContentClient()->set_browser_for_testing(old_browser_client
);
387 SetContentClient(old_client
);
390 TEST_F(RenderViewImplTest
, DecideNavigationPolicyForWebUI
) {
391 // Enable bindings to simulate a WebUI view.
392 view()->OnAllowBindings(BINDINGS_POLICY_WEB_UI
);
394 // Navigations to normal HTTP URLs will be sent to browser process.
395 WebKit::WebURLRequest
request(GURL("http://foo.com"));
396 WebKit::WebNavigationPolicy policy
= view()->decidePolicyForNavigation(
399 WebKit::WebNavigationTypeLinkClicked
,
401 WebKit::WebNavigationPolicyCurrentTab
,
403 EXPECT_EQ(WebKit::WebNavigationPolicyIgnore
, policy
);
405 // Navigations to WebUI URLs will also be sent to browser process.
406 WebKit::WebURLRequest
webui_request(GURL("chrome://foo"));
407 policy
= view()->decidePolicyForNavigation(
410 WebKit::WebNavigationTypeLinkClicked
,
412 WebKit::WebNavigationPolicyCurrentTab
,
414 EXPECT_EQ(WebKit::WebNavigationPolicyIgnore
, policy
);
416 // Verify that form posts to data URLs will be sent to the browser process.
417 WebKit::WebURLRequest
data_request(GURL("data:text/html,foo"));
418 data_request
.setHTTPMethod("POST");
419 policy
= view()->decidePolicyForNavigation(
422 WebKit::WebNavigationTypeFormSubmitted
,
424 WebKit::WebNavigationPolicyCurrentTab
,
426 EXPECT_EQ(WebKit::WebNavigationPolicyIgnore
, policy
);
428 // Verify that a popup that creates a view first and then navigates to a
429 // normal HTTP URL will be sent to the browser process, even though the
430 // new view does not have any enabled_bindings_.
431 WebKit::WebURLRequest
popup_request(GURL("http://foo.com"));
432 WebKit::WebView
* new_web_view
= view()->createView(
433 GetMainFrame(), popup_request
, WebKit::WebWindowFeatures(), "foo",
434 WebKit::WebNavigationPolicyNewForegroundTab
);
435 RenderViewImpl
* new_view
= RenderViewImpl::FromWebView(new_web_view
);
436 policy
= new_view
->decidePolicyForNavigation(
437 new_web_view
->mainFrame(),
439 WebKit::WebNavigationTypeLinkClicked
,
441 WebKit::WebNavigationPolicyNewForegroundTab
,
443 EXPECT_EQ(WebKit::WebNavigationPolicyIgnore
, policy
);
445 // Clean up after the new view so we don't leak it.
450 // Ensure the RenderViewImpl sends an ACK to a SwapOut request, even if it is
451 // already swapped out. http://crbug.com/93427.
452 TEST_F(RenderViewImplTest
, SendSwapOutACK
) {
453 LoadHTML("<div>Page A</div>");
454 int initial_page_id
= view()->GetPageId();
456 // Respond to a swap out request.
457 ViewMsg_SwapOut_Params params
;
458 params
.closing_process_id
= 10;
459 params
.closing_route_id
= 11;
460 params
.new_render_process_host_id
= 12;
461 params
.new_request_id
= 13;
462 view()->OnSwapOut(params
);
464 // Ensure the swap out commits synchronously.
465 EXPECT_NE(initial_page_id
, view()->GetPageId());
467 // Check for a valid OnSwapOutACK with echoed params.
468 const IPC::Message
* msg
= render_thread_
->sink().GetUniqueMessageMatching(
469 ViewHostMsg_SwapOut_ACK::ID
);
471 ViewHostMsg_SwapOut_ACK::Param reply_params
;
472 ViewHostMsg_SwapOut_ACK::Read(msg
, &reply_params
);
473 EXPECT_EQ(params
.closing_process_id
, reply_params
.a
.closing_process_id
);
474 EXPECT_EQ(params
.closing_route_id
, reply_params
.a
.closing_route_id
);
475 EXPECT_EQ(params
.new_render_process_host_id
,
476 reply_params
.a
.new_render_process_host_id
);
477 EXPECT_EQ(params
.new_request_id
, reply_params
.a
.new_request_id
);
479 // It is possible to get another swap out request. Ensure that we send
480 // an ACK, even if we don't have to do anything else.
481 render_thread_
->sink().ClearMessages();
482 view()->OnSwapOut(params
);
483 const IPC::Message
* msg2
= render_thread_
->sink().GetUniqueMessageMatching(
484 ViewHostMsg_SwapOut_ACK::ID
);
487 // If we navigate back to this RenderView, ensure we don't send a state
488 // update for the swapped out URL. (http://crbug.com/72235)
489 ViewMsg_Navigate_Params nav_params
;
490 nav_params
.url
= GURL("data:text/html,<div>Page B</div>");
491 nav_params
.navigation_type
= ViewMsg_Navigate_Type::NORMAL
;
492 nav_params
.transition
= PAGE_TRANSITION_TYPED
;
493 nav_params
.current_history_list_length
= 1;
494 nav_params
.current_history_list_offset
= 0;
495 nav_params
.pending_history_list_offset
= 1;
496 nav_params
.page_id
= -1;
497 view()->OnNavigate(nav_params
);
498 ProcessPendingMessages();
499 const IPC::Message
* msg3
= render_thread_
->sink().GetUniqueMessageMatching(
500 ViewHostMsg_UpdateState::ID
);
504 // Ensure the RenderViewImpl reloads the previous page if a reload request
505 // arrives while it is showing swappedout://. http://crbug.com/143155.
506 TEST_F(RenderViewImplTest
, ReloadWhileSwappedOut
) {
508 LoadHTML("<div>Page A</div>");
510 // Load page B, which will trigger an UpdateState message for page A.
511 LoadHTML("<div>Page B</div>");
513 // Check for a valid UpdateState message for page A.
514 ProcessPendingMessages();
515 const IPC::Message
* msg_A
= render_thread_
->sink().GetUniqueMessageMatching(
516 ViewHostMsg_UpdateState::ID
);
520 ViewHostMsg_UpdateState::Read(msg_A
, &page_id_A
, &state_A
);
521 EXPECT_EQ(1, page_id_A
);
522 render_thread_
->sink().ClearMessages();
524 // Back to page A (page_id 1) and commit.
525 ViewMsg_Navigate_Params params_A
;
526 params_A
.navigation_type
= ViewMsg_Navigate_Type::NORMAL
;
527 params_A
.transition
= PAGE_TRANSITION_FORWARD_BACK
;
528 params_A
.current_history_list_length
= 2;
529 params_A
.current_history_list_offset
= 1;
530 params_A
.pending_history_list_offset
= 0;
531 params_A
.page_id
= 1;
532 params_A
.state
= state_A
;
533 view()->OnNavigate(params_A
);
534 ProcessPendingMessages();
536 // Respond to a swap out request.
537 ViewMsg_SwapOut_Params params
;
538 params
.closing_process_id
= 10;
539 params
.closing_route_id
= 11;
540 params
.new_render_process_host_id
= 12;
541 params
.new_request_id
= 13;
542 view()->OnSwapOut(params
);
544 // Check for a OnSwapOutACK.
545 const IPC::Message
* msg
= render_thread_
->sink().GetUniqueMessageMatching(
546 ViewHostMsg_SwapOut_ACK::ID
);
548 render_thread_
->sink().ClearMessages();
550 // It is possible to get a reload request at this point, containing the
551 // params.state of the initial page (e.g., if the new page fails the
552 // provisional load in the renderer process, after we unload the old page).
553 // Ensure the old page gets reloaded, not swappedout://.
554 ViewMsg_Navigate_Params nav_params
;
555 nav_params
.url
= GURL("data:text/html,<div>Page A</div>");
556 nav_params
.navigation_type
= ViewMsg_Navigate_Type::RELOAD
;
557 nav_params
.transition
= PAGE_TRANSITION_RELOAD
;
558 nav_params
.current_history_list_length
= 2;
559 nav_params
.current_history_list_offset
= 0;
560 nav_params
.pending_history_list_offset
= 0;
561 nav_params
.page_id
= 1;
562 nav_params
.state
= state_A
;
563 view()->OnNavigate(nav_params
);
564 ProcessPendingMessages();
566 // Verify page A committed, not swappedout://.
567 const IPC::Message
* frame_navigate_msg
=
568 render_thread_
->sink().GetUniqueMessageMatching(
569 ViewHostMsg_FrameNavigate::ID
);
570 EXPECT_TRUE(frame_navigate_msg
);
572 // Read URL out of the parent trait of the params object.
573 ViewHostMsg_FrameNavigate::Param commit_params
;
574 ViewHostMsg_FrameNavigate::Read(frame_navigate_msg
, &commit_params
);
575 EXPECT_NE(GURL("swappedout://"), commit_params
.a
.url
);
579 // Test that we get the correct UpdateState message when we go back twice
580 // quickly without committing. Regression test for http://crbug.com/58082.
581 TEST_F(RenderViewImplTest
, LastCommittedUpdateState
) {
583 LoadHTML("<div>Page A</div>");
585 // Load page B, which will trigger an UpdateState message for page A.
586 LoadHTML("<div>Page B</div>");
588 // Check for a valid UpdateState message for page A.
589 ProcessPendingMessages();
590 const IPC::Message
* msg_A
= render_thread_
->sink().GetUniqueMessageMatching(
591 ViewHostMsg_UpdateState::ID
);
595 ViewHostMsg_UpdateState::Read(msg_A
, &page_id_A
, &state_A
);
596 EXPECT_EQ(1, page_id_A
);
597 render_thread_
->sink().ClearMessages();
599 // Load page C, which will trigger an UpdateState message for page B.
600 LoadHTML("<div>Page C</div>");
602 // Check for a valid UpdateState for page B.
603 ProcessPendingMessages();
604 const IPC::Message
* msg_B
= render_thread_
->sink().GetUniqueMessageMatching(
605 ViewHostMsg_UpdateState::ID
);
609 ViewHostMsg_UpdateState::Read(msg_B
, &page_id_B
, &state_B
);
610 EXPECT_EQ(2, page_id_B
);
611 EXPECT_NE(state_A
, state_B
);
612 render_thread_
->sink().ClearMessages();
614 // Load page D, which will trigger an UpdateState message for page C.
615 LoadHTML("<div>Page D</div>");
617 // Check for a valid UpdateState for page C.
618 ProcessPendingMessages();
619 const IPC::Message
* msg_C
= render_thread_
->sink().GetUniqueMessageMatching(
620 ViewHostMsg_UpdateState::ID
);
624 ViewHostMsg_UpdateState::Read(msg_C
, &page_id_C
, &state_C
);
625 EXPECT_EQ(3, page_id_C
);
626 EXPECT_NE(state_B
, state_C
);
627 render_thread_
->sink().ClearMessages();
629 // Go back to C and commit, preparing for our real test.
630 ViewMsg_Navigate_Params params_C
;
631 params_C
.navigation_type
= ViewMsg_Navigate_Type::NORMAL
;
632 params_C
.transition
= PAGE_TRANSITION_FORWARD_BACK
;
633 params_C
.current_history_list_length
= 4;
634 params_C
.current_history_list_offset
= 3;
635 params_C
.pending_history_list_offset
= 2;
636 params_C
.page_id
= 3;
637 params_C
.state
= state_C
;
638 view()->OnNavigate(params_C
);
639 ProcessPendingMessages();
640 render_thread_
->sink().ClearMessages();
642 // Go back twice quickly, such that page B does not have a chance to commit.
643 // This leads to two changes to the back/forward list but only one change to
644 // the RenderView's page ID.
646 // Back to page B (page_id 2), without committing.
647 ViewMsg_Navigate_Params params_B
;
648 params_B
.navigation_type
= ViewMsg_Navigate_Type::NORMAL
;
649 params_B
.transition
= PAGE_TRANSITION_FORWARD_BACK
;
650 params_B
.current_history_list_length
= 4;
651 params_B
.current_history_list_offset
= 2;
652 params_B
.pending_history_list_offset
= 1;
653 params_B
.page_id
= 2;
654 params_B
.state
= state_B
;
655 view()->OnNavigate(params_B
);
657 // Back to page A (page_id 1) and commit.
658 ViewMsg_Navigate_Params params
;
659 params
.navigation_type
= ViewMsg_Navigate_Type::NORMAL
;
660 params
.transition
= PAGE_TRANSITION_FORWARD_BACK
;
661 params_B
.current_history_list_length
= 4;
662 params_B
.current_history_list_offset
= 2;
663 params_B
.pending_history_list_offset
= 0;
665 params
.state
= state_A
;
666 view()->OnNavigate(params
);
667 ProcessPendingMessages();
669 // Now ensure that the UpdateState message we receive is consistent
670 // and represents page C in both page_id and state.
671 const IPC::Message
* msg
= render_thread_
->sink().GetUniqueMessageMatching(
672 ViewHostMsg_UpdateState::ID
);
676 ViewHostMsg_UpdateState::Read(msg
, &page_id
, &state
);
677 EXPECT_EQ(page_id_C
, page_id
);
678 EXPECT_NE(state_A
, state
);
679 EXPECT_NE(state_B
, state
);
680 EXPECT_EQ(state_C
, state
);
683 // Test that the history_page_ids_ list can reveal when a stale back/forward
684 // navigation arrives from the browser and can be ignored. See
685 // http://crbug.com/86758.
686 TEST_F(RenderViewImplTest
, StaleNavigationsIgnored
) {
688 LoadHTML("<div>Page A</div>");
689 EXPECT_EQ(1, view()->history_list_length_
);
690 EXPECT_EQ(0, view()->history_list_offset_
);
691 EXPECT_EQ(1, view()->history_page_ids_
[0]);
693 // Load page B, which will trigger an UpdateState message for page A.
694 LoadHTML("<div>Page B</div>");
695 EXPECT_EQ(2, view()->history_list_length_
);
696 EXPECT_EQ(1, view()->history_list_offset_
);
697 EXPECT_EQ(2, view()->history_page_ids_
[1]);
699 // Check for a valid UpdateState message for page A.
700 ProcessPendingMessages();
701 const IPC::Message
* msg_A
= render_thread_
->sink().GetUniqueMessageMatching(
702 ViewHostMsg_UpdateState::ID
);
706 ViewHostMsg_UpdateState::Read(msg_A
, &page_id_A
, &state_A
);
707 EXPECT_EQ(1, page_id_A
);
708 render_thread_
->sink().ClearMessages();
710 // Back to page A (page_id 1) and commit.
711 ViewMsg_Navigate_Params params_A
;
712 params_A
.navigation_type
= ViewMsg_Navigate_Type::NORMAL
;
713 params_A
.transition
= PAGE_TRANSITION_FORWARD_BACK
;
714 params_A
.current_history_list_length
= 2;
715 params_A
.current_history_list_offset
= 1;
716 params_A
.pending_history_list_offset
= 0;
717 params_A
.page_id
= 1;
718 params_A
.state
= state_A
;
719 view()->OnNavigate(params_A
);
720 ProcessPendingMessages();
722 // A new navigation commits, clearing the forward history.
723 LoadHTML("<div>Page C</div>");
724 EXPECT_EQ(2, view()->history_list_length_
);
725 EXPECT_EQ(1, view()->history_list_offset_
);
726 EXPECT_EQ(3, view()->history_page_ids_
[1]);
728 // The browser then sends a stale navigation to B, which should be ignored.
729 ViewMsg_Navigate_Params params_B
;
730 params_B
.navigation_type
= ViewMsg_Navigate_Type::NORMAL
;
731 params_B
.transition
= PAGE_TRANSITION_FORWARD_BACK
;
732 params_B
.current_history_list_length
= 2;
733 params_B
.current_history_list_offset
= 0;
734 params_B
.pending_history_list_offset
= 1;
735 params_B
.page_id
= 2;
736 params_B
.state
= state_A
; // Doesn't matter, just has to be present.
737 view()->OnNavigate(params_B
);
739 // State should be unchanged.
740 EXPECT_EQ(2, view()->history_list_length_
);
741 EXPECT_EQ(1, view()->history_list_offset_
);
742 EXPECT_EQ(3, view()->history_page_ids_
[1]);
745 // Test that we do not ignore navigations after the entry limit is reached,
746 // in which case the browser starts dropping entries from the front. In this
747 // case, we'll see a page_id mismatch but the RenderView's id will be older,
748 // not newer, than params.page_id. Use this as a cue that we should update the
749 // state and not treat it like a navigation to a cropped forward history item.
750 // See http://crbug.com/89798.
751 TEST_F(RenderViewImplTest
, DontIgnoreBackAfterNavEntryLimit
) {
753 LoadHTML("<div>Page A</div>");
754 EXPECT_EQ(1, view()->history_list_length_
);
755 EXPECT_EQ(0, view()->history_list_offset_
);
756 EXPECT_EQ(1, view()->history_page_ids_
[0]);
758 // Load page B, which will trigger an UpdateState message for page A.
759 LoadHTML("<div>Page B</div>");
760 EXPECT_EQ(2, view()->history_list_length_
);
761 EXPECT_EQ(1, view()->history_list_offset_
);
762 EXPECT_EQ(2, view()->history_page_ids_
[1]);
764 // Check for a valid UpdateState message for page A.
765 ProcessPendingMessages();
766 const IPC::Message
* msg_A
= render_thread_
->sink().GetUniqueMessageMatching(
767 ViewHostMsg_UpdateState::ID
);
771 ViewHostMsg_UpdateState::Read(msg_A
, &page_id_A
, &state_A
);
772 EXPECT_EQ(1, page_id_A
);
773 render_thread_
->sink().ClearMessages();
775 // Load page C, which will trigger an UpdateState message for page B.
776 LoadHTML("<div>Page C</div>");
777 EXPECT_EQ(3, view()->history_list_length_
);
778 EXPECT_EQ(2, view()->history_list_offset_
);
779 EXPECT_EQ(3, view()->history_page_ids_
[2]);
781 // Check for a valid UpdateState message for page B.
782 ProcessPendingMessages();
783 const IPC::Message
* msg_B
= render_thread_
->sink().GetUniqueMessageMatching(
784 ViewHostMsg_UpdateState::ID
);
788 ViewHostMsg_UpdateState::Read(msg_B
, &page_id_B
, &state_B
);
789 EXPECT_EQ(2, page_id_B
);
790 render_thread_
->sink().ClearMessages();
792 // Suppose the browser has limited the number of NavigationEntries to 2.
793 // It has now dropped the first entry, but the renderer isn't notified.
794 // Ensure that going back to page B (page_id 2) at offset 0 is successful.
795 ViewMsg_Navigate_Params params_B
;
796 params_B
.navigation_type
= ViewMsg_Navigate_Type::NORMAL
;
797 params_B
.transition
= PAGE_TRANSITION_FORWARD_BACK
;
798 params_B
.current_history_list_length
= 2;
799 params_B
.current_history_list_offset
= 1;
800 params_B
.pending_history_list_offset
= 0;
801 params_B
.page_id
= 2;
802 params_B
.state
= state_B
;
803 view()->OnNavigate(params_B
);
804 ProcessPendingMessages();
806 EXPECT_EQ(2, view()->history_list_length_
);
807 EXPECT_EQ(0, view()->history_list_offset_
);
808 EXPECT_EQ(2, view()->history_page_ids_
[0]);
811 // Test that our IME backend sends a notification message when the input focus
813 TEST_F(RenderViewImplTest
, OnImeStateChanged
) {
814 // Enable our IME backend code.
815 view()->OnSetInputMethodActive(true);
817 // Load an HTML page consisting of two input fields.
818 view()->set_send_content_state_immediately(true);
823 "<input id=\"test1\" type=\"text\" value=\"some text\"></input>"
824 "<input id=\"test2\" type=\"password\"></input>"
827 render_thread_
->sink().ClearMessages();
829 const int kRepeatCount
= 10;
830 for (int i
= 0; i
< kRepeatCount
; i
++) {
831 // Move the input focus to the first <input> element, where we should
833 ExecuteJavaScript("document.getElementById('test1').focus();");
834 ProcessPendingMessages();
835 render_thread_
->sink().ClearMessages();
837 // Update the IME status and verify if our IME backend sends an IPC message
839 view()->UpdateTextInputState(RenderWidget::DO_NOT_SHOW_IME
);
840 const IPC::Message
* msg
= render_thread_
->sink().GetMessageAt(0);
841 EXPECT_TRUE(msg
!= NULL
);
842 EXPECT_EQ(ViewHostMsg_TextInputStateChanged::ID
, msg
->type());
843 ViewHostMsg_TextInputStateChanged::Param params
;
844 ViewHostMsg_TextInputStateChanged::Read(msg
, ¶ms
);
845 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT
, params
.a
.type
);
846 EXPECT_EQ(true, params
.a
.can_compose_inline
);
847 EXPECT_EQ("some text", params
.a
.value
);
848 EXPECT_EQ(0, params
.a
.selection_start
);
849 EXPECT_EQ(9, params
.a
.selection_end
);
850 EXPECT_EQ(-1, params
.a
.composition_start
);
851 EXPECT_EQ(-1, params
.a
.composition_end
);
853 // Move the input focus to the second <input> element, where we should
855 ExecuteJavaScript("document.getElementById('test2').focus();");
856 ProcessPendingMessages();
857 render_thread_
->sink().ClearMessages();
859 // Update the IME status and verify if our IME backend sends an IPC message
860 // to de-activate IMEs.
861 view()->UpdateTextInputState(RenderWidget::DO_NOT_SHOW_IME
);
862 msg
= render_thread_
->sink().GetMessageAt(0);
863 EXPECT_TRUE(msg
!= NULL
);
864 EXPECT_EQ(ViewHostMsg_TextInputStateChanged::ID
, msg
->type());
865 ViewHostMsg_TextInputStateChanged::Read(msg
, ¶ms
);
866 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD
, params
.a
.type
);
870 // Test that our IME backend can compose CJK words.
871 // Our IME front-end sends many platform-independent messages to the IME backend
872 // while it composes CJK words. This test sends the minimal messages captured
873 // on my local environment directly to the IME backend to verify if the backend
874 // can compose CJK words without any problems.
875 // This test uses an array of command sets because an IME composotion does not
876 // only depends on IME events, but also depends on window events, e.g. moving
877 // the window focus while composing a CJK text. To handle such complicated
878 // cases, this test should not only call IME-related functions in the
879 // RenderWidget class, but also call some RenderWidget members, e.g.
880 // ExecuteJavaScript(), RenderWidget::OnSetFocus(), etc.
881 TEST_F(RenderViewImplTest
, ImeComposition
) {
887 IME_CONFIRMCOMPOSITION
,
888 IME_CANCELCOMPOSITION
895 const wchar_t* ime_string
;
896 const wchar_t* result
;
898 static const ImeMessage kImeMessages
[] = {
899 // Scenario 1: input a Chinese word with Microsoft IME (on Vista).
900 {IME_INITIALIZE
, true, 0, 0, NULL
, NULL
},
901 {IME_SETINPUTMODE
, true, 0, 0, NULL
, NULL
},
902 {IME_SETFOCUS
, true, 0, 0, NULL
, NULL
},
903 {IME_SETCOMPOSITION
, false, 1, 1, L
"n", L
"n"},
904 {IME_SETCOMPOSITION
, false, 2, 2, L
"ni", L
"ni"},
905 {IME_SETCOMPOSITION
, false, 3, 3, L
"nih", L
"nih"},
906 {IME_SETCOMPOSITION
, false, 4, 4, L
"niha", L
"niha"},
907 {IME_SETCOMPOSITION
, false, 5, 5, L
"nihao", L
"nihao"},
908 {IME_CONFIRMCOMPOSITION
, false, -1, -1, L
"\x4F60\x597D", L
"\x4F60\x597D"},
909 // Scenario 2: input a Japanese word with Microsoft IME (on Vista).
910 {IME_INITIALIZE
, true, 0, 0, NULL
, NULL
},
911 {IME_SETINPUTMODE
, true, 0, 0, NULL
, NULL
},
912 {IME_SETFOCUS
, true, 0, 0, NULL
, NULL
},
913 {IME_SETCOMPOSITION
, false, 0, 1, L
"\xFF4B", L
"\xFF4B"},
914 {IME_SETCOMPOSITION
, false, 0, 1, L
"\x304B", L
"\x304B"},
915 {IME_SETCOMPOSITION
, false, 0, 2, L
"\x304B\xFF4E", L
"\x304B\xFF4E"},
916 {IME_SETCOMPOSITION
, false, 0, 3, L
"\x304B\x3093\xFF4A",
917 L
"\x304B\x3093\xFF4A"},
918 {IME_SETCOMPOSITION
, false, 0, 3, L
"\x304B\x3093\x3058",
919 L
"\x304B\x3093\x3058"},
920 {IME_SETCOMPOSITION
, false, 0, 2, L
"\x611F\x3058", L
"\x611F\x3058"},
921 {IME_SETCOMPOSITION
, false, 0, 2, L
"\x6F22\x5B57", L
"\x6F22\x5B57"},
922 {IME_CONFIRMCOMPOSITION
, false, -1, -1, L
"", L
"\x6F22\x5B57"},
923 {IME_CANCELCOMPOSITION
, false, -1, -1, L
"", L
"\x6F22\x5B57"},
924 // Scenario 3: input a Korean word with Microsot IME (on Vista).
925 {IME_INITIALIZE
, true, 0, 0, NULL
, NULL
},
926 {IME_SETINPUTMODE
, true, 0, 0, NULL
, NULL
},
927 {IME_SETFOCUS
, true, 0, 0, NULL
, NULL
},
928 {IME_SETCOMPOSITION
, false, 0, 1, L
"\x3147", L
"\x3147"},
929 {IME_SETCOMPOSITION
, false, 0, 1, L
"\xC544", L
"\xC544"},
930 {IME_SETCOMPOSITION
, false, 0, 1, L
"\xC548", L
"\xC548"},
931 {IME_CONFIRMCOMPOSITION
, false, -1, -1, L
"", L
"\xC548"},
932 {IME_SETCOMPOSITION
, false, 0, 1, L
"\x3134", L
"\xC548\x3134"},
933 {IME_SETCOMPOSITION
, false, 0, 1, L
"\xB140", L
"\xC548\xB140"},
934 {IME_SETCOMPOSITION
, false, 0, 1, L
"\xB155", L
"\xC548\xB155"},
935 {IME_CANCELCOMPOSITION
, false, -1, -1, L
"", L
"\xC548"},
936 {IME_SETCOMPOSITION
, false, 0, 1, L
"\xB155", L
"\xC548\xB155"},
937 {IME_CONFIRMCOMPOSITION
, false, -1, -1, L
"", L
"\xC548\xB155"},
940 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(kImeMessages
); i
++) {
941 const ImeMessage
* ime_message
= &kImeMessages
[i
];
942 switch (ime_message
->command
) {
944 // Load an HTML page consisting of a content-editable <div> element,
945 // and move the input focus to the <div> element, where we can use
947 view()->OnSetInputMethodActive(ime_message
->enable
);
948 view()->set_send_content_state_immediately(true);
953 "<div id=\"test1\" contenteditable=\"true\"></div>"
956 ExecuteJavaScript("document.getElementById('test1').focus();");
959 case IME_SETINPUTMODE
:
960 // Activate (or deactivate) our IME back-end.
961 view()->OnSetInputMethodActive(ime_message
->enable
);
965 // Update the window focus.
966 view()->OnSetFocus(ime_message
->enable
);
969 case IME_SETCOMPOSITION
:
970 view()->OnImeSetComposition(
971 WideToUTF16Hack(ime_message
->ime_string
),
972 std::vector
<WebKit::WebCompositionUnderline
>(),
973 ime_message
->selection_start
,
974 ime_message
->selection_end
);
977 case IME_CONFIRMCOMPOSITION
:
978 view()->OnImeConfirmComposition(
979 WideToUTF16Hack(ime_message
->ime_string
),
980 ui::Range::InvalidRange());
983 case IME_CANCELCOMPOSITION
:
984 view()->OnImeSetComposition(
986 std::vector
<WebKit::WebCompositionUnderline
>(),
991 // Update the status of our IME back-end.
992 // TODO(hbono): we should verify messages to be sent from the back-end.
993 view()->UpdateTextInputState(RenderWidget::DO_NOT_SHOW_IME
);
994 ProcessPendingMessages();
995 render_thread_
->sink().ClearMessages();
997 if (ime_message
->result
) {
998 // Retrieve the content of this page and compare it with the expected
1000 const int kMaxOutputCharacters
= 128;
1001 std::wstring output
= UTF16ToWideHack(
1002 GetMainFrame()->contentAsText(kMaxOutputCharacters
));
1003 EXPECT_EQ(output
, ime_message
->result
);
1008 // Test that the RenderView::OnSetTextDirection() function can change the text
1009 // direction of the selected input element.
1010 TEST_F(RenderViewImplTest
, OnSetTextDirection
) {
1011 // Load an HTML page consisting of a <textarea> element and a <div> element.
1012 // This test changes the text direction of the <textarea> element, and
1013 // writes the values of its 'dir' attribute and its 'direction' property to
1014 // verify that the text direction is changed.
1015 view()->set_send_content_state_immediately(true);
1020 "<textarea id=\"test\"></textarea>"
1021 "<div id=\"result\" contenteditable=\"true\"></div>"
1024 render_thread_
->sink().ClearMessages();
1026 static const struct {
1027 WebTextDirection direction
;
1028 const wchar_t* expected_result
;
1029 } kTextDirection
[] = {
1030 { WebKit::WebTextDirectionRightToLeft
, L
"\x000A" L
"rtl,rtl" },
1031 { WebKit::WebTextDirectionLeftToRight
, L
"\x000A" L
"ltr,ltr" },
1033 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(kTextDirection
); ++i
) {
1034 // Set the text direction of the <textarea> element.
1035 ExecuteJavaScript("document.getElementById('test').focus();");
1036 view()->OnSetTextDirection(kTextDirection
[i
].direction
);
1038 // Write the values of its DOM 'dir' attribute and its CSS 'direction'
1039 // property to the <div> element.
1040 ExecuteJavaScript("var result = document.getElementById('result');"
1041 "var node = document.getElementById('test');"
1042 "var style = getComputedStyle(node, null);"
1043 "result.innerText ="
1044 " node.getAttribute('dir') + ',' +"
1045 " style.getPropertyValue('direction');");
1047 // Copy the document content to std::wstring and compare with the
1049 const int kMaxOutputCharacters
= 16;
1050 std::wstring output
= UTF16ToWideHack(
1051 GetMainFrame()->contentAsText(kMaxOutputCharacters
));
1052 EXPECT_EQ(output
, kTextDirection
[i
].expected_result
);
1056 // Test that we can receive correct DOM events when we send input events
1057 // through the RenderWidget::OnHandleInputEvent() function.
1058 TEST_F(RenderViewImplTest
, OnHandleKeyboardEvent
) {
1059 #if !defined(OS_MACOSX)
1060 // Load an HTML page consisting of one <input> element and three
1061 // contentediable <div> elements.
1062 // The <input> element is used for sending keyboard events, and the <div>
1063 // elements are used for writing DOM events in the following format:
1064 // "<keyCode>,<shiftKey>,<controlKey>,<altKey>".
1065 // TODO(hbono): <http://crbug.com/2215> Our WebKit port set |ev.metaKey| to
1066 // true when pressing an alt key, i.e. the |ev.metaKey| value is not
1067 // trustworthy. We will check the |ev.metaKey| value when this issue is fixed.
1068 view()->set_send_content_state_immediately(true);
1072 "<script type='text/javascript' language='javascript'>"
1073 "function OnKeyEvent(ev) {"
1074 " var result = document.getElementById(ev.type);"
1075 " result.innerText ="
1076 " (ev.which || ev.keyCode) + ',' +"
1077 " ev.shiftKey + ',' +"
1078 " ev.ctrlKey + ',' +"
1085 "<input id='test' type='text'"
1086 " onkeydown='return OnKeyEvent(event);'"
1087 " onkeypress='return OnKeyEvent(event);'"
1088 " onkeyup='return OnKeyEvent(event);'>"
1090 "<div id='keydown' contenteditable='true'>"
1092 "<div id='keypress' contenteditable='true'>"
1094 "<div id='keyup' contenteditable='true'>"
1098 ExecuteJavaScript("document.getElementById('test').focus();");
1099 render_thread_
->sink().ClearMessages();
1101 static const MockKeyboard::Layout kLayouts
[] = {
1103 // Since we ignore the mock keyboard layout on Linux and instead just use
1104 // the screen's keyboard layout, these trivially pass. They are commented
1105 // out to avoid the illusion that they work.
1106 MockKeyboard::LAYOUT_ARABIC
,
1107 MockKeyboard::LAYOUT_CANADIAN_FRENCH
,
1108 MockKeyboard::LAYOUT_FRENCH
,
1109 MockKeyboard::LAYOUT_HEBREW
,
1110 MockKeyboard::LAYOUT_RUSSIAN
,
1112 MockKeyboard::LAYOUT_UNITED_STATES
,
1115 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(kLayouts
); ++i
) {
1116 // For each key code, we send three keyboard events.
1117 // * we press only the key;
1118 // * we press the key and a left-shift key, and;
1119 // * we press the key and a right-alt (AltGr) key.
1120 // For each modifiers, we need a string used for formatting its expected
1121 // result. (See the above comment for its format.)
1122 static const struct {
1123 MockKeyboard::Modifiers modifiers
;
1124 const char* expected_result
;
1125 } kModifierData
[] = {
1126 {MockKeyboard::NONE
, "false,false,false"},
1127 {MockKeyboard::LEFT_SHIFT
, "true,false,false"},
1129 {MockKeyboard::RIGHT_ALT
, "false,false,true"},
1133 MockKeyboard::Layout layout
= kLayouts
[i
];
1134 for (size_t j
= 0; j
< ARRAYSIZE_UNSAFE(kModifierData
); ++j
) {
1135 // Virtual key codes used for this test.
1136 static const int kKeyCodes
[] = {
1137 '0', '1', '2', '3', '4', '5', '6', '7',
1138 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
1139 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
1140 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
1146 ui::VKEY_OEM_PERIOD
,
1154 // Not sure how to handle this key on Linux.
1159 MockKeyboard::Modifiers modifiers
= kModifierData
[j
].modifiers
;
1160 for (size_t k
= 0; k
< ARRAYSIZE_UNSAFE(kKeyCodes
); ++k
) {
1161 // Send a keyboard event to the RenderView object.
1162 // We should test a keyboard event only when the given keyboard-layout
1163 // driver is installed in a PC and the driver can assign a Unicode
1164 // charcter for the given tuple (key-code and modifiers).
1165 int key_code
= kKeyCodes
[k
];
1167 if (SendKeyEvent(layout
, key_code
, modifiers
, &char_code
) < 0)
1170 // Create an expected result from the virtual-key code, the character
1171 // code, and the modifier-key status.
1172 // We format a string that emulates a DOM-event string produced hy
1173 // our JavaScript function. (See the above comment for the format.)
1174 static char expected_result
[1024];
1175 expected_result
[0] = 0;
1176 base::snprintf(&expected_result
[0],
1177 sizeof(expected_result
),
1178 "\n" // texts in the <input> element
1179 "%d,%s\n" // texts in the first <div> element
1180 "%d,%s\n" // texts in the second <div> element
1181 "%d,%s", // texts in the third <div> element
1182 key_code
, kModifierData
[j
].expected_result
,
1183 static_cast<int>(char_code
[0]),
1184 kModifierData
[j
].expected_result
,
1185 key_code
, kModifierData
[j
].expected_result
);
1187 // Retrieve the text in the test page and compare it with the expected
1188 // text created from a virtual-key code, a character code, and the
1189 // modifier-key status.
1190 const int kMaxOutputCharacters
= 1024;
1191 std::string output
= UTF16ToUTF8(
1192 GetMainFrame()->contentAsText(kMaxOutputCharacters
));
1193 EXPECT_EQ(expected_result
, output
);
1202 // Test that our EditorClientImpl class can insert characters when we send
1203 // keyboard events through the RenderWidget::OnHandleInputEvent() function.
1204 // This test is for preventing regressions caused only when we use non-US
1205 // keyboards, such as Issue 10846.
1206 TEST_F(RenderViewImplTest
, InsertCharacters
) {
1207 #if !defined(OS_MACOSX)
1208 static const struct {
1209 MockKeyboard::Layout layout
;
1210 const wchar_t* expected_result
;
1213 // Disabled these keyboard layouts because buildbots do not have their
1214 // keyboard-layout drivers installed.
1215 {MockKeyboard::LAYOUT_ARABIC
,
1216 L
"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1217 L
"\x0038\x0039\x0634\x0624\x064a\x062b\x0628\x0644"
1218 L
"\x0627\x0647\x062a\x0646\x0645\x0629\x0649\x062e"
1219 L
"\x062d\x0636\x0642\x0633\x0641\x0639\x0631\x0635"
1220 L
"\x0621\x063a\x0626\x0643\x003d\x0648\x002d\x0632"
1221 L
"\x0638\x0630\x062c\x005c\x062f\x0637\x0028\x0021"
1222 L
"\x0040\x0023\x0024\x0025\x005e\x0026\x002a\x0029"
1223 L
"\x0650\x007d\x005d\x064f\x005b\x0623\x00f7\x0640"
1224 L
"\x060c\x002f\x2019\x0622\x00d7\x061b\x064e\x064c"
1225 L
"\x064d\x2018\x007b\x064b\x0652\x0625\x007e\x003a"
1226 L
"\x002b\x002c\x005f\x002e\x061f\x0651\x003c\x007c"
1227 L
"\x003e\x0022\x0030\x0031\x0032\x0033\x0034\x0035"
1228 L
"\x0036\x0037\x0038\x0039\x0634\x0624\x064a\x062b"
1229 L
"\x0628\x0644\x0627\x0647\x062a\x0646\x0645\x0629"
1230 L
"\x0649\x062e\x062d\x0636\x0642\x0633\x0641\x0639"
1231 L
"\x0631\x0635\x0621\x063a\x0626\x0643\x003d\x0648"
1232 L
"\x002d\x0632\x0638\x0630\x062c\x005c\x062f\x0637"
1234 {MockKeyboard::LAYOUT_HEBREW
,
1235 L
"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1236 L
"\x0038\x0039\x05e9\x05e0\x05d1\x05d2\x05e7\x05db"
1237 L
"\x05e2\x05d9\x05df\x05d7\x05dc\x05da\x05e6\x05de"
1238 L
"\x05dd\x05e4\x002f\x05e8\x05d3\x05d0\x05d5\x05d4"
1239 L
"\x0027\x05e1\x05d8\x05d6\x05e3\x003d\x05ea\x002d"
1240 L
"\x05e5\x002e\x003b\x005d\x005c\x005b\x002c\x0028"
1241 L
"\x0021\x0040\x0023\x0024\x0025\x005e\x0026\x002a"
1242 L
"\x0029\x0041\x0042\x0043\x0044\x0045\x0046\x0047"
1243 L
"\x0048\x0049\x004a\x004b\x004c\x004d\x004e\x004f"
1244 L
"\x0050\x0051\x0052\x0053\x0054\x0055\x0056\x0057"
1245 L
"\x0058\x0059\x005a\x003a\x002b\x003e\x005f\x003c"
1246 L
"\x003f\x007e\x007d\x007c\x007b\x0022\x0030\x0031"
1247 L
"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039"
1248 L
"\x05e9\x05e0\x05d1\x05d2\x05e7\x05db\x05e2\x05d9"
1249 L
"\x05df\x05d7\x05dc\x05da\x05e6\x05de\x05dd\x05e4"
1250 L
"\x002f\x05e8\x05d3\x05d0\x05d5\x05d4\x0027\x05e1"
1251 L
"\x05d8\x05d6\x05e3\x003d\x05ea\x002d\x05e5\x002e"
1252 L
"\x003b\x005d\x005c\x005b\x002c"
1256 // On Linux, the only way to test alternate keyboard layouts is to change
1257 // the keyboard layout of the whole screen. I'm worried about the side
1258 // effects this may have on the buildbots.
1259 {MockKeyboard::LAYOUT_CANADIAN_FRENCH
,
1260 L
"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1261 L
"\x0038\x0039\x0061\x0062\x0063\x0064\x0065\x0066"
1262 L
"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e"
1263 L
"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076"
1264 L
"\x0077\x0078\x0079\x007a\x003b\x003d\x002c\x002d"
1265 L
"\x002e\x00e9\x003c\x0029\x0021\x0022\x002f\x0024"
1266 L
"\x0025\x003f\x0026\x002a\x0028\x0041\x0042\x0043"
1267 L
"\x0044\x0045\x0046\x0047\x0048\x0049\x004a\x004b"
1268 L
"\x004c\x004d\x004e\x004f\x0050\x0051\x0052\x0053"
1269 L
"\x0054\x0055\x0056\x0057\x0058\x0059\x005a\x003a"
1270 L
"\x002b\x0027\x005f\x002e\x00c9\x003e\x0030\x0031"
1271 L
"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039"
1272 L
"\x0061\x0062\x0063\x0064\x0065\x0066\x0067\x0068"
1273 L
"\x0069\x006a\x006b\x006c\x006d\x006e\x006f\x0070"
1274 L
"\x0071\x0072\x0073\x0074\x0075\x0076\x0077\x0078"
1275 L
"\x0079\x007a\x003b\x003d\x002c\x002d\x002e\x00e9"
1278 {MockKeyboard::LAYOUT_FRENCH
,
1279 L
"\x00e0\x0026\x00e9\x0022\x0027\x0028\x002d\x00e8"
1280 L
"\x005f\x00e7\x0061\x0062\x0063\x0064\x0065\x0066"
1281 L
"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e"
1282 L
"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076"
1283 L
"\x0077\x0078\x0079\x007a\x0024\x003d\x002c\x003b"
1284 L
"\x003a\x00f9\x0029\x002a\x0021\x0030\x0031\x0032"
1285 L
"\x0033\x0034\x0035\x0036\x0037\x0038\x0039\x0041"
1286 L
"\x0042\x0043\x0044\x0045\x0046\x0047\x0048\x0049"
1287 L
"\x004a\x004b\x004c\x004d\x004e\x004f\x0050\x0051"
1288 L
"\x0052\x0053\x0054\x0055\x0056\x0057\x0058\x0059"
1289 L
"\x005a\x00a3\x002b\x003f\x002e\x002f\x0025\x00b0"
1290 L
"\x00b5\x00e0\x0026\x00e9\x0022\x0027\x0028\x002d"
1291 L
"\x00e8\x005f\x00e7\x0061\x0062\x0063\x0064\x0065"
1292 L
"\x0066\x0067\x0068\x0069\x006a\x006b\x006c\x006d"
1293 L
"\x006e\x006f\x0070\x0071\x0072\x0073\x0074\x0075"
1294 L
"\x0076\x0077\x0078\x0079\x007a\x0024\x003d\x002c"
1295 L
"\x003b\x003a\x00f9\x0029\x002a\x0021"
1297 {MockKeyboard::LAYOUT_RUSSIAN
,
1298 L
"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1299 L
"\x0038\x0039\x0444\x0438\x0441\x0432\x0443\x0430"
1300 L
"\x043f\x0440\x0448\x043e\x043b\x0434\x044c\x0442"
1301 L
"\x0449\x0437\x0439\x043a\x044b\x0435\x0433\x043c"
1302 L
"\x0446\x0447\x043d\x044f\x0436\x003d\x0431\x002d"
1303 L
"\x044e\x002e\x0451\x0445\x005c\x044a\x044d\x0029"
1304 L
"\x0021\x0022\x2116\x003b\x0025\x003a\x003f\x002a"
1305 L
"\x0028\x0424\x0418\x0421\x0412\x0423\x0410\x041f"
1306 L
"\x0420\x0428\x041e\x041b\x0414\x042c\x0422\x0429"
1307 L
"\x0417\x0419\x041a\x042b\x0415\x0413\x041c\x0426"
1308 L
"\x0427\x041d\x042f\x0416\x002b\x0411\x005f\x042e"
1309 L
"\x002c\x0401\x0425\x002f\x042a\x042d\x0030\x0031"
1310 L
"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039"
1311 L
"\x0444\x0438\x0441\x0432\x0443\x0430\x043f\x0440"
1312 L
"\x0448\x043e\x043b\x0434\x044c\x0442\x0449\x0437"
1313 L
"\x0439\x043a\x044b\x0435\x0433\x043c\x0446\x0447"
1314 L
"\x043d\x044f\x0436\x003d\x0431\x002d\x044e\x002e"
1315 L
"\x0451\x0445\x005c\x044a\x044d"
1317 #endif // defined(OS_WIN)
1318 {MockKeyboard::LAYOUT_UNITED_STATES
,
1319 L
"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1320 L
"\x0038\x0039\x0061\x0062\x0063\x0064\x0065\x0066"
1321 L
"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e"
1322 L
"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076"
1323 L
"\x0077\x0078\x0079\x007a\x003b\x003d\x002c\x002d"
1324 L
"\x002e\x002f\x0060\x005b\x005c\x005d\x0027\x0029"
1325 L
"\x0021\x0040\x0023\x0024\x0025\x005e\x0026\x002a"
1326 L
"\x0028\x0041\x0042\x0043\x0044\x0045\x0046\x0047"
1327 L
"\x0048\x0049\x004a\x004b\x004c\x004d\x004e\x004f"
1328 L
"\x0050\x0051\x0052\x0053\x0054\x0055\x0056\x0057"
1329 L
"\x0058\x0059\x005a\x003a\x002b\x003c\x005f\x003e"
1330 L
"\x003f\x007e\x007b\x007c\x007d\x0022"
1332 // This is ifdefed out for Linux to correspond to the fact that we don't
1333 // test alt+keystroke for now.
1334 L
"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1335 L
"\x0038\x0039\x0061\x0062\x0063\x0064\x0065\x0066"
1336 L
"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e"
1337 L
"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076"
1338 L
"\x0077\x0078\x0079\x007a\x003b\x003d\x002c\x002d"
1339 L
"\x002e\x002f\x0060\x005b\x005c\x005d\x0027"
1344 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(kLayouts
); ++i
) {
1345 // Load an HTML page consisting of one <div> element.
1346 // This <div> element is used by the EditorClientImpl class to insert
1347 // characters received through the RenderWidget::OnHandleInputEvent()
1349 view()->set_send_content_state_immediately(true);
1355 "<div id='test' contenteditable='true'>"
1359 ExecuteJavaScript("document.getElementById('test').focus();");
1360 render_thread_
->sink().ClearMessages();
1362 // For each key code, we send three keyboard events.
1363 // * Pressing only the key;
1364 // * Pressing the key and a left-shift key, and;
1365 // * Pressing the key and a right-alt (AltGr) key.
1366 static const MockKeyboard::Modifiers kModifiers
[] = {
1368 MockKeyboard::LEFT_SHIFT
,
1370 MockKeyboard::RIGHT_ALT
,
1374 MockKeyboard::Layout layout
= kLayouts
[i
].layout
;
1375 for (size_t j
= 0; j
< ARRAYSIZE_UNSAFE(kModifiers
); ++j
) {
1376 // Virtual key codes used for this test.
1377 static const int kKeyCodes
[] = {
1378 '0', '1', '2', '3', '4', '5', '6', '7',
1379 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
1380 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
1381 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
1387 ui::VKEY_OEM_PERIOD
,
1395 // Unclear how to handle this on Linux.
1400 MockKeyboard::Modifiers modifiers
= kModifiers
[j
];
1401 for (size_t k
= 0; k
< ARRAYSIZE_UNSAFE(kKeyCodes
); ++k
) {
1402 // Send a keyboard event to the RenderView object.
1403 // We should test a keyboard event only when the given keyboard-layout
1404 // driver is installed in a PC and the driver can assign a Unicode
1405 // charcter for the given tuple (layout, key-code, and modifiers).
1406 int key_code
= kKeyCodes
[k
];
1408 if (SendKeyEvent(layout
, key_code
, modifiers
, &char_code
) < 0)
1413 // Retrieve the text in the test page and compare it with the expected
1414 // text created from a virtual-key code, a character code, and the
1415 // modifier-key status.
1416 const int kMaxOutputCharacters
= 4096;
1417 std::wstring output
= UTF16ToWideHack(
1418 GetMainFrame()->contentAsText(kMaxOutputCharacters
));
1419 EXPECT_EQ(kLayouts
[i
].expected_result
, output
);
1426 // Crashy, http://crbug.com/53247.
1427 TEST_F(RenderViewImplTest
, DISABLED_DidFailProvisionalLoadWithErrorForError
) {
1428 GetMainFrame()->enableViewSourceMode(true);
1430 error
.domain
= WebString::fromUTF8(net::kErrorDomain
);
1431 error
.reason
= net::ERR_FILE_NOT_FOUND
;
1432 error
.unreachableURL
= GURL("http://foo");
1433 WebFrame
* web_frame
= GetMainFrame();
1434 // An error occurred.
1435 view()->didFailProvisionalLoad(web_frame
, error
);
1436 // Frame should exit view-source mode.
1437 EXPECT_FALSE(web_frame
->isViewSourceModeEnabled());
1440 TEST_F(RenderViewImplTest
, DidFailProvisionalLoadWithErrorForCancellation
) {
1441 GetMainFrame()->enableViewSourceMode(true);
1443 error
.domain
= WebString::fromUTF8(net::kErrorDomain
);
1444 error
.reason
= net::ERR_ABORTED
;
1445 error
.unreachableURL
= GURL("http://foo");
1446 WebFrame
* web_frame
= GetMainFrame();
1447 // A cancellation occurred.
1448 view()->didFailProvisionalLoad(web_frame
, error
);
1449 // Frame should stay in view-source mode.
1450 EXPECT_TRUE(web_frame
->isViewSourceModeEnabled());
1453 // Regression test for http://crbug.com/41562
1454 TEST_F(RenderViewImplTest
, UpdateTargetURLWithInvalidURL
) {
1455 const GURL
invalid_gurl("http://");
1456 view()->setMouseOverURL(WebKit::WebURL(invalid_gurl
));
1457 EXPECT_EQ(invalid_gurl
, view()->target_url_
);
1460 TEST_F(RenderViewImplTest
, SetHistoryLengthAndPrune
) {
1461 int expected_page_id
= -1;
1463 // No history to merge and no committed pages.
1464 view()->OnSetHistoryLengthAndPrune(0, -1);
1465 EXPECT_EQ(0, view()->history_list_length_
);
1466 EXPECT_EQ(-1, view()->history_list_offset_
);
1468 // History to merge and no committed pages.
1469 view()->OnSetHistoryLengthAndPrune(2, -1);
1470 EXPECT_EQ(2, view()->history_list_length_
);
1471 EXPECT_EQ(1, view()->history_list_offset_
);
1472 EXPECT_EQ(-1, view()->history_page_ids_
[0]);
1473 EXPECT_EQ(-1, view()->history_page_ids_
[1]);
1476 // No history to merge and a committed page to be kept.
1477 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1478 expected_page_id
= view()->page_id_
;
1479 view()->OnSetHistoryLengthAndPrune(0, expected_page_id
);
1480 EXPECT_EQ(1, view()->history_list_length_
);
1481 EXPECT_EQ(0, view()->history_list_offset_
);
1482 EXPECT_EQ(expected_page_id
, view()->history_page_ids_
[0]);
1485 // No history to merge and a committed page to be pruned.
1486 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1487 expected_page_id
= view()->page_id_
;
1488 view()->OnSetHistoryLengthAndPrune(0, expected_page_id
+ 1);
1489 EXPECT_EQ(0, view()->history_list_length_
);
1490 EXPECT_EQ(-1, view()->history_list_offset_
);
1493 // No history to merge and a committed page that the browser was unaware of.
1494 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1495 expected_page_id
= view()->page_id_
;
1496 view()->OnSetHistoryLengthAndPrune(0, -1);
1497 EXPECT_EQ(1, view()->history_list_length_
);
1498 EXPECT_EQ(0, view()->history_list_offset_
);
1499 EXPECT_EQ(expected_page_id
, view()->history_page_ids_
[0]);
1502 // History to merge and a committed page to be kept.
1503 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1504 expected_page_id
= view()->page_id_
;
1505 view()->OnSetHistoryLengthAndPrune(2, expected_page_id
);
1506 EXPECT_EQ(3, view()->history_list_length_
);
1507 EXPECT_EQ(2, view()->history_list_offset_
);
1508 EXPECT_EQ(-1, view()->history_page_ids_
[0]);
1509 EXPECT_EQ(-1, view()->history_page_ids_
[1]);
1510 EXPECT_EQ(expected_page_id
, view()->history_page_ids_
[2]);
1513 // History to merge and a committed page to be pruned.
1514 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1515 expected_page_id
= view()->page_id_
;
1516 view()->OnSetHistoryLengthAndPrune(2, expected_page_id
+ 1);
1517 EXPECT_EQ(2, view()->history_list_length_
);
1518 EXPECT_EQ(1, view()->history_list_offset_
);
1519 EXPECT_EQ(-1, view()->history_page_ids_
[0]);
1520 EXPECT_EQ(-1, view()->history_page_ids_
[1]);
1523 // History to merge and a committed page that the browser was unaware of.
1524 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1525 expected_page_id
= view()->page_id_
;
1526 view()->OnSetHistoryLengthAndPrune(2, -1);
1527 EXPECT_EQ(3, view()->history_list_length_
);
1528 EXPECT_EQ(2, view()->history_list_offset_
);
1529 EXPECT_EQ(-1, view()->history_page_ids_
[0]);
1530 EXPECT_EQ(-1, view()->history_page_ids_
[1]);
1531 EXPECT_EQ(expected_page_id
, view()->history_page_ids_
[2]);
1534 int expected_page_id_2
= -1;
1536 // No history to merge and two committed pages, both to be kept.
1537 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1538 expected_page_id
= view()->page_id_
;
1539 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1540 expected_page_id_2
= view()->page_id_
;
1541 EXPECT_GT(expected_page_id_2
, expected_page_id
);
1542 view()->OnSetHistoryLengthAndPrune(0, expected_page_id
);
1543 EXPECT_EQ(2, view()->history_list_length_
);
1544 EXPECT_EQ(1, view()->history_list_offset_
);
1545 EXPECT_EQ(expected_page_id
, view()->history_page_ids_
[0]);
1546 EXPECT_EQ(expected_page_id_2
, view()->history_page_ids_
[1]);
1549 // No history to merge and two committed pages, and only the second is kept.
1550 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1551 expected_page_id
= view()->page_id_
;
1552 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1553 expected_page_id_2
= view()->page_id_
;
1554 EXPECT_GT(expected_page_id_2
, expected_page_id
);
1555 view()->OnSetHistoryLengthAndPrune(0, expected_page_id_2
);
1556 EXPECT_EQ(1, view()->history_list_length_
);
1557 EXPECT_EQ(0, view()->history_list_offset_
);
1558 EXPECT_EQ(expected_page_id_2
, view()->history_page_ids_
[0]);
1561 // No history to merge and two committed pages, both of which the browser was
1563 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1564 expected_page_id
= view()->page_id_
;
1565 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1566 expected_page_id_2
= view()->page_id_
;
1567 EXPECT_GT(expected_page_id_2
, expected_page_id
);
1568 view()->OnSetHistoryLengthAndPrune(0, -1);
1569 EXPECT_EQ(2, view()->history_list_length_
);
1570 EXPECT_EQ(1, view()->history_list_offset_
);
1571 EXPECT_EQ(expected_page_id
, view()->history_page_ids_
[0]);
1572 EXPECT_EQ(expected_page_id_2
, view()->history_page_ids_
[1]);
1575 // History to merge and two committed pages, both to be kept.
1576 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1577 expected_page_id
= view()->page_id_
;
1578 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1579 expected_page_id_2
= view()->page_id_
;
1580 EXPECT_GT(expected_page_id_2
, expected_page_id
);
1581 view()->OnSetHistoryLengthAndPrune(2, expected_page_id
);
1582 EXPECT_EQ(4, view()->history_list_length_
);
1583 EXPECT_EQ(3, view()->history_list_offset_
);
1584 EXPECT_EQ(-1, view()->history_page_ids_
[0]);
1585 EXPECT_EQ(-1, view()->history_page_ids_
[1]);
1586 EXPECT_EQ(expected_page_id
, view()->history_page_ids_
[2]);
1587 EXPECT_EQ(expected_page_id_2
, view()->history_page_ids_
[3]);
1590 // History to merge and two committed pages, and only the second is kept.
1591 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1592 expected_page_id
= view()->page_id_
;
1593 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1594 expected_page_id_2
= view()->page_id_
;
1595 EXPECT_GT(expected_page_id_2
, expected_page_id
);
1596 view()->OnSetHistoryLengthAndPrune(2, expected_page_id_2
);
1597 EXPECT_EQ(3, view()->history_list_length_
);
1598 EXPECT_EQ(2, view()->history_list_offset_
);
1599 EXPECT_EQ(-1, view()->history_page_ids_
[0]);
1600 EXPECT_EQ(-1, view()->history_page_ids_
[1]);
1601 EXPECT_EQ(expected_page_id_2
, view()->history_page_ids_
[2]);
1604 // History to merge and two committed pages, both of which the browser was
1606 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1607 expected_page_id
= view()->page_id_
;
1608 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1609 expected_page_id_2
= view()->page_id_
;
1610 EXPECT_GT(expected_page_id_2
, expected_page_id
);
1611 view()->OnSetHistoryLengthAndPrune(2, -1);
1612 EXPECT_EQ(4, view()->history_list_length_
);
1613 EXPECT_EQ(3, view()->history_list_offset_
);
1614 EXPECT_EQ(-1, view()->history_page_ids_
[0]);
1615 EXPECT_EQ(-1, view()->history_page_ids_
[1]);
1616 EXPECT_EQ(expected_page_id
, view()->history_page_ids_
[2]);
1617 EXPECT_EQ(expected_page_id_2
, view()->history_page_ids_
[3]);
1620 TEST_F(RenderViewImplTest
, FindTitleForIntentsPage
) {
1621 view()->set_send_content_state_immediately(true);
1622 LoadHTML("<html><head><title>title</title>"
1623 "<intent action=\"a\" type=\"t\"></intent></head></html>");
1624 WebKit::WebIntentServiceInfo service
;
1625 service
.setAction(ASCIIToUTF16("a"));
1626 service
.setType(ASCIIToUTF16("t"));
1627 view()->registerIntentService(GetMainFrame(), service
);
1628 ProcessPendingMessages();
1630 EXPECT_TRUE(render_thread_
->sink().GetUniqueMessageMatching(
1631 IntentsHostMsg_RegisterIntentService::ID
));
1632 const IPC::Message
* msg
= render_thread_
->sink().GetUniqueMessageMatching(
1633 IntentsHostMsg_RegisterIntentService::ID
);
1635 webkit_glue::WebIntentServiceData service_data
;
1636 bool user_gesture
= true;
1637 IntentsHostMsg_RegisterIntentService::Read(msg
, &service_data
, &user_gesture
);
1638 EXPECT_EQ(ASCIIToUTF16("a"), service_data
.action
);
1639 EXPECT_EQ(ASCIIToUTF16("t"), service_data
.type
);
1640 EXPECT_EQ(ASCIIToUTF16("title"), service_data
.title
);
1641 EXPECT_FALSE(user_gesture
);
1644 TEST_F(RenderViewImplTest
, ContextMenu
) {
1645 LoadHTML("<div>Page A</div>");
1647 // Create a right click in the center of the iframe. (I'm hoping this will
1648 // make this a bit more robust in case of some other formatting or other bug.)
1649 WebMouseEvent mouse_event
;
1650 mouse_event
.type
= WebInputEvent::MouseDown
;
1651 mouse_event
.button
= WebMouseEvent::ButtonRight
;
1652 mouse_event
.x
= 250;
1653 mouse_event
.y
= 250;
1654 mouse_event
.globalX
= 250;
1655 mouse_event
.globalY
= 250;
1657 SendWebMouseEvent(mouse_event
);
1659 // Now simulate the corresponding up event which should display the menu
1660 mouse_event
.type
= WebInputEvent::MouseUp
;
1661 SendWebMouseEvent(mouse_event
);
1663 EXPECT_TRUE(render_thread_
->sink().GetUniqueMessageMatching(
1664 ViewHostMsg_ContextMenu::ID
));
1667 TEST_F(RenderViewImplTest
, TestBackForward
) {
1668 LoadHTML("<div id=pagename>Page A</div>");
1669 WebKit::WebHistoryItem page_a_item
= GetMainFrame()->currentHistoryItem();
1670 int was_page_a
= -1;
1671 string16 check_page_a
=
1673 "Number(document.getElementById('pagename').innerHTML == 'Page A')");
1674 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_a
, &was_page_a
));
1675 EXPECT_EQ(1, was_page_a
);
1677 LoadHTML("<div id=pagename>Page B</div>");
1678 int was_page_b
= -1;
1679 string16 check_page_b
=
1681 "Number(document.getElementById('pagename').innerHTML == 'Page B')");
1682 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b
, &was_page_b
));
1683 EXPECT_EQ(1, was_page_b
);
1685 LoadHTML("<div id=pagename>Page C</div>");
1686 int was_page_c
= -1;
1687 string16 check_page_c
=
1689 "Number(document.getElementById('pagename').innerHTML == 'Page C')");
1690 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_c
, &was_page_c
));
1691 EXPECT_EQ(1, was_page_b
);
1693 WebKit::WebHistoryItem forward_item
= GetMainFrame()->currentHistoryItem();
1694 GoBack(GetMainFrame()->previousHistoryItem());
1695 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b
, &was_page_b
));
1696 EXPECT_EQ(1, was_page_b
);
1698 GoForward(forward_item
);
1699 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_c
, &was_page_c
));
1700 EXPECT_EQ(1, was_page_c
);
1702 GoBack(GetMainFrame()->previousHistoryItem());
1703 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b
, &was_page_b
));
1704 EXPECT_EQ(1, was_page_b
);
1706 forward_item
= GetMainFrame()->currentHistoryItem();
1707 GoBack(page_a_item
);
1708 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_a
, &was_page_a
));
1709 EXPECT_EQ(1, was_page_a
);
1711 GoForward(forward_item
);
1712 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b
, &was_page_b
));
1713 EXPECT_EQ(1, was_page_b
);
1716 TEST_F(RenderViewImplTest
, GetCompositionCharacterBoundsTest
) {
1717 LoadHTML("<textarea id=\"test\"></textarea>");
1718 ExecuteJavaScript("document.getElementById('test').focus();");
1720 const string16 empty_string
= UTF8ToUTF16("");
1721 const std::vector
<WebKit::WebCompositionUnderline
> empty_underline
;
1722 std::vector
<gfx::Rect
> bounds
;
1723 view()->OnSetFocus(true);
1724 view()->OnSetInputMethodActive(true);
1726 // ASCII composition
1727 const string16 ascii_composition
= UTF8ToUTF16("aiueo");
1728 view()->OnImeSetComposition(ascii_composition
, empty_underline
, 0, 0);
1729 view()->GetCompositionCharacterBounds(&bounds
);
1730 ASSERT_EQ(ascii_composition
.size(), bounds
.size());
1731 for (size_t i
= 0; i
< bounds
.size(); ++i
)
1732 EXPECT_LT(0, bounds
[i
].width());
1733 view()->OnImeConfirmComposition(empty_string
, ui::Range::InvalidRange());
1735 // Non surrogate pair unicode character.
1736 const string16 unicode_composition
= UTF8ToUTF16(
1737 "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
1738 view()->OnImeSetComposition(unicode_composition
, empty_underline
, 0, 0);
1739 view()->GetCompositionCharacterBounds(&bounds
);
1740 ASSERT_EQ(unicode_composition
.size(), bounds
.size());
1741 for (size_t i
= 0; i
< bounds
.size(); ++i
)
1742 EXPECT_LT(0, bounds
[i
].width());
1743 view()->OnImeConfirmComposition(empty_string
, ui::Range::InvalidRange());
1745 // Surrogate pair character.
1746 const string16 surrogate_pair_char
= UTF8ToUTF16("\xF0\xA0\xAE\x9F");
1747 view()->OnImeSetComposition(surrogate_pair_char
,
1751 view()->GetCompositionCharacterBounds(&bounds
);
1752 ASSERT_EQ(surrogate_pair_char
.size(), bounds
.size());
1753 EXPECT_LT(0, bounds
[0].width());
1754 EXPECT_EQ(0, bounds
[1].width());
1755 view()->OnImeConfirmComposition(empty_string
, ui::Range::InvalidRange());
1758 const string16 surrogate_pair_mixed_composition
=
1759 surrogate_pair_char
+ UTF8ToUTF16("\xE3\x81\x82") + surrogate_pair_char
+
1760 UTF8ToUTF16("b") + surrogate_pair_char
;
1761 const size_t utf16_length
= 8UL;
1762 const bool is_surrogate_pair_empty_rect
[8] = {
1763 false, true, false, false, true, false, false, true };
1764 view()->OnImeSetComposition(surrogate_pair_mixed_composition
,
1768 view()->GetCompositionCharacterBounds(&bounds
);
1769 ASSERT_EQ(utf16_length
, bounds
.size());
1770 for (size_t i
= 0; i
< utf16_length
; ++i
) {
1771 if (is_surrogate_pair_empty_rect
[i
]) {
1772 EXPECT_EQ(0, bounds
[i
].width());
1774 EXPECT_LT(0, bounds
[i
].width());
1777 view()->OnImeConfirmComposition(empty_string
, ui::Range::InvalidRange());
1780 TEST_F(RenderViewImplTest
, ZoomLimit
) {
1781 const double kMinZoomLevel
=
1782 WebKit::WebView::zoomFactorToZoomLevel(kMinimumZoomFactor
);
1783 const double kMaxZoomLevel
=
1784 WebKit::WebView::zoomFactorToZoomLevel(kMaximumZoomFactor
);
1786 ViewMsg_Navigate_Params params
;
1787 params
.page_id
= -1;
1788 params
.navigation_type
= ViewMsg_Navigate_Type::NORMAL
;
1790 // Verifies navigation to a URL with preset zoom level indeed sets the level.
1791 // Regression test for http://crbug.com/139559, where the level was not
1792 // properly set when it is out of the default zoom limits of WebView.
1793 params
.url
= GURL("data:text/html,min_zoomlimit_test");
1794 view()->OnSetZoomLevelForLoadingURL(params
.url
, kMinZoomLevel
);
1795 view()->OnNavigate(params
);
1796 ProcessPendingMessages();
1797 EXPECT_DOUBLE_EQ(kMinZoomLevel
, view()->GetWebView()->zoomLevel());
1799 // It should work even when the zoom limit is temporarily changed in the page.
1800 view()->GetWebView()->zoomLimitsChanged(
1801 WebKit::WebView::zoomFactorToZoomLevel(1.0),
1802 WebKit::WebView::zoomFactorToZoomLevel(1.0));
1803 params
.url
= GURL("data:text/html,max_zoomlimit_test");
1804 view()->OnSetZoomLevelForLoadingURL(params
.url
, kMaxZoomLevel
);
1805 view()->OnNavigate(params
);
1806 ProcessPendingMessages();
1807 EXPECT_DOUBLE_EQ(kMaxZoomLevel
, view()->GetWebView()->zoomLevel());
1810 TEST_F(RenderViewImplTest
, SetEditableSelectionAndComposition
) {
1811 // Load an HTML page consisting of an input field.
1816 "<input id=\"test1\" value=\"some test text hello\"></input>"
1819 ExecuteJavaScript("document.getElementById('test1').focus();");
1820 view()->OnSetEditableSelectionOffsets(4, 8);
1821 const std::vector
<WebKit::WebCompositionUnderline
> empty_underline
;
1822 view()->OnSetCompositionFromExistingText(7,10, empty_underline
);
1823 WebKit::WebTextInputInfo info
= view()->webview()->textInputInfo();
1824 EXPECT_EQ(4, info
.selectionStart
);
1825 EXPECT_EQ(8, info
.selectionEnd
);
1826 EXPECT_EQ(7, info
.compositionStart
);
1827 EXPECT_EQ(10, info
.compositionEnd
);
1828 view()->OnUnselect();
1829 info
= view()->webview()->textInputInfo();
1830 EXPECT_EQ(0, info
.selectionStart
);
1831 EXPECT_EQ(0, info
.selectionEnd
);
1835 TEST_F(RenderViewImplTest
, OnExtendSelectionAndDelete
) {
1836 // Load an HTML page consisting of an input field.
1841 "<input id=\"test1\" value=\"abcdefghijklmnopqrstuvwxyz\"></input>"
1844 ExecuteJavaScript("document.getElementById('test1').focus();");
1845 view()->OnSetEditableSelectionOffsets(10, 10);
1846 view()->OnExtendSelectionAndDelete(3, 4);
1847 WebKit::WebTextInputInfo info
= view()->webview()->textInputInfo();
1848 EXPECT_EQ("abcdefgopqrstuvwxyz", info
.value
);
1849 EXPECT_EQ(7, info
.selectionStart
);
1850 EXPECT_EQ(7, info
.selectionEnd
);
1851 view()->OnSetEditableSelectionOffsets(4, 8);
1852 view()->OnExtendSelectionAndDelete(2, 5);
1853 info
= view()->webview()->textInputInfo();
1854 EXPECT_EQ("abuvwxyz", info
.value
);
1855 EXPECT_EQ(2, info
.selectionStart
);
1856 EXPECT_EQ(2, info
.selectionEnd
);
1859 } // namespace content