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