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.
4
5 #include "base/basictypes.h"
6
7 #include "base/memory/shared_memory.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/win/windows_version.h"
11 #include "content/common/ssl_status_serialization.h"
12 #include "content/common/view_messages.h"
13 #include "content/public/browser/native_web_keyboard_event.h"
14 #include "content/public/browser/web_ui_controller_factory.h"
15 #include "content/public/common/bindings_policy.h"
16 #include "content/public/common/page_zoom.h"
17 #include "content/public/common/url_constants.h"
18 #include "content/public/common/url_utils.h"
19 #include "content/public/renderer/document_state.h"
20 #include "content/public/renderer/history_item_serialization.h"
21 #include "content/public/renderer/navigation_state.h"
22 #include "content/public/test/render_view_test.h"
23 #include "content/renderer/render_view_impl.h"
24 #include "content/shell/browser/shell_content_browser_client.h"
25 #include "content/shell/common/shell_content_client.h"
26 #include "content/test/mock_keyboard.h"
27 #include "net/base/net_errors.h"
28 #include "net/cert/cert_status_flags.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30 #include "third_party/WebKit/public/platform/WebData.h"
31 #include "third_party/WebKit/public/platform/WebHTTPBody.h"
32 #include "third_party/WebKit/public/platform/WebString.h"
33 #include "third_party/WebKit/public/platform/WebURLError.h"
34 #include "third_party/WebKit/public/platform/WebURLResponse.h"
35 #include "third_party/WebKit/public/web/WebDataSource.h"
36 #include "third_party/WebKit/public/web/WebFrame.h"
37 #include "third_party/WebKit/public/web/WebHistoryItem.h"
38 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
39 #include "third_party/WebKit/public/web/WebView.h"
40 #include "third_party/WebKit/public/web/WebWindowFeatures.h"
41 #include "ui/events/keycodes/keyboard_codes.h"
42 #include "ui/gfx/codec/jpeg_codec.h"
43 #include "ui/gfx/range/range.h"
44
45 #if defined(OS_LINUX) && !defined(USE_AURA)
46 #include "ui/base/gtk/event_synthesis_gtk.h"
47 #endif
48
49 #if defined(USE_AURA)
50 #include "ui/events/event.h"
51 #endif
52
53 #if defined(USE_AURA) && defined(USE_X11)
54 #include <X11/Xlib.h>
55 #include "ui/events/event_constants.h"
56 #include "ui/events/keycodes/keyboard_code_conversion.h"
57 #include "ui/events/test/events_test_utils_x11.h"
58 #endif
59
60 #if defined(USE_OZONE)
61 #include "ui/events/keycodes/keyboard_code_conversion.h"
62 #endif
63
64 using blink::WebFrame;
65 using blink::WebInputEvent;
66 using blink::WebMouseEvent;
67 using blink::WebRuntimeFeatures;
68 using blink::WebString;
69 using blink::WebTextDirection;
70 using blink::WebURLError;
71
72 namespace content {
73
74 namespace {
75
76 #if (defined(USE_AURA) && defined(USE_X11)) || defined(USE_OZONE)
77 // Converts MockKeyboard::Modifiers to ui::EventFlags.
ConvertMockKeyboardModifier(MockKeyboard::Modifiers modifiers)78 int ConvertMockKeyboardModifier(MockKeyboard::Modifiers modifiers) {
79 static struct ModifierMap {
80 MockKeyboard::Modifiers src;
81 int dst;
82 } kModifierMap[] = {
83 { MockKeyboard::LEFT_SHIFT, ui::EF_SHIFT_DOWN },
84 { MockKeyboard::RIGHT_SHIFT, ui::EF_SHIFT_DOWN },
85 { MockKeyboard::LEFT_CONTROL, ui::EF_CONTROL_DOWN },
86 { MockKeyboard::RIGHT_CONTROL, ui::EF_CONTROL_DOWN },
87 { MockKeyboard::LEFT_ALT, ui::EF_ALT_DOWN },
88 { MockKeyboard::RIGHT_ALT, ui::EF_ALT_DOWN },
89 };
90 int flags = 0;
91 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kModifierMap); ++i) {
92 if (kModifierMap[i].src & modifiers) {
93 flags |= kModifierMap[i].dst;
94 }
95 }
96 return flags;
97 }
98 #endif
99
100 class WebUITestWebUIControllerFactory : public WebUIControllerFactory {
101 public:
CreateWebUIControllerForURL(WebUI * web_ui,const GURL & url) const102 virtual WebUIController* CreateWebUIControllerForURL(
103 WebUI* web_ui, const GURL& url) const OVERRIDE {
104 return NULL;
105 }
GetWebUIType(BrowserContext * browser_context,const GURL & url) const106 virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
107 const GURL& url) const OVERRIDE {
108 return WebUI::kNoWebUI;
109 }
UseWebUIForURL(BrowserContext * browser_context,const GURL & url) const110 virtual bool UseWebUIForURL(BrowserContext* browser_context,
111 const GURL& url) const OVERRIDE {
112 return HasWebUIScheme(url);
113 }
UseWebUIBindingsForURL(BrowserContext * browser_context,const GURL & url) const114 virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context,
115 const GURL& url) const OVERRIDE {
116 return HasWebUIScheme(url);
117 }
118 };
119
120 } // namespace
121
122 class RenderViewImplTest : public RenderViewTest {
123 public:
RenderViewImplTest()124 RenderViewImplTest() {
125 // Attach a pseudo keyboard device to this object.
126 mock_keyboard_.reset(new MockKeyboard());
127 }
128
~RenderViewImplTest()129 virtual ~RenderViewImplTest() {}
130
SetUp()131 virtual void SetUp() OVERRIDE {
132 RenderViewTest::SetUp();
133 // Enable Blink's experimental and test only features so that test code
134 // does not have to bother enabling each feature.
135 WebRuntimeFeatures::enableExperimentalFeatures(true);
136 WebRuntimeFeatures::enableTestOnlyFeatures(true);
137 }
138
view()139 RenderViewImpl* view() {
140 return static_cast<RenderViewImpl*>(view_);
141 }
142
143 // Sends IPC messages that emulates a key-press event.
SendKeyEvent(MockKeyboard::Layout layout,int key_code,MockKeyboard::Modifiers modifiers,base::string16 * output)144 int SendKeyEvent(MockKeyboard::Layout layout,
145 int key_code,
146 MockKeyboard::Modifiers modifiers,
147 base::string16* output) {
148 #if defined(OS_WIN)
149 // Retrieve the Unicode character for the given tuple (keyboard-layout,
150 // key-code, and modifiers).
151 // Exit when a keyboard-layout driver cannot assign a Unicode character to
152 // the tuple to prevent sending an invalid key code to the RenderView
153 // object.
154 CHECK(mock_keyboard_.get());
155 CHECK(output);
156 int length = mock_keyboard_->GetCharacters(layout, key_code, modifiers,
157 output);
158 if (length != 1)
159 return -1;
160
161 // Create IPC messages from Windows messages and send them to our
162 // back-end.
163 // A keyboard event of Windows consists of three Windows messages:
164 // WM_KEYDOWN, WM_CHAR, and WM_KEYUP.
165 // WM_KEYDOWN and WM_KEYUP sends virtual-key codes. On the other hand,
166 // WM_CHAR sends a composed Unicode character.
167 MSG msg1 = { NULL, WM_KEYDOWN, key_code, 0 };
168 #if defined(USE_AURA)
169 ui::KeyEvent evt1(msg1, false);
170 NativeWebKeyboardEvent keydown_event(&evt1);
171 #else
172 NativeWebKeyboardEvent keydown_event(msg1);
173 #endif
174 SendNativeKeyEvent(keydown_event);
175
176 MSG msg2 = { NULL, WM_CHAR, (*output)[0], 0 };
177 #if defined(USE_AURA)
178 ui::KeyEvent evt2(msg2, true);
179 NativeWebKeyboardEvent char_event(&evt2);
180 #else
181 NativeWebKeyboardEvent char_event(msg2);
182 #endif
183 SendNativeKeyEvent(char_event);
184
185 MSG msg3 = { NULL, WM_KEYUP, key_code, 0 };
186 #if defined(USE_AURA)
187 ui::KeyEvent evt3(msg3, false);
188 NativeWebKeyboardEvent keyup_event(&evt3);
189 #else
190 NativeWebKeyboardEvent keyup_event(msg3);
191 #endif
192 SendNativeKeyEvent(keyup_event);
193
194 return length;
195 #elif defined(USE_AURA) && defined(USE_X11)
196 // We ignore |layout|, which means we are only testing the layout of the
197 // current locale. TODO(mazda): fix this to respect |layout|.
198 CHECK(output);
199 const int flags = ConvertMockKeyboardModifier(modifiers);
200
201 ui::ScopedXI2Event xevent;
202 xevent.InitKeyEvent(ui::ET_KEY_PRESSED,
203 static_cast<ui::KeyboardCode>(key_code),
204 flags);
205 ui::KeyEvent event1(xevent, false);
206 NativeWebKeyboardEvent keydown_event(&event1);
207 SendNativeKeyEvent(keydown_event);
208
209 xevent.InitKeyEvent(ui::ET_KEY_PRESSED,
210 static_cast<ui::KeyboardCode>(key_code),
211 flags);
212 ui::KeyEvent event2(xevent, true);
213 NativeWebKeyboardEvent char_event(&event2);
214 SendNativeKeyEvent(char_event);
215
216 xevent.InitKeyEvent(ui::ET_KEY_RELEASED,
217 static_cast<ui::KeyboardCode>(key_code),
218 flags);
219 ui::KeyEvent event3(xevent, false);
220 NativeWebKeyboardEvent keyup_event(&event3);
221 SendNativeKeyEvent(keyup_event);
222
223 long c = GetCharacterFromKeyCode(static_cast<ui::KeyboardCode>(key_code),
224 flags);
225 output->assign(1, static_cast<char16>(c));
226 return 1;
227 #elif defined(USE_OZONE)
228 const int flags = ConvertMockKeyboardModifier(modifiers);
229
230 // Ozone's native events are ui::Events. So first create the "native" event,
231 // then create the actual ui::KeyEvent with the native event.
232 ui::KeyEvent keydown_native_event(ui::ET_KEY_PRESSED,
233 static_cast<ui::KeyboardCode>(key_code),
234 flags,
235 true);
236 ui::KeyEvent keydown_event(&keydown_native_event, false);
237 NativeWebKeyboardEvent keydown_web_event(&keydown_event);
238 SendNativeKeyEvent(keydown_web_event);
239
240 ui::KeyEvent char_native_event(ui::ET_KEY_PRESSED,
241 static_cast<ui::KeyboardCode>(key_code),
242 flags,
243 true);
244 ui::KeyEvent char_event(&char_native_event, true);
245 NativeWebKeyboardEvent char_web_event(&char_event);
246 SendNativeKeyEvent(char_web_event);
247
248 ui::KeyEvent keyup_native_event(ui::ET_KEY_RELEASED,
249 static_cast<ui::KeyboardCode>(key_code),
250 flags,
251 true);
252 ui::KeyEvent keyup_event(&keyup_native_event, false);
253 NativeWebKeyboardEvent keyup_web_event(&keyup_event);
254 SendNativeKeyEvent(keyup_web_event);
255
256 long c = GetCharacterFromKeyCode(static_cast<ui::KeyboardCode>(key_code),
257 flags);
258 output->assign(1, static_cast<char16>(c));
259 return 1;
260 #elif defined(TOOLKIT_GTK)
261 // We ignore |layout|, which means we are only testing the layout of the
262 // current locale. TODO(estade): fix this to respect |layout|.
263 std::vector<GdkEvent*> events;
264 ui::SynthesizeKeyPressEvents(
265 NULL, static_cast<ui::KeyboardCode>(key_code),
266 modifiers & (MockKeyboard::LEFT_CONTROL | MockKeyboard::RIGHT_CONTROL),
267 modifiers & (MockKeyboard::LEFT_SHIFT | MockKeyboard::RIGHT_SHIFT),
268 modifiers & (MockKeyboard::LEFT_ALT | MockKeyboard::RIGHT_ALT),
269 &events);
270
271 guint32 unicode_key = 0;
272 for (size_t i = 0; i < events.size(); ++i) {
273 // Only send the up/down events for key press itself (skip the up/down
274 // events for the modifier keys).
275 if ((i + 1) == (events.size() / 2) || i == (events.size() / 2)) {
276 unicode_key = gdk_keyval_to_unicode(events[i]->key.keyval);
277 NativeWebKeyboardEvent webkit_event(events[i]);
278 SendNativeKeyEvent(webkit_event);
279
280 // Need to add a char event after the key down.
281 if (webkit_event.type == blink::WebInputEvent::RawKeyDown) {
282 NativeWebKeyboardEvent char_event = webkit_event;
283 char_event.type = blink::WebInputEvent::Char;
284 char_event.skip_in_browser = true;
285 SendNativeKeyEvent(char_event);
286 }
287 }
288 gdk_event_free(events[i]);
289 }
290
291 output->assign(1, static_cast<char16>(unicode_key));
292 return 1;
293 #else
294 NOTIMPLEMENTED();
295 return L'\0';
296 #endif
297 }
298
299 private:
300 scoped_ptr<MockKeyboard> mock_keyboard_;
301 };
302
303 // Test that we get form state change notifications when input fields change.
TEST_F(RenderViewImplTest,DISABLED_OnNavStateChanged)304 TEST_F(RenderViewImplTest, DISABLED_OnNavStateChanged) {
305 // Don't want any delay for form state sync changes. This will still post a
306 // message so updates will get coalesced, but as soon as we spin the message
307 // loop, it will generate an update.
308 view()->set_send_content_state_immediately(true);
309
310 LoadHTML("<input type=\"text\" id=\"elt_text\"></input>");
311
312 // We should NOT have gotten a form state change notification yet.
313 EXPECT_FALSE(render_thread_->sink().GetFirstMessageMatching(
314 ViewHostMsg_UpdateState::ID));
315 render_thread_->sink().ClearMessages();
316
317 // Change the value of the input. We should have gotten an update state
318 // notification. We need to spin the message loop to catch this update.
319 ExecuteJavaScript("document.getElementById('elt_text').value = 'foo';");
320 ProcessPendingMessages();
321 EXPECT_TRUE(render_thread_->sink().GetUniqueMessageMatching(
322 ViewHostMsg_UpdateState::ID));
323 }
324
TEST_F(RenderViewImplTest,OnNavigationHttpPost)325 TEST_F(RenderViewImplTest, OnNavigationHttpPost) {
326 ViewMsg_Navigate_Params nav_params;
327
328 // An http url will trigger a resource load so cannot be used here.
329 nav_params.url = GURL("data:text/html,<div>Page</div>");
330 nav_params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
331 nav_params.transition = PAGE_TRANSITION_TYPED;
332 nav_params.page_id = -1;
333 nav_params.is_post = true;
334
335 // Set up post data.
336 const unsigned char* raw_data = reinterpret_cast<const unsigned char*>(
337 "post \0\ndata");
338 const unsigned int length = 11;
339 const std::vector<unsigned char> post_data(raw_data, raw_data + length);
340 nav_params.browser_initiated_post_data = post_data;
341
342 view()->OnNavigate(nav_params);
343 ProcessPendingMessages();
344
345 const IPC::Message* frame_navigate_msg =
346 render_thread_->sink().GetUniqueMessageMatching(
347 ViewHostMsg_FrameNavigate::ID);
348 EXPECT_TRUE(frame_navigate_msg);
349
350 ViewHostMsg_FrameNavigate::Param host_nav_params;
351 ViewHostMsg_FrameNavigate::Read(frame_navigate_msg, &host_nav_params);
352 EXPECT_TRUE(host_nav_params.a.is_post);
353
354 // Check post data sent to browser matches
355 EXPECT_TRUE(host_nav_params.a.page_state.IsValid());
356 const blink::WebHistoryItem item = PageStateToHistoryItem(
357 host_nav_params.a.page_state);
358 blink::WebHTTPBody body = item.httpBody();
359 blink::WebHTTPBody::Element element;
360 bool successful = body.elementAt(0, element);
361 EXPECT_TRUE(successful);
362 EXPECT_EQ(blink::WebHTTPBody::Element::TypeData, element.type);
363 EXPECT_EQ(length, element.data.size());
364 EXPECT_EQ(0, memcmp(raw_data, element.data.data(), length));
365 }
366
TEST_F(RenderViewImplTest,DecideNavigationPolicy)367 TEST_F(RenderViewImplTest, DecideNavigationPolicy) {
368 WebUITestWebUIControllerFactory factory;
369 WebUIControllerFactory::RegisterFactory(&factory);
370
371 DocumentState state;
372 state.set_navigation_state(NavigationState::CreateContentInitiated());
373
374 // Navigations to normal HTTP URLs can be handled locally.
375 blink::WebURLRequest request(GURL("http://foo.com"));
376 blink::WebNavigationPolicy policy = view()->decidePolicyForNavigation(
377 GetMainFrame(),
378 &state,
379 request,
380 blink::WebNavigationTypeLinkClicked,
381 blink::WebNavigationPolicyCurrentTab,
382 false);
383 EXPECT_EQ(blink::WebNavigationPolicyCurrentTab, policy);
384
385 // Verify that form posts to WebUI URLs will be sent to the browser process.
386 blink::WebURLRequest form_request(GURL("chrome://foo"));
387 form_request.setHTTPMethod("POST");
388 policy = view()->decidePolicyForNavigation(
389 GetMainFrame(),
390 &state,
391 form_request,
392 blink::WebNavigationTypeFormSubmitted,
393 blink::WebNavigationPolicyCurrentTab,
394 false);
395 EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
396
397 // Verify that popup links to WebUI URLs also are sent to browser.
398 blink::WebURLRequest popup_request(GURL("chrome://foo"));
399 policy = view()->decidePolicyForNavigation(
400 GetMainFrame(),
401 &state,
402 popup_request,
403 blink::WebNavigationTypeLinkClicked,
404 blink::WebNavigationPolicyNewForegroundTab,
405 false);
406 EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
407 }
408
TEST_F(RenderViewImplTest,DecideNavigationPolicyHandlesAllTopLevel)409 TEST_F(RenderViewImplTest, DecideNavigationPolicyHandlesAllTopLevel) {
410 DocumentState state;
411 state.set_navigation_state(NavigationState::CreateContentInitiated());
412
413 RendererPreferences prefs = view()->renderer_preferences();
414 prefs.browser_handles_all_top_level_requests = true;
415 view()->OnSetRendererPrefs(prefs);
416
417 const blink::WebNavigationType kNavTypes[] = {
418 blink::WebNavigationTypeLinkClicked,
419 blink::WebNavigationTypeFormSubmitted,
420 blink::WebNavigationTypeBackForward,
421 blink::WebNavigationTypeReload,
422 blink::WebNavigationTypeFormResubmitted,
423 blink::WebNavigationTypeOther,
424 };
425
426 blink::WebURLRequest request(GURL("http://foo.com"));
427 for (size_t i = 0; i < arraysize(kNavTypes); ++i) {
428 blink::WebNavigationPolicy policy = view()->decidePolicyForNavigation(
429 GetMainFrame(),
430 &state,
431 request,
432 kNavTypes[i],
433 blink::WebNavigationPolicyCurrentTab,
434 false);
435 EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
436 }
437 }
438
TEST_F(RenderViewImplTest,DecideNavigationPolicyForWebUI)439 TEST_F(RenderViewImplTest, DecideNavigationPolicyForWebUI) {
440 // Enable bindings to simulate a WebUI view.
441 view()->OnAllowBindings(BINDINGS_POLICY_WEB_UI);
442
443 DocumentState state;
444 state.set_navigation_state(NavigationState::CreateContentInitiated());
445
446 // Navigations to normal HTTP URLs will be sent to browser process.
447 blink::WebURLRequest request(GURL("http://foo.com"));
448 blink::WebNavigationPolicy policy = view()->decidePolicyForNavigation(
449 GetMainFrame(),
450 &state,
451 request,
452 blink::WebNavigationTypeLinkClicked,
453 blink::WebNavigationPolicyCurrentTab,
454 false);
455 EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
456
457 // Navigations to WebUI URLs will also be sent to browser process.
458 blink::WebURLRequest webui_request(GURL("chrome://foo"));
459 policy = view()->decidePolicyForNavigation(
460 GetMainFrame(),
461 &state,
462 webui_request,
463 blink::WebNavigationTypeLinkClicked,
464 blink::WebNavigationPolicyCurrentTab,
465 false);
466 EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
467
468 // Verify that form posts to data URLs will be sent to the browser process.
469 blink::WebURLRequest data_request(GURL("data:text/html,foo"));
470 data_request.setHTTPMethod("POST");
471 policy = view()->decidePolicyForNavigation(
472 GetMainFrame(),
473 &state,
474 data_request,
475 blink::WebNavigationTypeFormSubmitted,
476 blink::WebNavigationPolicyCurrentTab,
477 false);
478 EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
479
480 // Verify that a popup that creates a view first and then navigates to a
481 // normal HTTP URL will be sent to the browser process, even though the
482 // new view does not have any enabled_bindings_.
483 blink::WebURLRequest popup_request(GURL("http://foo.com"));
484 blink::WebView* new_web_view = view()->createView(
485 GetMainFrame(), popup_request, blink::WebWindowFeatures(), "foo",
486 blink::WebNavigationPolicyNewForegroundTab, false);
487 RenderViewImpl* new_view = RenderViewImpl::FromWebView(new_web_view);
488 policy = new_view->decidePolicyForNavigation(
489 new_web_view->mainFrame(),
490 &state,
491 popup_request,
492 blink::WebNavigationTypeLinkClicked,
493 blink::WebNavigationPolicyNewForegroundTab,
494 false);
495 EXPECT_EQ(blink::WebNavigationPolicyIgnore, policy);
496
497 // Clean up after the new view so we don't leak it.
498 new_view->Close();
499 new_view->Release();
500 }
501
502 // Ensure the RenderViewImpl sends an ACK to a SwapOut request, even if it is
503 // already swapped out. http://crbug.com/93427.
TEST_F(RenderViewImplTest,SendSwapOutACK)504 TEST_F(RenderViewImplTest, SendSwapOutACK) {
505 LoadHTML("<div>Page A</div>");
506 int initial_page_id = view()->GetPageId();
507
508 // Respond to a swap out request.
509 view()->OnSwapOut();
510
511 // Ensure the swap out commits synchronously.
512 EXPECT_NE(initial_page_id, view()->GetPageId());
513
514 // Check for a valid OnSwapOutACK.
515 const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching(
516 ViewHostMsg_SwapOut_ACK::ID);
517 ASSERT_TRUE(msg);
518
519 // It is possible to get another swap out request. Ensure that we send
520 // an ACK, even if we don't have to do anything else.
521 render_thread_->sink().ClearMessages();
522 view()->OnSwapOut();
523 const IPC::Message* msg2 = render_thread_->sink().GetUniqueMessageMatching(
524 ViewHostMsg_SwapOut_ACK::ID);
525 ASSERT_TRUE(msg2);
526
527 // If we navigate back to this RenderView, ensure we don't send a state
528 // update for the swapped out URL. (http://crbug.com/72235)
529 ViewMsg_Navigate_Params nav_params;
530 nav_params.url = GURL("data:text/html,<div>Page B</div>");
531 nav_params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
532 nav_params.transition = PAGE_TRANSITION_TYPED;
533 nav_params.current_history_list_length = 1;
534 nav_params.current_history_list_offset = 0;
535 nav_params.pending_history_list_offset = 1;
536 nav_params.page_id = -1;
537 view()->OnNavigate(nav_params);
538 ProcessPendingMessages();
539 const IPC::Message* msg3 = render_thread_->sink().GetUniqueMessageMatching(
540 ViewHostMsg_UpdateState::ID);
541 EXPECT_FALSE(msg3);
542 }
543
544 // Ensure the RenderViewImpl reloads the previous page if a reload request
545 // arrives while it is showing swappedout://. http://crbug.com/143155.
TEST_F(RenderViewImplTest,ReloadWhileSwappedOut)546 TEST_F(RenderViewImplTest, ReloadWhileSwappedOut) {
547 // Load page A.
548 LoadHTML("<div>Page A</div>");
549
550 // Load page B, which will trigger an UpdateState message for page A.
551 LoadHTML("<div>Page B</div>");
552
553 // Check for a valid UpdateState message for page A.
554 ProcessPendingMessages();
555 const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
556 ViewHostMsg_UpdateState::ID);
557 ASSERT_TRUE(msg_A);
558 int page_id_A;
559 PageState state_A;
560 ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A);
561 EXPECT_EQ(1, page_id_A);
562 render_thread_->sink().ClearMessages();
563
564 // Back to page A (page_id 1) and commit.
565 ViewMsg_Navigate_Params params_A;
566 params_A.navigation_type = ViewMsg_Navigate_Type::NORMAL;
567 params_A.transition = PAGE_TRANSITION_FORWARD_BACK;
568 params_A.current_history_list_length = 2;
569 params_A.current_history_list_offset = 1;
570 params_A.pending_history_list_offset = 0;
571 params_A.page_id = 1;
572 params_A.page_state = state_A;
573 view()->OnNavigate(params_A);
574 ProcessPendingMessages();
575
576 // Respond to a swap out request.
577 view()->OnSwapOut();
578
579 // Check for a OnSwapOutACK.
580 const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching(
581 ViewHostMsg_SwapOut_ACK::ID);
582 ASSERT_TRUE(msg);
583 render_thread_->sink().ClearMessages();
584
585 // It is possible to get a reload request at this point, containing the
586 // params.page_state of the initial page (e.g., if the new page fails the
587 // provisional load in the renderer process, after we unload the old page).
588 // Ensure the old page gets reloaded, not swappedout://.
589 ViewMsg_Navigate_Params nav_params;
590 nav_params.url = GURL("data:text/html,<div>Page A</div>");
591 nav_params.navigation_type = ViewMsg_Navigate_Type::RELOAD;
592 nav_params.transition = PAGE_TRANSITION_RELOAD;
593 nav_params.current_history_list_length = 2;
594 nav_params.current_history_list_offset = 0;
595 nav_params.pending_history_list_offset = 0;
596 nav_params.page_id = 1;
597 nav_params.page_state = state_A;
598 view()->OnNavigate(nav_params);
599 ProcessPendingMessages();
600
601 // Verify page A committed, not swappedout://.
602 const IPC::Message* frame_navigate_msg =
603 render_thread_->sink().GetUniqueMessageMatching(
604 ViewHostMsg_FrameNavigate::ID);
605 EXPECT_TRUE(frame_navigate_msg);
606
607 // Read URL out of the parent trait of the params object.
608 ViewHostMsg_FrameNavigate::Param commit_params;
609 ViewHostMsg_FrameNavigate::Read(frame_navigate_msg, &commit_params);
610 EXPECT_NE(GURL("swappedout://"), commit_params.a.url);
611 }
612
613
614 // Test that we get the correct UpdateState message when we go back twice
615 // quickly without committing. Regression test for http://crbug.com/58082.
616 // Disabled: http://crbug.com/157357 .
TEST_F(RenderViewImplTest,DISABLED_LastCommittedUpdateState)617 TEST_F(RenderViewImplTest, DISABLED_LastCommittedUpdateState) {
618 // Load page A.
619 LoadHTML("<div>Page A</div>");
620
621 // Load page B, which will trigger an UpdateState message for page A.
622 LoadHTML("<div>Page B</div>");
623
624 // Check for a valid UpdateState message for page A.
625 ProcessPendingMessages();
626 const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
627 ViewHostMsg_UpdateState::ID);
628 ASSERT_TRUE(msg_A);
629 int page_id_A;
630 PageState state_A;
631 ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A);
632 EXPECT_EQ(1, page_id_A);
633 render_thread_->sink().ClearMessages();
634
635 // Load page C, which will trigger an UpdateState message for page B.
636 LoadHTML("<div>Page C</div>");
637
638 // Check for a valid UpdateState for page B.
639 ProcessPendingMessages();
640 const IPC::Message* msg_B = render_thread_->sink().GetUniqueMessageMatching(
641 ViewHostMsg_UpdateState::ID);
642 ASSERT_TRUE(msg_B);
643 int page_id_B;
644 PageState state_B;
645 ViewHostMsg_UpdateState::Read(msg_B, &page_id_B, &state_B);
646 EXPECT_EQ(2, page_id_B);
647 EXPECT_NE(state_A, state_B);
648 render_thread_->sink().ClearMessages();
649
650 // Load page D, which will trigger an UpdateState message for page C.
651 LoadHTML("<div>Page D</div>");
652
653 // Check for a valid UpdateState for page C.
654 ProcessPendingMessages();
655 const IPC::Message* msg_C = render_thread_->sink().GetUniqueMessageMatching(
656 ViewHostMsg_UpdateState::ID);
657 ASSERT_TRUE(msg_C);
658 int page_id_C;
659 PageState state_C;
660 ViewHostMsg_UpdateState::Read(msg_C, &page_id_C, &state_C);
661 EXPECT_EQ(3, page_id_C);
662 EXPECT_NE(state_B, state_C);
663 render_thread_->sink().ClearMessages();
664
665 // Go back to C and commit, preparing for our real test.
666 ViewMsg_Navigate_Params params_C;
667 params_C.navigation_type = ViewMsg_Navigate_Type::NORMAL;
668 params_C.transition = PAGE_TRANSITION_FORWARD_BACK;
669 params_C.current_history_list_length = 4;
670 params_C.current_history_list_offset = 3;
671 params_C.pending_history_list_offset = 2;
672 params_C.page_id = 3;
673 params_C.page_state = state_C;
674 view()->OnNavigate(params_C);
675 ProcessPendingMessages();
676 render_thread_->sink().ClearMessages();
677
678 // Go back twice quickly, such that page B does not have a chance to commit.
679 // This leads to two changes to the back/forward list but only one change to
680 // the RenderView's page ID.
681
682 // Back to page B (page_id 2), without committing.
683 ViewMsg_Navigate_Params params_B;
684 params_B.navigation_type = ViewMsg_Navigate_Type::NORMAL;
685 params_B.transition = PAGE_TRANSITION_FORWARD_BACK;
686 params_B.current_history_list_length = 4;
687 params_B.current_history_list_offset = 2;
688 params_B.pending_history_list_offset = 1;
689 params_B.page_id = 2;
690 params_B.page_state = state_B;
691 view()->OnNavigate(params_B);
692
693 // Back to page A (page_id 1) and commit.
694 ViewMsg_Navigate_Params params;
695 params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
696 params.transition = PAGE_TRANSITION_FORWARD_BACK;
697 params_B.current_history_list_length = 4;
698 params_B.current_history_list_offset = 2;
699 params_B.pending_history_list_offset = 0;
700 params.page_id = 1;
701 params.page_state = state_A;
702 view()->OnNavigate(params);
703 ProcessPendingMessages();
704
705 // Now ensure that the UpdateState message we receive is consistent
706 // and represents page C in both page_id and state.
707 const IPC::Message* msg = render_thread_->sink().GetUniqueMessageMatching(
708 ViewHostMsg_UpdateState::ID);
709 ASSERT_TRUE(msg);
710 int page_id;
711 PageState state;
712 ViewHostMsg_UpdateState::Read(msg, &page_id, &state);
713 EXPECT_EQ(page_id_C, page_id);
714 EXPECT_NE(state_A, state);
715 EXPECT_NE(state_B, state);
716 EXPECT_EQ(state_C, state);
717 }
718
719 // Test that the history_page_ids_ list can reveal when a stale back/forward
720 // navigation arrives from the browser and can be ignored. See
721 // http://crbug.com/86758.
TEST_F(RenderViewImplTest,StaleNavigationsIgnored)722 TEST_F(RenderViewImplTest, StaleNavigationsIgnored) {
723 // Load page A.
724 LoadHTML("<div>Page A</div>");
725 EXPECT_EQ(1, view()->history_list_length_);
726 EXPECT_EQ(0, view()->history_list_offset_);
727 EXPECT_EQ(1, view()->history_page_ids_[0]);
728
729 // Load page B, which will trigger an UpdateState message for page A.
730 LoadHTML("<div>Page B</div>");
731 EXPECT_EQ(2, view()->history_list_length_);
732 EXPECT_EQ(1, view()->history_list_offset_);
733 EXPECT_EQ(2, view()->history_page_ids_[1]);
734
735 // Check for a valid UpdateState message for page A.
736 ProcessPendingMessages();
737 const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
738 ViewHostMsg_UpdateState::ID);
739 ASSERT_TRUE(msg_A);
740 int page_id_A;
741 PageState state_A;
742 ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A);
743 EXPECT_EQ(1, page_id_A);
744 render_thread_->sink().ClearMessages();
745
746 // Back to page A (page_id 1) and commit.
747 ViewMsg_Navigate_Params params_A;
748 params_A.navigation_type = ViewMsg_Navigate_Type::NORMAL;
749 params_A.transition = PAGE_TRANSITION_FORWARD_BACK;
750 params_A.current_history_list_length = 2;
751 params_A.current_history_list_offset = 1;
752 params_A.pending_history_list_offset = 0;
753 params_A.page_id = 1;
754 params_A.page_state = state_A;
755 view()->OnNavigate(params_A);
756 ProcessPendingMessages();
757
758 // A new navigation commits, clearing the forward history.
759 LoadHTML("<div>Page C</div>");
760 EXPECT_EQ(2, view()->history_list_length_);
761 EXPECT_EQ(1, view()->history_list_offset_);
762 EXPECT_EQ(3, view()->history_page_ids_[1]);
763
764 // The browser then sends a stale navigation to B, which should be ignored.
765 ViewMsg_Navigate_Params params_B;
766 params_B.navigation_type = ViewMsg_Navigate_Type::NORMAL;
767 params_B.transition = PAGE_TRANSITION_FORWARD_BACK;
768 params_B.current_history_list_length = 2;
769 params_B.current_history_list_offset = 0;
770 params_B.pending_history_list_offset = 1;
771 params_B.page_id = 2;
772 params_B.page_state = state_A; // Doesn't matter, just has to be present.
773 view()->OnNavigate(params_B);
774
775 // State should be unchanged.
776 EXPECT_EQ(2, view()->history_list_length_);
777 EXPECT_EQ(1, view()->history_list_offset_);
778 EXPECT_EQ(3, view()->history_page_ids_[1]);
779 }
780
781 // Test that we do not ignore navigations after the entry limit is reached,
782 // in which case the browser starts dropping entries from the front. In this
783 // case, we'll see a page_id mismatch but the RenderView's id will be older,
784 // not newer, than params.page_id. Use this as a cue that we should update the
785 // state and not treat it like a navigation to a cropped forward history item.
786 // See http://crbug.com/89798.
TEST_F(RenderViewImplTest,DontIgnoreBackAfterNavEntryLimit)787 TEST_F(RenderViewImplTest, DontIgnoreBackAfterNavEntryLimit) {
788 // Load page A.
789 LoadHTML("<div>Page A</div>");
790 EXPECT_EQ(1, view()->history_list_length_);
791 EXPECT_EQ(0, view()->history_list_offset_);
792 EXPECT_EQ(1, view()->history_page_ids_[0]);
793
794 // Load page B, which will trigger an UpdateState message for page A.
795 LoadHTML("<div>Page B</div>");
796 EXPECT_EQ(2, view()->history_list_length_);
797 EXPECT_EQ(1, view()->history_list_offset_);
798 EXPECT_EQ(2, view()->history_page_ids_[1]);
799
800 // Check for a valid UpdateState message for page A.
801 ProcessPendingMessages();
802 const IPC::Message* msg_A = render_thread_->sink().GetUniqueMessageMatching(
803 ViewHostMsg_UpdateState::ID);
804 ASSERT_TRUE(msg_A);
805 int page_id_A;
806 PageState state_A;
807 ViewHostMsg_UpdateState::Read(msg_A, &page_id_A, &state_A);
808 EXPECT_EQ(1, page_id_A);
809 render_thread_->sink().ClearMessages();
810
811 // Load page C, which will trigger an UpdateState message for page B.
812 LoadHTML("<div>Page C</div>");
813 EXPECT_EQ(3, view()->history_list_length_);
814 EXPECT_EQ(2, view()->history_list_offset_);
815 EXPECT_EQ(3, view()->history_page_ids_[2]);
816
817 // Check for a valid UpdateState message for page B.
818 ProcessPendingMessages();
819 const IPC::Message* msg_B = render_thread_->sink().GetUniqueMessageMatching(
820 ViewHostMsg_UpdateState::ID);
821 ASSERT_TRUE(msg_B);
822 int page_id_B;
823 PageState state_B;
824 ViewHostMsg_UpdateState::Read(msg_B, &page_id_B, &state_B);
825 EXPECT_EQ(2, page_id_B);
826 render_thread_->sink().ClearMessages();
827
828 // Suppose the browser has limited the number of NavigationEntries to 2.
829 // It has now dropped the first entry, but the renderer isn't notified.
830 // Ensure that going back to page B (page_id 2) at offset 0 is successful.
831 ViewMsg_Navigate_Params params_B;
832 params_B.navigation_type = ViewMsg_Navigate_Type::NORMAL;
833 params_B.transition = PAGE_TRANSITION_FORWARD_BACK;
834 params_B.current_history_list_length = 2;
835 params_B.current_history_list_offset = 1;
836 params_B.pending_history_list_offset = 0;
837 params_B.page_id = 2;
838 params_B.page_state = state_B;
839 view()->OnNavigate(params_B);
840 ProcessPendingMessages();
841
842 EXPECT_EQ(2, view()->history_list_length_);
843 EXPECT_EQ(0, view()->history_list_offset_);
844 EXPECT_EQ(2, view()->history_page_ids_[0]);
845 }
846
847 // Test that our IME backend sends a notification message when the input focus
848 // changes.
TEST_F(RenderViewImplTest,OnImeTypeChanged)849 TEST_F(RenderViewImplTest, OnImeTypeChanged) {
850 // Enable our IME backend code.
851 view()->OnSetInputMethodActive(true);
852
853 // Load an HTML page consisting of two input fields.
854 view()->set_send_content_state_immediately(true);
855 LoadHTML("<html>"
856 "<head>"
857 "</head>"
858 "<body>"
859 "<input id=\"test1\" type=\"text\" value=\"some text\"></input>"
860 "<input id=\"test2\" type=\"password\"></input>"
861 "<input id=\"test3\" type=\"text\" inputmode=\"verbatim\"></input>"
862 "<input id=\"test4\" type=\"text\" inputmode=\"latin\"></input>"
863 "<input id=\"test5\" type=\"text\" inputmode=\"latin-name\"></input>"
864 "<input id=\"test6\" type=\"text\" inputmode=\"latin-prose\">"
865 "</input>"
866 "<input id=\"test7\" type=\"text\" inputmode=\"full-width-latin\">"
867 "</input>"
868 "<input id=\"test8\" type=\"text\" inputmode=\"kana\"></input>"
869 "<input id=\"test9\" type=\"text\" inputmode=\"katakana\"></input>"
870 "<input id=\"test10\" type=\"text\" inputmode=\"numeric\"></input>"
871 "<input id=\"test11\" type=\"text\" inputmode=\"tel\"></input>"
872 "<input id=\"test12\" type=\"text\" inputmode=\"email\"></input>"
873 "<input id=\"test13\" type=\"text\" inputmode=\"url\"></input>"
874 "<input id=\"test14\" type=\"text\" inputmode=\"unknown\"></input>"
875 "<input id=\"test15\" type=\"text\" inputmode=\"verbatim\"></input>"
876 "</body>"
877 "</html>");
878 render_thread_->sink().ClearMessages();
879
880 struct InputModeTestCase {
881 const char* input_id;
882 ui::TextInputMode expected_mode;
883 };
884 static const InputModeTestCase kInputModeTestCases[] = {
885 {"test1", ui::TEXT_INPUT_MODE_DEFAULT},
886 {"test3", ui::TEXT_INPUT_MODE_VERBATIM},
887 {"test4", ui::TEXT_INPUT_MODE_LATIN},
888 {"test5", ui::TEXT_INPUT_MODE_LATIN_NAME},
889 {"test6", ui::TEXT_INPUT_MODE_LATIN_PROSE},
890 {"test7", ui::TEXT_INPUT_MODE_FULL_WIDTH_LATIN},
891 {"test8", ui::TEXT_INPUT_MODE_KANA},
892 {"test9", ui::TEXT_INPUT_MODE_KATAKANA},
893 {"test10", ui::TEXT_INPUT_MODE_NUMERIC},
894 {"test11", ui::TEXT_INPUT_MODE_TEL},
895 {"test12", ui::TEXT_INPUT_MODE_EMAIL},
896 {"test13", ui::TEXT_INPUT_MODE_URL},
897 {"test14", ui::TEXT_INPUT_MODE_DEFAULT},
898 {"test15", ui::TEXT_INPUT_MODE_VERBATIM},
899 };
900
901 const int kRepeatCount = 10;
902 for (int i = 0; i < kRepeatCount; i++) {
903 // Move the input focus to the first <input> element, where we should
904 // activate IMEs.
905 ExecuteJavaScript("document.getElementById('test1').focus();");
906 ProcessPendingMessages();
907 render_thread_->sink().ClearMessages();
908
909 // Update the IME status and verify if our IME backend sends an IPC message
910 // to activate IMEs.
911 view()->UpdateTextInputType();
912 const IPC::Message* msg = render_thread_->sink().GetMessageAt(0);
913 EXPECT_TRUE(msg != NULL);
914 EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type());
915 ui::TextInputType type;
916 bool can_compose_inline = false;
917 ui::TextInputMode input_mode = ui::TEXT_INPUT_MODE_DEFAULT;
918 ViewHostMsg_TextInputTypeChanged::Read(msg,
919 &type,
920 &input_mode,
921 &can_compose_inline);
922 EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, type);
923 EXPECT_EQ(true, can_compose_inline);
924
925 // Move the input focus to the second <input> element, where we should
926 // de-activate IMEs.
927 ExecuteJavaScript("document.getElementById('test2').focus();");
928 ProcessPendingMessages();
929 render_thread_->sink().ClearMessages();
930
931 // Update the IME status and verify if our IME backend sends an IPC message
932 // to de-activate IMEs.
933 view()->UpdateTextInputType();
934 msg = render_thread_->sink().GetMessageAt(0);
935 EXPECT_TRUE(msg != NULL);
936 EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type());
937 ViewHostMsg_TextInputTypeChanged::Read(msg,
938 &type,
939 &input_mode,
940 &can_compose_inline);
941 EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, type);
942
943 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kInputModeTestCases); i++) {
944 const InputModeTestCase* test_case = &kInputModeTestCases[i];
945 std::string javascript =
946 base::StringPrintf("document.getElementById('%s').focus();",
947 test_case->input_id);
948 // Move the input focus to the target <input> element, where we should
949 // activate IMEs.
950 ExecuteJavaScriptAndReturnIntValue(ASCIIToUTF16(javascript), NULL);
951 ProcessPendingMessages();
952 render_thread_->sink().ClearMessages();
953
954 // Update the IME status and verify if our IME backend sends an IPC
955 // message to activate IMEs.
956 view()->UpdateTextInputType();
957 const IPC::Message* msg = render_thread_->sink().GetMessageAt(0);
958 EXPECT_TRUE(msg != NULL);
959 EXPECT_EQ(ViewHostMsg_TextInputTypeChanged::ID, msg->type());
960 ViewHostMsg_TextInputTypeChanged::Read(msg,
961 &type,
962 &input_mode,
963 &can_compose_inline);
964 EXPECT_EQ(test_case->expected_mode, input_mode);
965 }
966 }
967 }
968
969 // Test that our IME backend can compose CJK words.
970 // Our IME front-end sends many platform-independent messages to the IME backend
971 // while it composes CJK words. This test sends the minimal messages captured
972 // on my local environment directly to the IME backend to verify if the backend
973 // can compose CJK words without any problems.
974 // This test uses an array of command sets because an IME composotion does not
975 // only depends on IME events, but also depends on window events, e.g. moving
976 // the window focus while composing a CJK text. To handle such complicated
977 // cases, this test should not only call IME-related functions in the
978 // RenderWidget class, but also call some RenderWidget members, e.g.
979 // ExecuteJavaScript(), RenderWidget::OnSetFocus(), etc.
TEST_F(RenderViewImplTest,ImeComposition)980 TEST_F(RenderViewImplTest, ImeComposition) {
981 enum ImeCommand {
982 IME_INITIALIZE,
983 IME_SETINPUTMODE,
984 IME_SETFOCUS,
985 IME_SETCOMPOSITION,
986 IME_CONFIRMCOMPOSITION,
987 IME_CANCELCOMPOSITION
988 };
989 struct ImeMessage {
990 ImeCommand command;
991 bool enable;
992 int selection_start;
993 int selection_end;
994 const wchar_t* ime_string;
995 const wchar_t* result;
996 };
997 static const ImeMessage kImeMessages[] = {
998 // Scenario 1: input a Chinese word with Microsoft IME (on Vista).
999 {IME_INITIALIZE, true, 0, 0, NULL, NULL},
1000 {IME_SETINPUTMODE, true, 0, 0, NULL, NULL},
1001 {IME_SETFOCUS, true, 0, 0, NULL, NULL},
1002 {IME_SETCOMPOSITION, false, 1, 1, L"n", L"n"},
1003 {IME_SETCOMPOSITION, false, 2, 2, L"ni", L"ni"},
1004 {IME_SETCOMPOSITION, false, 3, 3, L"nih", L"nih"},
1005 {IME_SETCOMPOSITION, false, 4, 4, L"niha", L"niha"},
1006 {IME_SETCOMPOSITION, false, 5, 5, L"nihao", L"nihao"},
1007 {IME_CONFIRMCOMPOSITION, false, -1, -1, L"\x4F60\x597D", L"\x4F60\x597D"},
1008 // Scenario 2: input a Japanese word with Microsoft IME (on Vista).
1009 {IME_INITIALIZE, true, 0, 0, NULL, NULL},
1010 {IME_SETINPUTMODE, true, 0, 0, NULL, NULL},
1011 {IME_SETFOCUS, true, 0, 0, NULL, NULL},
1012 {IME_SETCOMPOSITION, false, 0, 1, L"\xFF4B", L"\xFF4B"},
1013 {IME_SETCOMPOSITION, false, 0, 1, L"\x304B", L"\x304B"},
1014 {IME_SETCOMPOSITION, false, 0, 2, L"\x304B\xFF4E", L"\x304B\xFF4E"},
1015 {IME_SETCOMPOSITION, false, 0, 3, L"\x304B\x3093\xFF4A",
1016 L"\x304B\x3093\xFF4A"},
1017 {IME_SETCOMPOSITION, false, 0, 3, L"\x304B\x3093\x3058",
1018 L"\x304B\x3093\x3058"},
1019 {IME_SETCOMPOSITION, false, 0, 2, L"\x611F\x3058", L"\x611F\x3058"},
1020 {IME_SETCOMPOSITION, false, 0, 2, L"\x6F22\x5B57", L"\x6F22\x5B57"},
1021 {IME_CONFIRMCOMPOSITION, false, -1, -1, L"", L"\x6F22\x5B57"},
1022 {IME_CANCELCOMPOSITION, false, -1, -1, L"", L"\x6F22\x5B57"},
1023 // Scenario 3: input a Korean word with Microsot IME (on Vista).
1024 {IME_INITIALIZE, true, 0, 0, NULL, NULL},
1025 {IME_SETINPUTMODE, true, 0, 0, NULL, NULL},
1026 {IME_SETFOCUS, true, 0, 0, NULL, NULL},
1027 {IME_SETCOMPOSITION, false, 0, 1, L"\x3147", L"\x3147"},
1028 {IME_SETCOMPOSITION, false, 0, 1, L"\xC544", L"\xC544"},
1029 {IME_SETCOMPOSITION, false, 0, 1, L"\xC548", L"\xC548"},
1030 {IME_CONFIRMCOMPOSITION, false, -1, -1, L"", L"\xC548"},
1031 {IME_SETCOMPOSITION, false, 0, 1, L"\x3134", L"\xC548\x3134"},
1032 {IME_SETCOMPOSITION, false, 0, 1, L"\xB140", L"\xC548\xB140"},
1033 {IME_SETCOMPOSITION, false, 0, 1, L"\xB155", L"\xC548\xB155"},
1034 {IME_CANCELCOMPOSITION, false, -1, -1, L"", L"\xC548"},
1035 {IME_SETCOMPOSITION, false, 0, 1, L"\xB155", L"\xC548\xB155"},
1036 {IME_CONFIRMCOMPOSITION, false, -1, -1, L"", L"\xC548\xB155"},
1037 };
1038
1039 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kImeMessages); i++) {
1040 const ImeMessage* ime_message = &kImeMessages[i];
1041 switch (ime_message->command) {
1042 case IME_INITIALIZE:
1043 // Load an HTML page consisting of a content-editable <div> element,
1044 // and move the input focus to the <div> element, where we can use
1045 // IMEs.
1046 view()->OnSetInputMethodActive(ime_message->enable);
1047 view()->set_send_content_state_immediately(true);
1048 LoadHTML("<html>"
1049 "<head>"
1050 "</head>"
1051 "<body>"
1052 "<div id=\"test1\" contenteditable=\"true\"></div>"
1053 "</body>"
1054 "</html>");
1055 ExecuteJavaScript("document.getElementById('test1').focus();");
1056 break;
1057
1058 case IME_SETINPUTMODE:
1059 // Activate (or deactivate) our IME back-end.
1060 view()->OnSetInputMethodActive(ime_message->enable);
1061 break;
1062
1063 case IME_SETFOCUS:
1064 // Update the window focus.
1065 view()->OnSetFocus(ime_message->enable);
1066 break;
1067
1068 case IME_SETCOMPOSITION:
1069 view()->OnImeSetComposition(
1070 WideToUTF16Hack(ime_message->ime_string),
1071 std::vector<blink::WebCompositionUnderline>(),
1072 ime_message->selection_start,
1073 ime_message->selection_end);
1074 break;
1075
1076 case IME_CONFIRMCOMPOSITION:
1077 view()->OnImeConfirmComposition(
1078 WideToUTF16Hack(ime_message->ime_string),
1079 gfx::Range::InvalidRange(),
1080 false);
1081 break;
1082
1083 case IME_CANCELCOMPOSITION:
1084 view()->OnImeSetComposition(
1085 base::string16(),
1086 std::vector<blink::WebCompositionUnderline>(),
1087 0, 0);
1088 break;
1089 }
1090
1091 // Update the status of our IME back-end.
1092 // TODO(hbono): we should verify messages to be sent from the back-end.
1093 view()->UpdateTextInputType();
1094 ProcessPendingMessages();
1095 render_thread_->sink().ClearMessages();
1096
1097 if (ime_message->result) {
1098 // Retrieve the content of this page and compare it with the expected
1099 // result.
1100 const int kMaxOutputCharacters = 128;
1101 std::wstring output = UTF16ToWideHack(
1102 GetMainFrame()->contentAsText(kMaxOutputCharacters));
1103 EXPECT_EQ(output, ime_message->result);
1104 }
1105 }
1106 }
1107
1108 // Test that the RenderView::OnSetTextDirection() function can change the text
1109 // direction of the selected input element.
TEST_F(RenderViewImplTest,OnSetTextDirection)1110 TEST_F(RenderViewImplTest, OnSetTextDirection) {
1111 // Load an HTML page consisting of a <textarea> element and a <div> element.
1112 // This test changes the text direction of the <textarea> element, and
1113 // writes the values of its 'dir' attribute and its 'direction' property to
1114 // verify that the text direction is changed.
1115 view()->set_send_content_state_immediately(true);
1116 LoadHTML("<html>"
1117 "<head>"
1118 "</head>"
1119 "<body>"
1120 "<textarea id=\"test\"></textarea>"
1121 "<div id=\"result\" contenteditable=\"true\"></div>"
1122 "</body>"
1123 "</html>");
1124 render_thread_->sink().ClearMessages();
1125
1126 static const struct {
1127 WebTextDirection direction;
1128 const wchar_t* expected_result;
1129 } kTextDirection[] = {
1130 { blink::WebTextDirectionRightToLeft, L"\x000A" L"rtl,rtl" },
1131 { blink::WebTextDirectionLeftToRight, L"\x000A" L"ltr,ltr" },
1132 };
1133 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTextDirection); ++i) {
1134 // Set the text direction of the <textarea> element.
1135 ExecuteJavaScript("document.getElementById('test').focus();");
1136 view()->OnSetTextDirection(kTextDirection[i].direction);
1137
1138 // Write the values of its DOM 'dir' attribute and its CSS 'direction'
1139 // property to the <div> element.
1140 ExecuteJavaScript("var result = document.getElementById('result');"
1141 "var node = document.getElementById('test');"
1142 "var style = getComputedStyle(node, null);"
1143 "result.innerText ="
1144 " node.getAttribute('dir') + ',' +"
1145 " style.getPropertyValue('direction');");
1146
1147 // Copy the document content to std::wstring and compare with the
1148 // expected result.
1149 const int kMaxOutputCharacters = 16;
1150 std::wstring output = UTF16ToWideHack(
1151 GetMainFrame()->contentAsText(kMaxOutputCharacters));
1152 EXPECT_EQ(output, kTextDirection[i].expected_result);
1153 }
1154 }
1155
1156 // see http://crbug.com/238750
1157 #if defined(OS_WIN)
1158 #define MAYBE_OnHandleKeyboardEvent DISABLED_OnHandleKeyboardEvent
1159 #else
1160 #define MAYBE_OnHandleKeyboardEvent OnHandleKeyboardEvent
1161 #endif
1162
1163 // Test that we can receive correct DOM events when we send input events
1164 // through the RenderWidget::OnHandleInputEvent() function.
TEST_F(RenderViewImplTest,MAYBE_OnHandleKeyboardEvent)1165 TEST_F(RenderViewImplTest, MAYBE_OnHandleKeyboardEvent) {
1166 #if !defined(OS_MACOSX)
1167 // Load an HTML page consisting of one <input> element and three
1168 // contentediable <div> elements.
1169 // The <input> element is used for sending keyboard events, and the <div>
1170 // elements are used for writing DOM events in the following format:
1171 // "<keyCode>,<shiftKey>,<controlKey>,<altKey>".
1172 // TODO(hbono): <http://crbug.com/2215> Our WebKit port set |ev.metaKey| to
1173 // true when pressing an alt key, i.e. the |ev.metaKey| value is not
1174 // trustworthy. We will check the |ev.metaKey| value when this issue is fixed.
1175 view()->set_send_content_state_immediately(true);
1176 LoadHTML("<html>"
1177 "<head>"
1178 "<title></title>"
1179 "<script type='text/javascript' language='javascript'>"
1180 "function OnKeyEvent(ev) {"
1181 " var result = document.getElementById(ev.type);"
1182 " result.innerText ="
1183 " (ev.which || ev.keyCode) + ',' +"
1184 " ev.shiftKey + ',' +"
1185 " ev.ctrlKey + ',' +"
1186 " ev.altKey;"
1187 " return true;"
1188 "}"
1189 "</script>"
1190 "</head>"
1191 "<body>"
1192 "<input id='test' type='text'"
1193 " onkeydown='return OnKeyEvent(event);'"
1194 " onkeypress='return OnKeyEvent(event);'"
1195 " onkeyup='return OnKeyEvent(event);'>"
1196 "</input>"
1197 "<div id='keydown' contenteditable='true'>"
1198 "</div>"
1199 "<div id='keypress' contenteditable='true'>"
1200 "</div>"
1201 "<div id='keyup' contenteditable='true'>"
1202 "</div>"
1203 "</body>"
1204 "</html>");
1205 ExecuteJavaScript("document.getElementById('test').focus();");
1206 render_thread_->sink().ClearMessages();
1207
1208 static const MockKeyboard::Layout kLayouts[] = {
1209 #if defined(OS_WIN)
1210 // Since we ignore the mock keyboard layout on Linux and instead just use
1211 // the screen's keyboard layout, these trivially pass. They are commented
1212 // out to avoid the illusion that they work.
1213 MockKeyboard::LAYOUT_ARABIC,
1214 MockKeyboard::LAYOUT_CANADIAN_FRENCH,
1215 MockKeyboard::LAYOUT_FRENCH,
1216 MockKeyboard::LAYOUT_HEBREW,
1217 MockKeyboard::LAYOUT_RUSSIAN,
1218 #endif
1219 MockKeyboard::LAYOUT_UNITED_STATES,
1220 };
1221
1222 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kLayouts); ++i) {
1223 // For each key code, we send three keyboard events.
1224 // * we press only the key;
1225 // * we press the key and a left-shift key, and;
1226 // * we press the key and a right-alt (AltGr) key.
1227 // For each modifiers, we need a string used for formatting its expected
1228 // result. (See the above comment for its format.)
1229 static const struct {
1230 MockKeyboard::Modifiers modifiers;
1231 const char* expected_result;
1232 } kModifierData[] = {
1233 {MockKeyboard::NONE, "false,false,false"},
1234 {MockKeyboard::LEFT_SHIFT, "true,false,false"},
1235 #if defined(OS_WIN)
1236 {MockKeyboard::RIGHT_ALT, "false,false,true"},
1237 #endif
1238 };
1239
1240 MockKeyboard::Layout layout = kLayouts[i];
1241 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(kModifierData); ++j) {
1242 // Virtual key codes used for this test.
1243 static const int kKeyCodes[] = {
1244 '0', '1', '2', '3', '4', '5', '6', '7',
1245 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
1246 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
1247 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
1248 'W', 'X', 'Y', 'Z',
1249 ui::VKEY_OEM_1,
1250 ui::VKEY_OEM_PLUS,
1251 ui::VKEY_OEM_COMMA,
1252 ui::VKEY_OEM_MINUS,
1253 ui::VKEY_OEM_PERIOD,
1254 ui::VKEY_OEM_2,
1255 ui::VKEY_OEM_3,
1256 ui::VKEY_OEM_4,
1257 ui::VKEY_OEM_5,
1258 ui::VKEY_OEM_6,
1259 ui::VKEY_OEM_7,
1260 #if defined(OS_WIN)
1261 // Not sure how to handle this key on Linux.
1262 ui::VKEY_OEM_8,
1263 #endif
1264 };
1265
1266 MockKeyboard::Modifiers modifiers = kModifierData[j].modifiers;
1267 for (size_t k = 0; k < ARRAYSIZE_UNSAFE(kKeyCodes); ++k) {
1268 // Send a keyboard event to the RenderView object.
1269 // We should test a keyboard event only when the given keyboard-layout
1270 // driver is installed in a PC and the driver can assign a Unicode
1271 // charcter for the given tuple (key-code and modifiers).
1272 int key_code = kKeyCodes[k];
1273 base::string16 char_code;
1274 if (SendKeyEvent(layout, key_code, modifiers, &char_code) < 0)
1275 continue;
1276
1277 // Create an expected result from the virtual-key code, the character
1278 // code, and the modifier-key status.
1279 // We format a string that emulates a DOM-event string produced hy
1280 // our JavaScript function. (See the above comment for the format.)
1281 static char expected_result[1024];
1282 expected_result[0] = 0;
1283 base::snprintf(&expected_result[0],
1284 sizeof(expected_result),
1285 "\n" // texts in the <input> element
1286 "%d,%s\n" // texts in the first <div> element
1287 "%d,%s\n" // texts in the second <div> element
1288 "%d,%s", // texts in the third <div> element
1289 key_code, kModifierData[j].expected_result,
1290 static_cast<int>(char_code[0]),
1291 kModifierData[j].expected_result,
1292 key_code, kModifierData[j].expected_result);
1293
1294 // Retrieve the text in the test page and compare it with the expected
1295 // text created from a virtual-key code, a character code, and the
1296 // modifier-key status.
1297 const int kMaxOutputCharacters = 1024;
1298 std::string output = UTF16ToUTF8(
1299 GetMainFrame()->contentAsText(kMaxOutputCharacters));
1300 EXPECT_EQ(expected_result, output);
1301 }
1302 }
1303 }
1304 #else
1305 NOTIMPLEMENTED();
1306 #endif
1307 }
1308
1309 // Test that our EditorClientImpl class can insert characters when we send
1310 // keyboard events through the RenderWidget::OnHandleInputEvent() function.
1311 // This test is for preventing regressions caused only when we use non-US
1312 // keyboards, such as Issue 10846.
1313 // see http://crbug.com/244562
1314 #if defined(OS_WIN)
1315 #define MAYBE_InsertCharacters DISABLED_InsertCharacters
1316 #else
1317 #define MAYBE_InsertCharacters InsertCharacters
1318 #endif
TEST_F(RenderViewImplTest,MAYBE_InsertCharacters)1319 TEST_F(RenderViewImplTest, MAYBE_InsertCharacters) {
1320 #if !defined(OS_MACOSX)
1321 static const struct {
1322 MockKeyboard::Layout layout;
1323 const wchar_t* expected_result;
1324 } kLayouts[] = {
1325 #if 0
1326 // Disabled these keyboard layouts because buildbots do not have their
1327 // keyboard-layout drivers installed.
1328 {MockKeyboard::LAYOUT_ARABIC,
1329 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1330 L"\x0038\x0039\x0634\x0624\x064a\x062b\x0628\x0644"
1331 L"\x0627\x0647\x062a\x0646\x0645\x0629\x0649\x062e"
1332 L"\x062d\x0636\x0642\x0633\x0641\x0639\x0631\x0635"
1333 L"\x0621\x063a\x0626\x0643\x003d\x0648\x002d\x0632"
1334 L"\x0638\x0630\x062c\x005c\x062f\x0637\x0028\x0021"
1335 L"\x0040\x0023\x0024\x0025\x005e\x0026\x002a\x0029"
1336 L"\x0650\x007d\x005d\x064f\x005b\x0623\x00f7\x0640"
1337 L"\x060c\x002f\x2019\x0622\x00d7\x061b\x064e\x064c"
1338 L"\x064d\x2018\x007b\x064b\x0652\x0625\x007e\x003a"
1339 L"\x002b\x002c\x005f\x002e\x061f\x0651\x003c\x007c"
1340 L"\x003e\x0022\x0030\x0031\x0032\x0033\x0034\x0035"
1341 L"\x0036\x0037\x0038\x0039\x0634\x0624\x064a\x062b"
1342 L"\x0628\x0644\x0627\x0647\x062a\x0646\x0645\x0629"
1343 L"\x0649\x062e\x062d\x0636\x0642\x0633\x0641\x0639"
1344 L"\x0631\x0635\x0621\x063a\x0626\x0643\x003d\x0648"
1345 L"\x002d\x0632\x0638\x0630\x062c\x005c\x062f\x0637"
1346 },
1347 {MockKeyboard::LAYOUT_HEBREW,
1348 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1349 L"\x0038\x0039\x05e9\x05e0\x05d1\x05d2\x05e7\x05db"
1350 L"\x05e2\x05d9\x05df\x05d7\x05dc\x05da\x05e6\x05de"
1351 L"\x05dd\x05e4\x002f\x05e8\x05d3\x05d0\x05d5\x05d4"
1352 L"\x0027\x05e1\x05d8\x05d6\x05e3\x003d\x05ea\x002d"
1353 L"\x05e5\x002e\x003b\x005d\x005c\x005b\x002c\x0028"
1354 L"\x0021\x0040\x0023\x0024\x0025\x005e\x0026\x002a"
1355 L"\x0029\x0041\x0042\x0043\x0044\x0045\x0046\x0047"
1356 L"\x0048\x0049\x004a\x004b\x004c\x004d\x004e\x004f"
1357 L"\x0050\x0051\x0052\x0053\x0054\x0055\x0056\x0057"
1358 L"\x0058\x0059\x005a\x003a\x002b\x003e\x005f\x003c"
1359 L"\x003f\x007e\x007d\x007c\x007b\x0022\x0030\x0031"
1360 L"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039"
1361 L"\x05e9\x05e0\x05d1\x05d2\x05e7\x05db\x05e2\x05d9"
1362 L"\x05df\x05d7\x05dc\x05da\x05e6\x05de\x05dd\x05e4"
1363 L"\x002f\x05e8\x05d3\x05d0\x05d5\x05d4\x0027\x05e1"
1364 L"\x05d8\x05d6\x05e3\x003d\x05ea\x002d\x05e5\x002e"
1365 L"\x003b\x005d\x005c\x005b\x002c"
1366 },
1367 #endif
1368 #if defined(OS_WIN)
1369 // On Linux, the only way to test alternate keyboard layouts is to change
1370 // the keyboard layout of the whole screen. I'm worried about the side
1371 // effects this may have on the buildbots.
1372 {MockKeyboard::LAYOUT_CANADIAN_FRENCH,
1373 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1374 L"\x0038\x0039\x0061\x0062\x0063\x0064\x0065\x0066"
1375 L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e"
1376 L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076"
1377 L"\x0077\x0078\x0079\x007a\x003b\x003d\x002c\x002d"
1378 L"\x002e\x00e9\x003c\x0029\x0021\x0022\x002f\x0024"
1379 L"\x0025\x003f\x0026\x002a\x0028\x0041\x0042\x0043"
1380 L"\x0044\x0045\x0046\x0047\x0048\x0049\x004a\x004b"
1381 L"\x004c\x004d\x004e\x004f\x0050\x0051\x0052\x0053"
1382 L"\x0054\x0055\x0056\x0057\x0058\x0059\x005a\x003a"
1383 L"\x002b\x0027\x005f\x002e\x00c9\x003e\x0030\x0031"
1384 L"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039"
1385 L"\x0061\x0062\x0063\x0064\x0065\x0066\x0067\x0068"
1386 L"\x0069\x006a\x006b\x006c\x006d\x006e\x006f\x0070"
1387 L"\x0071\x0072\x0073\x0074\x0075\x0076\x0077\x0078"
1388 L"\x0079\x007a\x003b\x003d\x002c\x002d\x002e\x00e9"
1389 L"\x003c"
1390 },
1391 {MockKeyboard::LAYOUT_FRENCH,
1392 L"\x00e0\x0026\x00e9\x0022\x0027\x0028\x002d\x00e8"
1393 L"\x005f\x00e7\x0061\x0062\x0063\x0064\x0065\x0066"
1394 L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e"
1395 L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076"
1396 L"\x0077\x0078\x0079\x007a\x0024\x003d\x002c\x003b"
1397 L"\x003a\x00f9\x0029\x002a\x0021\x0030\x0031\x0032"
1398 L"\x0033\x0034\x0035\x0036\x0037\x0038\x0039\x0041"
1399 L"\x0042\x0043\x0044\x0045\x0046\x0047\x0048\x0049"
1400 L"\x004a\x004b\x004c\x004d\x004e\x004f\x0050\x0051"
1401 L"\x0052\x0053\x0054\x0055\x0056\x0057\x0058\x0059"
1402 L"\x005a\x00a3\x002b\x003f\x002e\x002f\x0025\x00b0"
1403 L"\x00b5\x00e0\x0026\x00e9\x0022\x0027\x0028\x002d"
1404 L"\x00e8\x005f\x00e7\x0061\x0062\x0063\x0064\x0065"
1405 L"\x0066\x0067\x0068\x0069\x006a\x006b\x006c\x006d"
1406 L"\x006e\x006f\x0070\x0071\x0072\x0073\x0074\x0075"
1407 L"\x0076\x0077\x0078\x0079\x007a\x0024\x003d\x002c"
1408 L"\x003b\x003a\x00f9\x0029\x002a\x0021"
1409 },
1410 {MockKeyboard::LAYOUT_RUSSIAN,
1411 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1412 L"\x0038\x0039\x0444\x0438\x0441\x0432\x0443\x0430"
1413 L"\x043f\x0440\x0448\x043e\x043b\x0434\x044c\x0442"
1414 L"\x0449\x0437\x0439\x043a\x044b\x0435\x0433\x043c"
1415 L"\x0446\x0447\x043d\x044f\x0436\x003d\x0431\x002d"
1416 L"\x044e\x002e\x0451\x0445\x005c\x044a\x044d\x0029"
1417 L"\x0021\x0022\x2116\x003b\x0025\x003a\x003f\x002a"
1418 L"\x0028\x0424\x0418\x0421\x0412\x0423\x0410\x041f"
1419 L"\x0420\x0428\x041e\x041b\x0414\x042c\x0422\x0429"
1420 L"\x0417\x0419\x041a\x042b\x0415\x0413\x041c\x0426"
1421 L"\x0427\x041d\x042f\x0416\x002b\x0411\x005f\x042e"
1422 L"\x002c\x0401\x0425\x002f\x042a\x042d\x0030\x0031"
1423 L"\x0032\x0033\x0034\x0035\x0036\x0037\x0038\x0039"
1424 L"\x0444\x0438\x0441\x0432\x0443\x0430\x043f\x0440"
1425 L"\x0448\x043e\x043b\x0434\x044c\x0442\x0449\x0437"
1426 L"\x0439\x043a\x044b\x0435\x0433\x043c\x0446\x0447"
1427 L"\x043d\x044f\x0436\x003d\x0431\x002d\x044e\x002e"
1428 L"\x0451\x0445\x005c\x044a\x044d"
1429 },
1430 #endif // defined(OS_WIN)
1431 {MockKeyboard::LAYOUT_UNITED_STATES,
1432 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1433 L"\x0038\x0039\x0061\x0062\x0063\x0064\x0065\x0066"
1434 L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e"
1435 L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076"
1436 L"\x0077\x0078\x0079\x007a\x003b\x003d\x002c\x002d"
1437 L"\x002e\x002f\x0060\x005b\x005c\x005d\x0027\x0029"
1438 L"\x0021\x0040\x0023\x0024\x0025\x005e\x0026\x002a"
1439 L"\x0028\x0041\x0042\x0043\x0044\x0045\x0046\x0047"
1440 L"\x0048\x0049\x004a\x004b\x004c\x004d\x004e\x004f"
1441 L"\x0050\x0051\x0052\x0053\x0054\x0055\x0056\x0057"
1442 L"\x0058\x0059\x005a\x003a\x002b\x003c\x005f\x003e"
1443 L"\x003f\x007e\x007b\x007c\x007d\x0022"
1444 #if defined(OS_WIN)
1445 // This is ifdefed out for Linux to correspond to the fact that we don't
1446 // test alt+keystroke for now.
1447 L"\x0030\x0031\x0032\x0033\x0034\x0035\x0036\x0037"
1448 L"\x0038\x0039\x0061\x0062\x0063\x0064\x0065\x0066"
1449 L"\x0067\x0068\x0069\x006a\x006b\x006c\x006d\x006e"
1450 L"\x006f\x0070\x0071\x0072\x0073\x0074\x0075\x0076"
1451 L"\x0077\x0078\x0079\x007a\x003b\x003d\x002c\x002d"
1452 L"\x002e\x002f\x0060\x005b\x005c\x005d\x0027"
1453 #endif
1454 },
1455 };
1456
1457 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kLayouts); ++i) {
1458 // Load an HTML page consisting of one <div> element.
1459 // This <div> element is used by the EditorClientImpl class to insert
1460 // characters received through the RenderWidget::OnHandleInputEvent()
1461 // function.
1462 view()->set_send_content_state_immediately(true);
1463 LoadHTML("<html>"
1464 "<head>"
1465 "<title></title>"
1466 "</head>"
1467 "<body>"
1468 "<div id='test' contenteditable='true'>"
1469 "</div>"
1470 "</body>"
1471 "</html>");
1472 ExecuteJavaScript("document.getElementById('test').focus();");
1473 render_thread_->sink().ClearMessages();
1474
1475 // For each key code, we send three keyboard events.
1476 // * Pressing only the key;
1477 // * Pressing the key and a left-shift key, and;
1478 // * Pressing the key and a right-alt (AltGr) key.
1479 static const MockKeyboard::Modifiers kModifiers[] = {
1480 MockKeyboard::NONE,
1481 MockKeyboard::LEFT_SHIFT,
1482 #if defined(OS_WIN)
1483 MockKeyboard::RIGHT_ALT,
1484 #endif
1485 };
1486
1487 MockKeyboard::Layout layout = kLayouts[i].layout;
1488 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(kModifiers); ++j) {
1489 // Virtual key codes used for this test.
1490 static const int kKeyCodes[] = {
1491 '0', '1', '2', '3', '4', '5', '6', '7',
1492 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
1493 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
1494 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
1495 'W', 'X', 'Y', 'Z',
1496 ui::VKEY_OEM_1,
1497 ui::VKEY_OEM_PLUS,
1498 ui::VKEY_OEM_COMMA,
1499 ui::VKEY_OEM_MINUS,
1500 ui::VKEY_OEM_PERIOD,
1501 ui::VKEY_OEM_2,
1502 ui::VKEY_OEM_3,
1503 ui::VKEY_OEM_4,
1504 ui::VKEY_OEM_5,
1505 ui::VKEY_OEM_6,
1506 ui::VKEY_OEM_7,
1507 #if defined(OS_WIN)
1508 // Unclear how to handle this on Linux.
1509 ui::VKEY_OEM_8,
1510 #endif
1511 };
1512
1513 MockKeyboard::Modifiers modifiers = kModifiers[j];
1514 for (size_t k = 0; k < ARRAYSIZE_UNSAFE(kKeyCodes); ++k) {
1515 // Send a keyboard event to the RenderView object.
1516 // We should test a keyboard event only when the given keyboard-layout
1517 // driver is installed in a PC and the driver can assign a Unicode
1518 // charcter for the given tuple (layout, key-code, and modifiers).
1519 int key_code = kKeyCodes[k];
1520 base::string16 char_code;
1521 if (SendKeyEvent(layout, key_code, modifiers, &char_code) < 0)
1522 continue;
1523 }
1524 }
1525
1526 // Retrieve the text in the test page and compare it with the expected
1527 // text created from a virtual-key code, a character code, and the
1528 // modifier-key status.
1529 const int kMaxOutputCharacters = 4096;
1530 std::wstring output = UTF16ToWideHack(
1531 GetMainFrame()->contentAsText(kMaxOutputCharacters));
1532 EXPECT_EQ(kLayouts[i].expected_result, output);
1533 }
1534 #else
1535 NOTIMPLEMENTED();
1536 #endif
1537 }
1538
1539 // Crashy, http://crbug.com/53247.
TEST_F(RenderViewImplTest,DISABLED_DidFailProvisionalLoadWithErrorForError)1540 TEST_F(RenderViewImplTest, DISABLED_DidFailProvisionalLoadWithErrorForError) {
1541 GetMainFrame()->enableViewSourceMode(true);
1542 WebURLError error;
1543 error.domain = WebString::fromUTF8(net::kErrorDomain);
1544 error.reason = net::ERR_FILE_NOT_FOUND;
1545 error.unreachableURL = GURL("http://foo");
1546 WebFrame* web_frame = GetMainFrame();
1547
1548 // Start a load that will reach provisional state synchronously,
1549 // but won't complete synchronously.
1550 ViewMsg_Navigate_Params params;
1551 params.page_id = -1;
1552 params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
1553 params.url = GURL("data:text/html,test data");
1554 view()->OnNavigate(params);
1555
1556 // An error occurred.
1557 view()->didFailProvisionalLoad(web_frame, error);
1558 // Frame should exit view-source mode.
1559 EXPECT_FALSE(web_frame->isViewSourceModeEnabled());
1560 }
1561
TEST_F(RenderViewImplTest,DidFailProvisionalLoadWithErrorForCancellation)1562 TEST_F(RenderViewImplTest, DidFailProvisionalLoadWithErrorForCancellation) {
1563 GetMainFrame()->enableViewSourceMode(true);
1564 WebURLError error;
1565 error.domain = WebString::fromUTF8(net::kErrorDomain);
1566 error.reason = net::ERR_ABORTED;
1567 error.unreachableURL = GURL("http://foo");
1568 WebFrame* web_frame = GetMainFrame();
1569
1570 // Start a load that will reach provisional state synchronously,
1571 // but won't complete synchronously.
1572 ViewMsg_Navigate_Params params;
1573 params.page_id = -1;
1574 params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
1575 params.url = GURL("data:text/html,test data");
1576 view()->OnNavigate(params);
1577
1578 // A cancellation occurred.
1579 view()->didFailProvisionalLoad(web_frame, error);
1580 // Frame should stay in view-source mode.
1581 EXPECT_TRUE(web_frame->isViewSourceModeEnabled());
1582 }
1583
1584 // Regression test for http://crbug.com/41562
TEST_F(RenderViewImplTest,UpdateTargetURLWithInvalidURL)1585 TEST_F(RenderViewImplTest, UpdateTargetURLWithInvalidURL) {
1586 const GURL invalid_gurl("http://");
1587 view()->setMouseOverURL(blink::WebURL(invalid_gurl));
1588 EXPECT_EQ(invalid_gurl, view()->target_url_);
1589 }
1590
TEST_F(RenderViewImplTest,SetHistoryLengthAndPrune)1591 TEST_F(RenderViewImplTest, SetHistoryLengthAndPrune) {
1592 int expected_page_id = -1;
1593
1594 // No history to merge and no committed pages.
1595 view()->OnSetHistoryLengthAndPrune(0, -1);
1596 EXPECT_EQ(0, view()->history_list_length_);
1597 EXPECT_EQ(-1, view()->history_list_offset_);
1598
1599 // History to merge and no committed pages.
1600 view()->OnSetHistoryLengthAndPrune(2, -1);
1601 EXPECT_EQ(2, view()->history_list_length_);
1602 EXPECT_EQ(1, view()->history_list_offset_);
1603 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1604 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1605 ClearHistory();
1606
1607 // No history to merge and a committed page to be kept.
1608 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1609 expected_page_id = view()->page_id_;
1610 view()->OnSetHistoryLengthAndPrune(0, expected_page_id);
1611 EXPECT_EQ(1, view()->history_list_length_);
1612 EXPECT_EQ(0, view()->history_list_offset_);
1613 EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
1614 ClearHistory();
1615
1616 // No history to merge and a committed page to be pruned.
1617 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1618 expected_page_id = view()->page_id_;
1619 view()->OnSetHistoryLengthAndPrune(0, expected_page_id + 1);
1620 EXPECT_EQ(0, view()->history_list_length_);
1621 EXPECT_EQ(-1, view()->history_list_offset_);
1622 ClearHistory();
1623
1624 // No history to merge and a committed page that the browser was unaware of.
1625 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1626 expected_page_id = view()->page_id_;
1627 view()->OnSetHistoryLengthAndPrune(0, -1);
1628 EXPECT_EQ(1, view()->history_list_length_);
1629 EXPECT_EQ(0, view()->history_list_offset_);
1630 EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
1631 ClearHistory();
1632
1633 // History to merge and a committed page to be kept.
1634 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1635 expected_page_id = view()->page_id_;
1636 view()->OnSetHistoryLengthAndPrune(2, expected_page_id);
1637 EXPECT_EQ(3, view()->history_list_length_);
1638 EXPECT_EQ(2, view()->history_list_offset_);
1639 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1640 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1641 EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
1642 ClearHistory();
1643
1644 // History to merge and a committed page to be pruned.
1645 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1646 expected_page_id = view()->page_id_;
1647 view()->OnSetHistoryLengthAndPrune(2, expected_page_id + 1);
1648 EXPECT_EQ(2, view()->history_list_length_);
1649 EXPECT_EQ(1, view()->history_list_offset_);
1650 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1651 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1652 ClearHistory();
1653
1654 // History to merge and a committed page that the browser was unaware of.
1655 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1656 expected_page_id = view()->page_id_;
1657 view()->OnSetHistoryLengthAndPrune(2, -1);
1658 EXPECT_EQ(3, view()->history_list_length_);
1659 EXPECT_EQ(2, view()->history_list_offset_);
1660 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1661 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1662 EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
1663 ClearHistory();
1664
1665 int expected_page_id_2 = -1;
1666
1667 // No history to merge and two committed pages, both to be kept.
1668 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1669 expected_page_id = view()->page_id_;
1670 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1671 expected_page_id_2 = view()->page_id_;
1672 EXPECT_GT(expected_page_id_2, expected_page_id);
1673 view()->OnSetHistoryLengthAndPrune(0, expected_page_id);
1674 EXPECT_EQ(2, view()->history_list_length_);
1675 EXPECT_EQ(1, view()->history_list_offset_);
1676 EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
1677 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[1]);
1678 ClearHistory();
1679
1680 // No history to merge and two committed pages, and only the second is kept.
1681 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1682 expected_page_id = view()->page_id_;
1683 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1684 expected_page_id_2 = view()->page_id_;
1685 EXPECT_GT(expected_page_id_2, expected_page_id);
1686 view()->OnSetHistoryLengthAndPrune(0, expected_page_id_2);
1687 EXPECT_EQ(1, view()->history_list_length_);
1688 EXPECT_EQ(0, view()->history_list_offset_);
1689 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[0]);
1690 ClearHistory();
1691
1692 // No history to merge and two committed pages, both of which the browser was
1693 // unaware of.
1694 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1695 expected_page_id = view()->page_id_;
1696 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1697 expected_page_id_2 = view()->page_id_;
1698 EXPECT_GT(expected_page_id_2, expected_page_id);
1699 view()->OnSetHistoryLengthAndPrune(0, -1);
1700 EXPECT_EQ(2, view()->history_list_length_);
1701 EXPECT_EQ(1, view()->history_list_offset_);
1702 EXPECT_EQ(expected_page_id, view()->history_page_ids_[0]);
1703 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[1]);
1704 ClearHistory();
1705
1706 // History to merge and two committed pages, both to be kept.
1707 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1708 expected_page_id = view()->page_id_;
1709 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1710 expected_page_id_2 = view()->page_id_;
1711 EXPECT_GT(expected_page_id_2, expected_page_id);
1712 view()->OnSetHistoryLengthAndPrune(2, expected_page_id);
1713 EXPECT_EQ(4, view()->history_list_length_);
1714 EXPECT_EQ(3, view()->history_list_offset_);
1715 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1716 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1717 EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
1718 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[3]);
1719 ClearHistory();
1720
1721 // History to merge and two committed pages, and only the second is kept.
1722 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1723 expected_page_id = view()->page_id_;
1724 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1725 expected_page_id_2 = view()->page_id_;
1726 EXPECT_GT(expected_page_id_2, expected_page_id);
1727 view()->OnSetHistoryLengthAndPrune(2, expected_page_id_2);
1728 EXPECT_EQ(3, view()->history_list_length_);
1729 EXPECT_EQ(2, view()->history_list_offset_);
1730 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1731 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1732 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[2]);
1733 ClearHistory();
1734
1735 // History to merge and two committed pages, both of which the browser was
1736 // unaware of.
1737 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1738 expected_page_id = view()->page_id_;
1739 view()->didCommitProvisionalLoad(GetMainFrame(), true);
1740 expected_page_id_2 = view()->page_id_;
1741 EXPECT_GT(expected_page_id_2, expected_page_id);
1742 view()->OnSetHistoryLengthAndPrune(2, -1);
1743 EXPECT_EQ(4, view()->history_list_length_);
1744 EXPECT_EQ(3, view()->history_list_offset_);
1745 EXPECT_EQ(-1, view()->history_page_ids_[0]);
1746 EXPECT_EQ(-1, view()->history_page_ids_[1]);
1747 EXPECT_EQ(expected_page_id, view()->history_page_ids_[2]);
1748 EXPECT_EQ(expected_page_id_2, view()->history_page_ids_[3]);
1749 }
1750
TEST_F(RenderViewImplTest,ContextMenu)1751 TEST_F(RenderViewImplTest, ContextMenu) {
1752 LoadHTML("<div>Page A</div>");
1753
1754 // Create a right click in the center of the iframe. (I'm hoping this will
1755 // make this a bit more robust in case of some other formatting or other bug.)
1756 WebMouseEvent mouse_event;
1757 mouse_event.type = WebInputEvent::MouseDown;
1758 mouse_event.button = WebMouseEvent::ButtonRight;
1759 mouse_event.x = 250;
1760 mouse_event.y = 250;
1761 mouse_event.globalX = 250;
1762 mouse_event.globalY = 250;
1763
1764 SendWebMouseEvent(mouse_event);
1765
1766 // Now simulate the corresponding up event which should display the menu
1767 mouse_event.type = WebInputEvent::MouseUp;
1768 SendWebMouseEvent(mouse_event);
1769
1770 EXPECT_TRUE(render_thread_->sink().GetUniqueMessageMatching(
1771 ViewHostMsg_ContextMenu::ID));
1772 }
1773
TEST_F(RenderViewImplTest,TestBackForward)1774 TEST_F(RenderViewImplTest, TestBackForward) {
1775 LoadHTML("<div id=pagename>Page A</div>");
1776 blink::WebHistoryItem page_a_item = GetMainFrame()->currentHistoryItem();
1777 int was_page_a = -1;
1778 base::string16 check_page_a =
1779 ASCIIToUTF16(
1780 "Number(document.getElementById('pagename').innerHTML == 'Page A')");
1781 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_a, &was_page_a));
1782 EXPECT_EQ(1, was_page_a);
1783
1784 LoadHTML("<div id=pagename>Page B</div>");
1785 int was_page_b = -1;
1786 base::string16 check_page_b =
1787 ASCIIToUTF16(
1788 "Number(document.getElementById('pagename').innerHTML == 'Page B')");
1789 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
1790 EXPECT_EQ(1, was_page_b);
1791
1792 LoadHTML("<div id=pagename>Page C</div>");
1793 int was_page_c = -1;
1794 base::string16 check_page_c =
1795 ASCIIToUTF16(
1796 "Number(document.getElementById('pagename').innerHTML == 'Page C')");
1797 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_c, &was_page_c));
1798 EXPECT_EQ(1, was_page_b);
1799
1800 blink::WebHistoryItem forward_item = GetMainFrame()->currentHistoryItem();
1801 GoBack(GetMainFrame()->previousHistoryItem());
1802 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
1803 EXPECT_EQ(1, was_page_b);
1804
1805 GoForward(forward_item);
1806 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_c, &was_page_c));
1807 EXPECT_EQ(1, was_page_c);
1808
1809 GoBack(GetMainFrame()->previousHistoryItem());
1810 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
1811 EXPECT_EQ(1, was_page_b);
1812
1813 forward_item = GetMainFrame()->currentHistoryItem();
1814 GoBack(page_a_item);
1815 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_a, &was_page_a));
1816 EXPECT_EQ(1, was_page_a);
1817
1818 GoForward(forward_item);
1819 EXPECT_TRUE(ExecuteJavaScriptAndReturnIntValue(check_page_b, &was_page_b));
1820 EXPECT_EQ(1, was_page_b);
1821 }
1822
1823 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_AURA)
TEST_F(RenderViewImplTest,GetCompositionCharacterBoundsTest)1824 TEST_F(RenderViewImplTest, GetCompositionCharacterBoundsTest) {
1825
1826 #if defined(OS_WIN)
1827 // http://crbug.com/304193
1828 if (base::win::GetVersion() < base::win::VERSION_VISTA)
1829 return;
1830 #endif
1831
1832 LoadHTML("<textarea id=\"test\"></textarea>");
1833 ExecuteJavaScript("document.getElementById('test').focus();");
1834
1835 const base::string16 empty_string = UTF8ToUTF16("");
1836 const std::vector<blink::WebCompositionUnderline> empty_underline;
1837 std::vector<gfx::Rect> bounds;
1838 view()->OnSetFocus(true);
1839 view()->OnSetInputMethodActive(true);
1840
1841 // ASCII composition
1842 const base::string16 ascii_composition = UTF8ToUTF16("aiueo");
1843 view()->OnImeSetComposition(ascii_composition, empty_underline, 0, 0);
1844 view()->GetCompositionCharacterBounds(&bounds);
1845 ASSERT_EQ(ascii_composition.size(), bounds.size());
1846 for (size_t i = 0; i < bounds.size(); ++i)
1847 EXPECT_LT(0, bounds[i].width());
1848 view()->OnImeConfirmComposition(
1849 empty_string, gfx::Range::InvalidRange(), false);
1850
1851 // Non surrogate pair unicode character.
1852 const base::string16 unicode_composition = UTF8ToUTF16(
1853 "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
1854 view()->OnImeSetComposition(unicode_composition, empty_underline, 0, 0);
1855 view()->GetCompositionCharacterBounds(&bounds);
1856 ASSERT_EQ(unicode_composition.size(), bounds.size());
1857 for (size_t i = 0; i < bounds.size(); ++i)
1858 EXPECT_LT(0, bounds[i].width());
1859 view()->OnImeConfirmComposition(
1860 empty_string, gfx::Range::InvalidRange(), false);
1861
1862 // Surrogate pair character.
1863 const base::string16 surrogate_pair_char = UTF8ToUTF16("\xF0\xA0\xAE\x9F");
1864 view()->OnImeSetComposition(surrogate_pair_char,
1865 empty_underline,
1866 0,
1867 0);
1868 view()->GetCompositionCharacterBounds(&bounds);
1869 ASSERT_EQ(surrogate_pair_char.size(), bounds.size());
1870 EXPECT_LT(0, bounds[0].width());
1871 EXPECT_EQ(0, bounds[1].width());
1872 view()->OnImeConfirmComposition(
1873 empty_string, gfx::Range::InvalidRange(), false);
1874
1875 // Mixed string.
1876 const base::string16 surrogate_pair_mixed_composition =
1877 surrogate_pair_char + UTF8ToUTF16("\xE3\x81\x82") + surrogate_pair_char +
1878 UTF8ToUTF16("b") + surrogate_pair_char;
1879 const size_t utf16_length = 8UL;
1880 const bool is_surrogate_pair_empty_rect[8] = {
1881 false, true, false, false, true, false, false, true };
1882 view()->OnImeSetComposition(surrogate_pair_mixed_composition,
1883 empty_underline,
1884 0,
1885 0);
1886 view()->GetCompositionCharacterBounds(&bounds);
1887 ASSERT_EQ(utf16_length, bounds.size());
1888 for (size_t i = 0; i < utf16_length; ++i) {
1889 if (is_surrogate_pair_empty_rect[i]) {
1890 EXPECT_EQ(0, bounds[i].width());
1891 } else {
1892 EXPECT_LT(0, bounds[i].width());
1893 }
1894 }
1895 view()->OnImeConfirmComposition(
1896 empty_string, gfx::Range::InvalidRange(), false);
1897 }
1898 #endif
1899
TEST_F(RenderViewImplTest,ZoomLimit)1900 TEST_F(RenderViewImplTest, ZoomLimit) {
1901 const double kMinZoomLevel = ZoomFactorToZoomLevel(kMinimumZoomFactor);
1902 const double kMaxZoomLevel = ZoomFactorToZoomLevel(kMaximumZoomFactor);
1903
1904 ViewMsg_Navigate_Params params;
1905 params.page_id = -1;
1906 params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
1907
1908 // Verifies navigation to a URL with preset zoom level indeed sets the level.
1909 // Regression test for http://crbug.com/139559, where the level was not
1910 // properly set when it is out of the default zoom limits of WebView.
1911 params.url = GURL("data:text/html,min_zoomlimit_test");
1912 view()->OnSetZoomLevelForLoadingURL(params.url, kMinZoomLevel);
1913 view()->OnNavigate(params);
1914 ProcessPendingMessages();
1915 EXPECT_DOUBLE_EQ(kMinZoomLevel, view()->GetWebView()->zoomLevel());
1916
1917 // It should work even when the zoom limit is temporarily changed in the page.
1918 view()->GetWebView()->zoomLimitsChanged(ZoomFactorToZoomLevel(1.0),
1919 ZoomFactorToZoomLevel(1.0));
1920 params.url = GURL("data:text/html,max_zoomlimit_test");
1921 view()->OnSetZoomLevelForLoadingURL(params.url, kMaxZoomLevel);
1922 view()->OnNavigate(params);
1923 ProcessPendingMessages();
1924 EXPECT_DOUBLE_EQ(kMaxZoomLevel, view()->GetWebView()->zoomLevel());
1925 }
1926
TEST_F(RenderViewImplTest,SetEditableSelectionAndComposition)1927 TEST_F(RenderViewImplTest, SetEditableSelectionAndComposition) {
1928 // Load an HTML page consisting of an input field.
1929 LoadHTML("<html>"
1930 "<head>"
1931 "</head>"
1932 "<body>"
1933 "<input id=\"test1\" value=\"some test text hello\"></input>"
1934 "</body>"
1935 "</html>");
1936 ExecuteJavaScript("document.getElementById('test1').focus();");
1937 view()->OnSetEditableSelectionOffsets(4, 8);
1938 const std::vector<blink::WebCompositionUnderline> empty_underline;
1939 view()->OnSetCompositionFromExistingText(7, 10, empty_underline);
1940 blink::WebTextInputInfo info = view()->webview()->textInputInfo();
1941 EXPECT_EQ(4, info.selectionStart);
1942 EXPECT_EQ(8, info.selectionEnd);
1943 EXPECT_EQ(7, info.compositionStart);
1944 EXPECT_EQ(10, info.compositionEnd);
1945 view()->OnUnselect();
1946 info = view()->webview()->textInputInfo();
1947 EXPECT_EQ(0, info.selectionStart);
1948 EXPECT_EQ(0, info.selectionEnd);
1949 }
1950
1951
TEST_F(RenderViewImplTest,OnExtendSelectionAndDelete)1952 TEST_F(RenderViewImplTest, OnExtendSelectionAndDelete) {
1953 // Load an HTML page consisting of an input field.
1954 LoadHTML("<html>"
1955 "<head>"
1956 "</head>"
1957 "<body>"
1958 "<input id=\"test1\" value=\"abcdefghijklmnopqrstuvwxyz\"></input>"
1959 "</body>"
1960 "</html>");
1961 ExecuteJavaScript("document.getElementById('test1').focus();");
1962 view()->OnSetEditableSelectionOffsets(10, 10);
1963 view()->OnExtendSelectionAndDelete(3, 4);
1964 blink::WebTextInputInfo info = view()->webview()->textInputInfo();
1965 EXPECT_EQ("abcdefgopqrstuvwxyz", info.value);
1966 EXPECT_EQ(7, info.selectionStart);
1967 EXPECT_EQ(7, info.selectionEnd);
1968 view()->OnSetEditableSelectionOffsets(4, 8);
1969 view()->OnExtendSelectionAndDelete(2, 5);
1970 info = view()->webview()->textInputInfo();
1971 EXPECT_EQ("abuvwxyz", info.value);
1972 EXPECT_EQ(2, info.selectionStart);
1973 EXPECT_EQ(2, info.selectionEnd);
1974 }
1975
1976 // Test that the navigating specific frames works correctly.
TEST_F(RenderViewImplTest,NavigateFrame)1977 TEST_F(RenderViewImplTest, NavigateFrame) {
1978 // Load page A.
1979 LoadHTML("hello <iframe srcdoc='fail' name='frame'></iframe>");
1980
1981 // Navigate the frame only.
1982 ViewMsg_Navigate_Params nav_params;
1983 nav_params.url = GURL("data:text/html,world");
1984 nav_params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
1985 nav_params.transition = PAGE_TRANSITION_TYPED;
1986 nav_params.current_history_list_length = 1;
1987 nav_params.current_history_list_offset = 0;
1988 nav_params.pending_history_list_offset = 1;
1989 nav_params.page_id = -1;
1990 nav_params.frame_to_navigate = "frame";
1991 view()->OnNavigate(nav_params);
1992 ProcessPendingMessages();
1993
1994 // Copy the document content to std::wstring and compare with the
1995 // expected result.
1996 const int kMaxOutputCharacters = 256;
1997 std::wstring output = UTF16ToWideHack(
1998 GetMainFrame()->contentAsText(kMaxOutputCharacters));
1999 EXPECT_EQ(output, L"hello \n\nworld");
2000 }
2001
2002 // This test ensures that a RenderFrame object is created for the top level
2003 // frame in the RenderView.
TEST_F(RenderViewImplTest,BasicRenderFrame)2004 TEST_F(RenderViewImplTest, BasicRenderFrame) {
2005 EXPECT_TRUE(view()->main_render_frame_.get());
2006 }
2007
TEST_F(RenderViewImplTest,GetSSLStatusOfFrame)2008 TEST_F(RenderViewImplTest, GetSSLStatusOfFrame) {
2009 LoadHTML("<!DOCTYPE html><html><body></body></html>");
2010
2011 WebFrame* frame = GetMainFrame();
2012 SSLStatus ssl_status = view()->GetSSLStatusOfFrame(frame);
2013 EXPECT_FALSE(net::IsCertStatusError(ssl_status.cert_status));
2014
2015 const_cast<blink::WebURLResponse&>(frame->dataSource()->response()).
2016 setSecurityInfo(
2017 SerializeSecurityInfo(0, net::CERT_STATUS_ALL_ERRORS, 0, 0,
2018 SignedCertificateTimestampIDStatusList()));
2019 ssl_status = view()->GetSSLStatusOfFrame(frame);
2020 EXPECT_TRUE(net::IsCertStatusError(ssl_status.cert_status));
2021 }
2022
TEST_F(RenderViewImplTest,MessageOrderInDidChangeSelection)2023 TEST_F(RenderViewImplTest, MessageOrderInDidChangeSelection) {
2024 view()->OnSetInputMethodActive(true);
2025 view()->set_send_content_state_immediately(true);
2026 LoadHTML("<textarea id=\"test\"></textarea>");
2027
2028 view()->handling_input_event_ = true;
2029 ExecuteJavaScript("document.getElementById('test').focus();");
2030
2031 bool is_input_type_called = false;
2032 bool is_selection_called = false;
2033 size_t last_input_type = 0;
2034 size_t last_selection = 0;
2035
2036 for (size_t i = 0; i < render_thread_->sink().message_count(); ++i) {
2037 const uint32 type = render_thread_->sink().GetMessageAt(i)->type();
2038 if (type == ViewHostMsg_TextInputTypeChanged::ID) {
2039 is_input_type_called = true;
2040 last_input_type = i;
2041 } else if (type == ViewHostMsg_SelectionChanged::ID) {
2042 is_selection_called = true;
2043 last_selection = i;
2044 }
2045 }
2046
2047 EXPECT_TRUE(is_input_type_called);
2048 EXPECT_TRUE(is_selection_called);
2049
2050 // InputTypeChange shold be called earlier than SelectionChanged.
2051 EXPECT_LT(last_input_type, last_selection);
2052 }
2053
2054 class SuppressErrorPageTest : public RenderViewTest {
2055 public:
SetUp()2056 virtual void SetUp() OVERRIDE {
2057 SetRendererClientForTesting(&client_);
2058 RenderViewTest::SetUp();
2059 }
2060
view()2061 RenderViewImpl* view() {
2062 return static_cast<RenderViewImpl*>(view_);
2063 }
2064
2065 private:
2066 class TestContentRendererClient : public ContentRendererClient {
2067 public:
ShouldSuppressErrorPage(const GURL & url)2068 virtual bool ShouldSuppressErrorPage(const GURL& url) OVERRIDE {
2069 return url == GURL("http://example.com/suppress");
2070 }
2071
GetNavigationErrorStrings(blink::WebFrame * frame,const blink::WebURLRequest & failed_request,const blink::WebURLError & error,const std::string & accept_languages,std::string * error_html,base::string16 * error_description)2072 virtual void GetNavigationErrorStrings(
2073 blink::WebFrame* frame,
2074 const blink::WebURLRequest& failed_request,
2075 const blink::WebURLError& error,
2076 const std::string& accept_languages,
2077 std::string* error_html,
2078 base::string16* error_description) OVERRIDE {
2079 if (error_html)
2080 *error_html = "A suffusion of yellow.";
2081 }
2082 };
2083
2084 TestContentRendererClient client_;
2085 };
2086
2087 #if defined(OS_ANDROID)
2088 // Crashing on Android: http://crbug.com/311341
2089 #define MAYBE_Suppresses DISABLED_Suppresses
2090 #else
2091 #define MAYBE_Suppresses Suppresses
2092 #endif
2093
TEST_F(SuppressErrorPageTest,MAYBE_Suppresses)2094 TEST_F(SuppressErrorPageTest, MAYBE_Suppresses) {
2095 WebURLError error;
2096 error.domain = WebString::fromUTF8(net::kErrorDomain);
2097 error.reason = net::ERR_FILE_NOT_FOUND;
2098 error.unreachableURL = GURL("http://example.com/suppress");
2099 WebFrame* web_frame = GetMainFrame();
2100
2101 // Start a load that will reach provisional state synchronously,
2102 // but won't complete synchronously.
2103 ViewMsg_Navigate_Params params;
2104 params.page_id = -1;
2105 params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
2106 params.url = GURL("data:text/html,test data");
2107 view()->OnNavigate(params);
2108
2109 // An error occurred.
2110 view()->didFailProvisionalLoad(web_frame, error);
2111 const int kMaxOutputCharacters = 22;
2112 EXPECT_EQ("", UTF16ToASCII(web_frame->contentAsText(kMaxOutputCharacters)));
2113 }
2114
2115 #if defined(OS_ANDROID)
2116 // Crashing on Android: http://crbug.com/311341
2117 #define MAYBE_DoesNotSuppress DISABLED_DoesNotSuppress
2118 #else
2119 #define MAYBE_DoesNotSuppress DoesNotSuppress
2120 #endif
2121
TEST_F(SuppressErrorPageTest,MAYBE_DoesNotSuppress)2122 TEST_F(SuppressErrorPageTest, MAYBE_DoesNotSuppress) {
2123 WebURLError error;
2124 error.domain = WebString::fromUTF8(net::kErrorDomain);
2125 error.reason = net::ERR_FILE_NOT_FOUND;
2126 error.unreachableURL = GURL("http://example.com/dont-suppress");
2127 WebFrame* web_frame = GetMainFrame();
2128
2129 // Start a load that will reach provisional state synchronously,
2130 // but won't complete synchronously.
2131 ViewMsg_Navigate_Params params;
2132 params.page_id = -1;
2133 params.navigation_type = ViewMsg_Navigate_Type::NORMAL;
2134 params.url = GURL("data:text/html,test data");
2135 view()->OnNavigate(params);
2136
2137 // An error occurred.
2138 view()->didFailProvisionalLoad(web_frame, error);
2139 ProcessPendingMessages();
2140 const int kMaxOutputCharacters = 22;
2141 EXPECT_EQ("A suffusion of yellow.",
2142 UTF16ToASCII(web_frame->contentAsText(kMaxOutputCharacters)));
2143 }
2144
2145 // Tests if IME API's candidatewindow* events sent from browser are handled
2146 // in renderer.
TEST_F(RenderViewImplTest,SendCandidateWindowEvents)2147 TEST_F(RenderViewImplTest, SendCandidateWindowEvents) {
2148 // Sends an HTML with an <input> element and scripts to the renderer.
2149 // The script handles all 3 of candidatewindow* events for an
2150 // InputMethodContext object and once it received 'show', 'update', 'hide'
2151 // should appear in the result div.
2152 LoadHTML("<input id='test'>"
2153 "<div id='result'>Result: </div>"
2154 "<script>"
2155 "window.onload = function() {"
2156 " var result = document.getElementById('result');"
2157 " var test = document.getElementById('test');"
2158 " test.focus();"
2159 " var context = test.inputMethodContext;"
2160 " if (context) {"
2161 " context.oncandidatewindowshow = function() {"
2162 " result.innerText += 'show'; };"
2163 " context.oncandidatewindowupdate = function(){"
2164 " result.innerText += 'update'; };"
2165 " context.oncandidatewindowhide = function(){"
2166 " result.innerText += 'hide'; };"
2167 " }"
2168 "};"
2169 "</script>");
2170
2171 // Fire candidatewindow events.
2172 view()->OnCandidateWindowShown();
2173 view()->OnCandidateWindowUpdated();
2174 view()->OnCandidateWindowHidden();
2175
2176 // Retrieve the content and check if it is expected.
2177 const int kMaxOutputCharacters = 50;
2178 std::string output = UTF16ToUTF8(
2179 GetMainFrame()->contentAsText(kMaxOutputCharacters));
2180 EXPECT_EQ(output, "\nResult:showupdatehide");
2181 }
2182
2183 } // namespace content
2184