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: 26 ExternalPopupMenuTest() {} 27 28 RenderViewImpl* view() { 29 return static_cast<RenderViewImpl*>(view_); 30 } 31 32 RenderFrameImpl* frame() { 33 return view()->GetMainRenderFrame(); 34 } 35 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 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: 73 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. 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, ¶m); 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, ¶m); 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. 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 127 TEST_F(ExternalPopupMenuTest, EmptySelect) { 128 EXPECT_TRUE(SimulateElementClick(kEmptySelectID)); 129 } 130 131 class ExternalPopupMenuRemoveTest : public ExternalPopupMenuTest { 132 public: 133 ExternalPopupMenuRemoveTest() {} 134 135 protected: 136 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) 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: 155 ExternalPopupMenuDisplayNoneTest() {} 156 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 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, ¶m); 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