1// Copyright (c) 2011 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/strings/utf_string_conversions.h"
6#include "content/common/view_messages.h"
7#include "content/public/test/render_view_test.h"
8#include "content/renderer/render_view_impl.h"
9#include "testing/gtest/include/gtest/gtest.h"
10#include "third_party/WebKit/public/web/WebView.h"
11#include "third_party/WebKit/public/platform/WebSize.h"
12
13// Tests for the external select popup menu (Mac specific).
14
15namespace content {
16namespace {
17
18const char* const kSelectID = "mySelect";
19const char* const kEmptySelectID = "myEmptySelect";
20
21}  // namespace
22
23class ExternalPopupMenuTest : public RenderViewTest {
24 public:
25  ExternalPopupMenuTest() {}
26
27  RenderViewImpl* view() {
28    return static_cast<RenderViewImpl*>(view_);
29  }
30
31  virtual void SetUp() {
32    RenderViewTest::SetUp();
33    // We need to set this explictly as RenderMain is not run.
34    blink::WebView::setUseExternalPopupMenus(true);
35
36    std::string html = "<select id='mySelect' onchange='selectChanged(this)'>"
37                       "  <option>zero</option>"
38                       "  <option selected='1'>one</option>"
39                       "  <option>two</option>"
40                       "</select>"
41                       "<select id='myEmptySelect'>"
42                       "</select>";
43    if (ShouldRemoveSelectOnChange()) {
44      html += "<script>"
45              "  function selectChanged(select) {"
46              "    select.parentNode.removeChild(select);"
47              "  }"
48              "</script>";
49    }
50
51    // Load the test page.
52    LoadHTML(html.c_str());
53
54    // Set a minimum size and give focus so simulated events work.
55    view()->webwidget()->resize(blink::WebSize(500, 500));
56    view()->webwidget()->setFocus(true);
57  }
58
59  int GetSelectedIndex() {
60    base::string16 script(ASCIIToUTF16(kSelectID));
61    script.append(ASCIIToUTF16(".selectedIndex"));
62    int selected_index = -1;
63    ExecuteJavaScriptAndReturnIntValue(script, &selected_index);
64    return selected_index;
65  }
66
67 protected:
68  virtual bool ShouldRemoveSelectOnChange() const { return false; }
69
70  DISALLOW_COPY_AND_ASSIGN(ExternalPopupMenuTest);
71};
72
73// Normal case: test showing a select popup, canceling/selecting an item.
74TEST_F(ExternalPopupMenuTest, NormalCase) {
75  IPC::TestSink& sink = render_thread_->sink();
76
77  // Click the text field once.
78  EXPECT_TRUE(SimulateElementClick(kSelectID));
79
80  // We should have sent a message to the browser to show the popup menu.
81  const IPC::Message* message =
82      sink.GetUniqueMessageMatching(ViewHostMsg_ShowPopup::ID);
83  ASSERT_TRUE(message != NULL);
84  Tuple1<ViewHostMsg_ShowPopup_Params> param;
85  ViewHostMsg_ShowPopup::Read(message, &param);
86  ASSERT_EQ(3U, param.a.popup_items.size());
87  EXPECT_EQ(1, param.a.selected_item);
88
89  // Simulate the user canceling the popup, the index should not have changed.
90  view()->OnSelectPopupMenuItem(-1);
91  EXPECT_EQ(1, GetSelectedIndex());
92
93  // Show the pop-up again and this time make a selection.
94  EXPECT_TRUE(SimulateElementClick(kSelectID));
95  view()->OnSelectPopupMenuItem(0);
96  EXPECT_EQ(0, GetSelectedIndex());
97
98  // Show the pop-up again and make another selection.
99  sink.ClearMessages();
100  EXPECT_TRUE(SimulateElementClick(kSelectID));
101  message = sink.GetUniqueMessageMatching(ViewHostMsg_ShowPopup::ID);
102  ASSERT_TRUE(message != NULL);
103  ViewHostMsg_ShowPopup::Read(message, &param);
104  ASSERT_EQ(3U, param.a.popup_items.size());
105  EXPECT_EQ(0, param.a.selected_item);
106}
107
108// Page shows popup, then navigates away while popup showing, then select.
109TEST_F(ExternalPopupMenuTest, ShowPopupThenNavigate) {
110  // Click the text field once.
111  EXPECT_TRUE(SimulateElementClick(kSelectID));
112
113  // Now we navigate to another pager.
114  LoadHTML("<blink>Awesome page!</blink>");
115
116  // Now the user selects something, we should not crash.
117  view()->OnSelectPopupMenuItem(-1);
118}
119
120// An empty select should not cause a crash when clicked.
121// http://crbug.com/63774
122TEST_F(ExternalPopupMenuTest, EmptySelect) {
123  EXPECT_TRUE(SimulateElementClick(kEmptySelectID));
124}
125
126class ExternalPopupMenuRemoveTest : public ExternalPopupMenuTest {
127 public:
128  ExternalPopupMenuRemoveTest() {}
129
130 protected:
131  virtual bool ShouldRemoveSelectOnChange() const OVERRIDE { return true; }
132};
133
134// Tests that nothing bad happen when the page removes the select when it
135// changes. (http://crbug.com/61997)
136TEST_F(ExternalPopupMenuRemoveTest, RemoveOnChange) {
137  // Click the text field once to show the popup.
138  EXPECT_TRUE(SimulateElementClick(kSelectID));
139
140  // Select something, it causes the select to be removed from the page.
141  view()->OnSelectPopupMenuItem(0);
142
143  // Just to check the soundness of the test, call SimulateElementClick again.
144  // It should return false as the select has been removed.
145  EXPECT_FALSE(SimulateElementClick(kSelectID));
146}
147
148}  // namespace content
149