Home | History | Annotate | Download | only in renderer
      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, &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.
    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, &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