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