• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/frame_messages.h"
7 #include "content/public/test/render_view_test.h"
8 #include "content/renderer/render_frame_impl.h"
9 #include "content/renderer/render_view_impl.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "third_party/WebKit/public/web/WebView.h"
12 #include "third_party/WebKit/public/platform/WebSize.h"
13 
14 // Tests for the external select popup menu (Mac specific).
15 
16 namespace content {
17 namespace {
18 
19 const char* const kSelectID = "mySelect";
20 const char* const kEmptySelectID = "myEmptySelect";
21 
22 }  // namespace
23 
24 class ExternalPopupMenuTest : public RenderViewTest {
25  public:
ExternalPopupMenuTest()26   ExternalPopupMenuTest() {}
27 
view()28   RenderViewImpl* view() {
29     return static_cast<RenderViewImpl*>(view_);
30   }
31 
frame()32   RenderFrameImpl* frame() {
33     return view()->GetMainRenderFrame();
34   }
35 
SetUp()36   virtual void SetUp() {
37     RenderViewTest::SetUp();
38     // We need to set this explictly as RenderMain is not run.
39     blink::WebView::setUseExternalPopupMenus(true);
40 
41     std::string html = "<select id='mySelect' onchange='selectChanged(this)'>"
42                        "  <option>zero</option>"
43                        "  <option selected='1'>one</option>"
44                        "  <option>two</option>"
45                        "</select>"
46                        "<select id='myEmptySelect'>"
47                        "</select>";
48     if (ShouldRemoveSelectOnChange()) {
49       html += "<script>"
50               "  function selectChanged(select) {"
51               "    select.parentNode.removeChild(select);"
52               "  }"
53               "</script>";
54     }
55 
56     // Load the test page.
57     LoadHTML(html.c_str());
58 
59     // Set a minimum size and give focus so simulated events work.
60     view()->webwidget()->resize(blink::WebSize(500, 500));
61     view()->webwidget()->setFocus(true);
62   }
63 
GetSelectedIndex()64   int GetSelectedIndex() {
65     base::string16 script(base::ASCIIToUTF16(kSelectID));
66     script.append(base::ASCIIToUTF16(".selectedIndex"));
67     int selected_index = -1;
68     ExecuteJavaScriptAndReturnIntValue(script, &selected_index);
69     return selected_index;
70   }
71 
72  protected:
ShouldRemoveSelectOnChange() const73   virtual bool ShouldRemoveSelectOnChange() const { return false; }
74 
75   DISALLOW_COPY_AND_ASSIGN(ExternalPopupMenuTest);
76 };
77 
78 // Normal case: test showing a select popup, canceling/selecting an item.
TEST_F(ExternalPopupMenuTest,NormalCase)79 TEST_F(ExternalPopupMenuTest, NormalCase) {
80   IPC::TestSink& sink = render_thread_->sink();
81 
82   // Click the text field once.
83   EXPECT_TRUE(SimulateElementClick(kSelectID));
84 
85   // We should have sent a message to the browser to show the popup menu.
86   const IPC::Message* message =
87       sink.GetUniqueMessageMatching(FrameHostMsg_ShowPopup::ID);
88   ASSERT_TRUE(message != NULL);
89   Tuple1<FrameHostMsg_ShowPopup_Params> param;
90   FrameHostMsg_ShowPopup::Read(message, &param);
91   ASSERT_EQ(3U, param.a.popup_items.size());
92   EXPECT_EQ(1, param.a.selected_item);
93 
94   // Simulate the user canceling the popup; the index should not have changed.
95   frame()->OnSelectPopupMenuItem(-1);
96   EXPECT_EQ(1, GetSelectedIndex());
97 
98   // Show the pop-up again and this time make a selection.
99   EXPECT_TRUE(SimulateElementClick(kSelectID));
100   frame()->OnSelectPopupMenuItem(0);
101   EXPECT_EQ(0, GetSelectedIndex());
102 
103   // Show the pop-up again and make another selection.
104   sink.ClearMessages();
105   EXPECT_TRUE(SimulateElementClick(kSelectID));
106   message = sink.GetUniqueMessageMatching(FrameHostMsg_ShowPopup::ID);
107   ASSERT_TRUE(message != NULL);
108   FrameHostMsg_ShowPopup::Read(message, &param);
109   ASSERT_EQ(3U, param.a.popup_items.size());
110   EXPECT_EQ(0, param.a.selected_item);
111 }
112 
113 // Page shows popup, then navigates away while popup showing, then select.
TEST_F(ExternalPopupMenuTest,ShowPopupThenNavigate)114 TEST_F(ExternalPopupMenuTest, ShowPopupThenNavigate) {
115   // Click the text field once.
116   EXPECT_TRUE(SimulateElementClick(kSelectID));
117 
118   // Now we navigate to another pager.
119   LoadHTML("<blink>Awesome page!</blink>");
120 
121   // Now the user selects something, we should not crash.
122   frame()->OnSelectPopupMenuItem(-1);
123 }
124 
125 // An empty select should not cause a crash when clicked.
126 // http://crbug.com/63774
TEST_F(ExternalPopupMenuTest,EmptySelect)127 TEST_F(ExternalPopupMenuTest, EmptySelect) {
128   EXPECT_TRUE(SimulateElementClick(kEmptySelectID));
129 }
130 
131 class ExternalPopupMenuRemoveTest : public ExternalPopupMenuTest {
132  public:
ExternalPopupMenuRemoveTest()133   ExternalPopupMenuRemoveTest() {}
134 
135  protected:
ShouldRemoveSelectOnChange() const136   virtual bool ShouldRemoveSelectOnChange() const OVERRIDE { return true; }
137 };
138 
139 // Tests that nothing bad happen when the page removes the select when it
140 // changes. (http://crbug.com/61997)
TEST_F(ExternalPopupMenuRemoveTest,RemoveOnChange)141 TEST_F(ExternalPopupMenuRemoveTest, RemoveOnChange) {
142   // Click the text field once to show the popup.
143   EXPECT_TRUE(SimulateElementClick(kSelectID));
144 
145   // Select something, it causes the select to be removed from the page.
146   frame()->OnSelectPopupMenuItem(0);
147 
148   // Just to check the soundness of the test, call SimulateElementClick again.
149   // It should return false as the select has been removed.
150   EXPECT_FALSE(SimulateElementClick(kSelectID));
151 }
152 
153 class ExternalPopupMenuDisplayNoneTest : public ExternalPopupMenuTest {
154   public:
ExternalPopupMenuDisplayNoneTest()155   ExternalPopupMenuDisplayNoneTest() {}
156 
SetUp()157   virtual void SetUp() {
158     RenderViewTest::SetUp();
159     // We need to set this explictly as RenderMain is not run.
160     blink::WebView::setUseExternalPopupMenus(true);
161 
162     std::string html = "<select id='mySelect'>"
163                        "  <option value='zero'>zero</option>"
164                        "  <optgroup label='hide' style='display: none'>"
165                        "    <option value='one'>one</option>"
166                        "  </optgroup>"
167                        "  <option value='two'>two</option>"
168                        "  <option value='three'>three</option>"
169                        "  <option value='four'>four</option>"
170                        "  <option value='five'>five</option>"
171                        "</select>";
172     // Load the test page.
173     LoadHTML(html.c_str());
174 
175     // Set a minimum size and give focus so simulated events work.
176     view()->webwidget()->resize(blink::WebSize(500, 500));
177     view()->webwidget()->setFocus(true);
178   }
179 
180 };
181 
TEST_F(ExternalPopupMenuDisplayNoneTest,SelectItem)182 TEST_F(ExternalPopupMenuDisplayNoneTest, SelectItem) {
183   IPC::TestSink& sink = render_thread_->sink();
184 
185   // Click the text field once to show the popup.
186   EXPECT_TRUE(SimulateElementClick(kSelectID));
187 
188   // Read the message sent to browser to show the popup menu.
189   const IPC::Message* message =
190       sink.GetUniqueMessageMatching(FrameHostMsg_ShowPopup::ID);
191   ASSERT_TRUE(message != NULL);
192   Tuple1<FrameHostMsg_ShowPopup_Params> param;
193   FrameHostMsg_ShowPopup::Read(message, &param);
194   // Number of items should match item count minus the number
195   // of "display: none" items.
196   ASSERT_EQ(5U, param.a.popup_items.size());
197 
198   // Select index 1 item. This should select item with index 2,
199   // skipping the item with 'display: none'
200   frame()->OnSelectPopupMenuItem(1);
201 
202   EXPECT_EQ(2, GetSelectedIndex());
203 }
204 
205 }  // namespace content
206