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