| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/core/html/forms/html_select_element.h" |
| |
| #include <memory> |
| |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/renderer/core/dom/document.h" |
| #include "third_party/blink/renderer/core/frame/local_frame_view.h" |
| #include "third_party/blink/renderer/core/frame/settings.h" |
| #include "third_party/blink/renderer/core/html/forms/form_controller.h" |
| #include "third_party/blink/renderer/core/html/forms/html_form_element.h" |
| #include "third_party/blink/renderer/core/html/forms/select_type.h" |
| #include "third_party/blink/renderer/core/layout/layout_theme.h" |
| #include "third_party/blink/renderer/core/testing/page_test_base.h" |
| #include "third_party/blink/renderer/platform/heap/heap.h" |
| |
| namespace blink { |
| |
| class HTMLSelectElementTest : public PageTestBase { |
| protected: |
| void SetUp() override; |
| void TearDown() override; |
| |
| SelectType& GetSelectType(const HTMLSelectElement& select) { |
| return *select.select_type_; |
| } |
| |
| HTMLOptionElement* FirstSelectableOption(const HTMLSelectElement& select) { |
| return GetSelectType(select).FirstSelectableOption(); |
| } |
| HTMLOptionElement* LastSelectableOption(const HTMLSelectElement& select) { |
| return GetSelectType(select).LastSelectableOption(); |
| } |
| HTMLOptionElement* NextSelectableOption(const HTMLSelectElement& select, |
| HTMLOptionElement* option) { |
| return GetSelectType(select).NextSelectableOption(option); |
| } |
| HTMLOptionElement* PreviousSelectableOption(const HTMLSelectElement& select, |
| HTMLOptionElement* option) { |
| return GetSelectType(select).PreviousSelectableOption(option); |
| } |
| |
| bool FirstSelectIsConnectedAfterSelectMultiple(const Vector<int>& indices) { |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| select->focus(); |
| select->SelectMultipleOptionsByPopup(indices); |
| return select->isConnected(); |
| } |
| |
| String MenuListLabel() const { |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| return select->InnerElement().textContent(); |
| } |
| |
| private: |
| bool original_delegates_flag_; |
| }; |
| |
| void HTMLSelectElementTest::SetUp() { |
| PageTestBase::SetUp(); |
| GetDocument().SetMimeType("text/html"); |
| original_delegates_flag_ = |
| LayoutTheme::GetTheme().DelegatesMenuListRendering(); |
| } |
| |
| void HTMLSelectElementTest::TearDown() { |
| LayoutTheme::GetTheme().SetDelegatesMenuListRenderingForTesting( |
| original_delegates_flag_); |
| PageTestBase::TearDown(); |
| } |
| |
| TEST_F(HTMLSelectElementTest, SaveRestoreSelectSingleFormControlState) { |
| SetHtmlInnerHTML( |
| "<!DOCTYPE HTML><select id='sel'>" |
| "<option value='111' id='0'>111</option>" |
| "<option value='222'>222</option>" |
| "<option value='111' selected id='2'>!666</option>" |
| "<option value='999'>999</option></select>"); |
| Element* element = GetElementById("sel"); |
| auto* opt0 = To<HTMLOptionElement>(GetElementById("0")); |
| auto* opt2 = To<HTMLOptionElement>(GetElementById("2")); |
| |
| // Save the select element state, and then restore again. |
| // Test passes if the restored state is not changed. |
| EXPECT_EQ(2, To<HTMLSelectElement>(element)->selectedIndex()); |
| EXPECT_FALSE(opt0->Selected()); |
| EXPECT_TRUE(opt2->Selected()); |
| HTMLFormControlElementWithState* select = To<HTMLSelectElement>(element); |
| FormControlState select_state = select->SaveFormControlState(); |
| EXPECT_EQ(2U, select_state.ValueSize()); |
| |
| // Clear the selected state, to be restored by restoreFormControlState. |
| To<HTMLSelectElement>(select)->setSelectedIndex(-1); |
| ASSERT_FALSE(opt2->Selected()); |
| |
| // Restore |
| select->RestoreFormControlState(select_state); |
| EXPECT_EQ(2, To<HTMLSelectElement>(element)->selectedIndex()); |
| EXPECT_FALSE(opt0->Selected()); |
| EXPECT_TRUE(opt2->Selected()); |
| EXPECT_EQ("!666", |
| To<HTMLSelectElement>(element)->InnerElement().textContent()); |
| } |
| |
| TEST_F(HTMLSelectElementTest, SaveRestoreSelectMultipleFormControlState) { |
| SetHtmlInnerHTML( |
| "<!DOCTYPE HTML><select id='sel' multiple>" |
| "<option value='111' id='0'>111</option>" |
| "<option value='222'>222</option>" |
| "<option value='111' selected id='2'>!666</option>" |
| "<option value='999' selected id='3'>999</option></select>"); |
| |
| auto* opt0 = To<HTMLOptionElement>(GetElementById("0")); |
| auto* opt2 = To<HTMLOptionElement>(GetElementById("2")); |
| auto* opt3 = To<HTMLOptionElement>(GetElementById("3")); |
| |
| // Save the select element state, and then restore again. |
| // Test passes if the selected options are not changed. |
| EXPECT_FALSE(opt0->Selected()); |
| EXPECT_TRUE(opt2->Selected()); |
| EXPECT_TRUE(opt3->Selected()); |
| HTMLFormControlElementWithState* select = |
| To<HTMLSelectElement>(GetElementById("sel")); |
| FormControlState select_state = select->SaveFormControlState(); |
| EXPECT_EQ(4U, select_state.ValueSize()); |
| |
| // Clear the selected state, to be restored by restoreFormControlState. |
| opt2->SetSelected(false); |
| opt3->SetSelected(false); |
| ASSERT_FALSE(opt2->Selected()); |
| ASSERT_FALSE(opt3->Selected()); |
| |
| // Restore |
| select->RestoreFormControlState(select_state); |
| EXPECT_FALSE(opt0->Selected()); |
| EXPECT_TRUE(opt2->Selected()); |
| EXPECT_TRUE(opt3->Selected()); |
| } |
| |
| TEST_F(HTMLSelectElementTest, RestoreUnmatchedFormControlState) { |
| // We had a bug that SelectedOption() and last_on_change_option_ were |
| // mismatched in OptionToBeShown(). It happened when |
| // RestoreFormControlState() couldn't find matched OPTIONs. |
| // crbug.com/627833. |
| |
| SetHtmlInnerHTML(R"HTML( |
| <select id='sel'> |
| <option selected>Default</option> |
| <option id='2'>222</option> |
| </select> |
| )HTML"); |
| Element* element = GetElementById("sel"); |
| auto* opt2 = To<HTMLOptionElement>(GetElementById("2")); |
| |
| To<HTMLSelectElement>(element)->setSelectedIndex(1); |
| // Save the current state. |
| HTMLFormControlElementWithState* select = To<HTMLSelectElement>(element); |
| FormControlState select_state = select->SaveFormControlState(); |
| EXPECT_EQ(2U, select_state.ValueSize()); |
| |
| // Reset the status. |
| select->Reset(); |
| ASSERT_FALSE(opt2->Selected()); |
| element->RemoveChild(opt2); |
| |
| // Restore |
| select->RestoreFormControlState(select_state); |
| EXPECT_EQ(-1, To<HTMLSelectElement>(element)->selectedIndex()); |
| EXPECT_EQ(nullptr, |
| To<HTMLSelectElement>(element)->OptionToBeShownForTesting()); |
| } |
| |
| TEST_F(HTMLSelectElementTest, VisibleBoundsInVisualViewport) { |
| SetHtmlInnerHTML( |
| "<select style='position:fixed; top:12.3px; height:24px; " |
| "-webkit-appearance:none;'><option>o1</select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| ASSERT_NE(select, nullptr); |
| IntRect bounds = select->VisibleBoundsInVisualViewport(); |
| EXPECT_EQ(24, bounds.Height()); |
| } |
| |
| TEST_F(HTMLSelectElementTest, PopupIsVisible) { |
| SetHtmlInnerHTML("<select><option>o1</option></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| ASSERT_NE(select, nullptr); |
| EXPECT_FALSE(select->PopupIsVisible()); |
| select->ShowPopup(); |
| EXPECT_TRUE(select->PopupIsVisible()); |
| GetDocument().Shutdown(); |
| EXPECT_FALSE(select->PopupIsVisible()); |
| } |
| |
| TEST_F(HTMLSelectElementTest, FirstSelectableOption) { |
| { |
| SetHtmlInnerHTML("<select></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| EXPECT_EQ(nullptr, FirstSelectableOption(*select)); |
| } |
| { |
| SetHtmlInnerHTML( |
| "<select><option id=o1></option><option id=o2></option></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| EXPECT_EQ("o1", FirstSelectableOption(*select)->FastGetAttribute( |
| html_names::kIdAttr)); |
| } |
| { |
| SetHtmlInnerHTML( |
| "<select><option id=o1 disabled></option><option " |
| "id=o2></option></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| EXPECT_EQ("o2", FirstSelectableOption(*select)->FastGetAttribute( |
| html_names::kIdAttr)); |
| } |
| { |
| SetHtmlInnerHTML( |
| "<select><option id=o1 style='display:none'></option><option " |
| "id=o2></option></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| EXPECT_EQ("o2", FirstSelectableOption(*select)->FastGetAttribute( |
| html_names::kIdAttr)); |
| } |
| { |
| SetHtmlInnerHTML( |
| "<select><optgroup><option id=o1></option><option " |
| "id=o2></option></optgroup></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| EXPECT_EQ("o1", FirstSelectableOption(*select)->FastGetAttribute( |
| html_names::kIdAttr)); |
| } |
| } |
| |
| TEST_F(HTMLSelectElementTest, LastSelectableOption) { |
| { |
| SetHtmlInnerHTML("<select></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| EXPECT_EQ(nullptr, LastSelectableOption(*select)); |
| } |
| { |
| SetHtmlInnerHTML( |
| "<select><option id=o1></option><option id=o2></option></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| EXPECT_EQ("o2", LastSelectableOption(*select)->FastGetAttribute( |
| html_names::kIdAttr)); |
| } |
| { |
| SetHtmlInnerHTML( |
| "<select><option id=o1></option><option id=o2 " |
| "disabled></option></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| EXPECT_EQ("o1", LastSelectableOption(*select)->FastGetAttribute( |
| html_names::kIdAttr)); |
| } |
| { |
| SetHtmlInnerHTML( |
| "<select><option id=o1></option><option id=o2 " |
| "style='display:none'></option></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| EXPECT_EQ("o1", LastSelectableOption(*select)->FastGetAttribute( |
| html_names::kIdAttr)); |
| } |
| { |
| SetHtmlInnerHTML( |
| "<select><optgroup><option id=o1></option><option " |
| "id=o2></option></optgroup></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| EXPECT_EQ("o2", LastSelectableOption(*select)->FastGetAttribute( |
| html_names::kIdAttr)); |
| } |
| } |
| |
| TEST_F(HTMLSelectElementTest, NextSelectableOption) { |
| { |
| SetHtmlInnerHTML("<select></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| EXPECT_EQ(nullptr, NextSelectableOption(*select, nullptr)); |
| } |
| { |
| SetHtmlInnerHTML( |
| "<select><option id=o1></option><option id=o2></option></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| EXPECT_EQ("o1", NextSelectableOption(*select, nullptr) |
| ->FastGetAttribute(html_names::kIdAttr)); |
| } |
| { |
| SetHtmlInnerHTML( |
| "<select><option id=o1 disabled></option><option " |
| "id=o2></option></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| EXPECT_EQ("o2", NextSelectableOption(*select, nullptr) |
| ->FastGetAttribute(html_names::kIdAttr)); |
| } |
| { |
| SetHtmlInnerHTML( |
| "<select><option id=o1 style='display:none'></option><option " |
| "id=o2></option></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| EXPECT_EQ("o2", NextSelectableOption(*select, nullptr) |
| ->FastGetAttribute(html_names::kIdAttr)); |
| } |
| { |
| SetHtmlInnerHTML( |
| "<select><optgroup><option id=o1></option><option " |
| "id=o2></option></optgroup></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| EXPECT_EQ("o1", NextSelectableOption(*select, nullptr) |
| ->FastGetAttribute(html_names::kIdAttr)); |
| } |
| { |
| SetHtmlInnerHTML( |
| "<select><option id=o1></option><option id=o2></option></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| auto* option = To<HTMLOptionElement>(GetElementById("o1")); |
| EXPECT_EQ("o2", NextSelectableOption(*select, option) |
| ->FastGetAttribute(html_names::kIdAttr)); |
| |
| EXPECT_EQ(nullptr, |
| NextSelectableOption( |
| *select, To<HTMLOptionElement>(GetElementById("o2")))); |
| } |
| { |
| SetHtmlInnerHTML( |
| "<select><option id=o1></option><optgroup><option " |
| "id=o2></option></optgroup></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| auto* option = To<HTMLOptionElement>(GetElementById("o1")); |
| EXPECT_EQ("o2", NextSelectableOption(*select, option) |
| ->FastGetAttribute(html_names::kIdAttr)); |
| } |
| } |
| |
| TEST_F(HTMLSelectElementTest, PreviousSelectableOption) { |
| { |
| SetHtmlInnerHTML("<select></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| EXPECT_EQ(nullptr, PreviousSelectableOption(*select, nullptr)); |
| } |
| { |
| SetHtmlInnerHTML( |
| "<select><option id=o1></option><option id=o2></option></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| EXPECT_EQ("o2", PreviousSelectableOption(*select, nullptr) |
| ->FastGetAttribute(html_names::kIdAttr)); |
| } |
| { |
| SetHtmlInnerHTML( |
| "<select><option id=o1></option><option id=o2 " |
| "disabled></option></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| EXPECT_EQ("o1", PreviousSelectableOption(*select, nullptr) |
| ->FastGetAttribute(html_names::kIdAttr)); |
| } |
| { |
| SetHtmlInnerHTML( |
| "<select><option id=o1></option><option id=o2 " |
| "style='display:none'></option></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| EXPECT_EQ("o1", PreviousSelectableOption(*select, nullptr) |
| ->FastGetAttribute(html_names::kIdAttr)); |
| } |
| { |
| SetHtmlInnerHTML( |
| "<select><optgroup><option id=o1></option><option " |
| "id=o2></option></optgroup></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| EXPECT_EQ("o2", PreviousSelectableOption(*select, nullptr) |
| ->FastGetAttribute(html_names::kIdAttr)); |
| } |
| { |
| SetHtmlInnerHTML( |
| "<select><option id=o1></option><option id=o2></option></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| auto* option = To<HTMLOptionElement>(GetElementById("o2")); |
| EXPECT_EQ("o1", PreviousSelectableOption(*select, option) |
| ->FastGetAttribute(html_names::kIdAttr)); |
| |
| EXPECT_EQ(nullptr, |
| PreviousSelectableOption( |
| *select, To<HTMLOptionElement>(GetElementById("o1")))); |
| } |
| { |
| SetHtmlInnerHTML( |
| "<select><option id=o1></option><optgroup><option " |
| "id=o2></option></optgroup></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| auto* option = To<HTMLOptionElement>(GetElementById("o2")); |
| EXPECT_EQ("o1", PreviousSelectableOption(*select, option) |
| ->FastGetAttribute(html_names::kIdAttr)); |
| } |
| } |
| |
| TEST_F(HTMLSelectElementTest, ActiveSelectionEndAfterOptionRemoval) { |
| SetHtmlInnerHTML( |
| "<select size=4>" |
| "<optgroup><option selected>o1</option></optgroup></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| // ActiveSelectionEnd*() work only in the listbox mode, which Android |
| // doesn't have. |
| if (select->UsesMenuList()) |
| return; |
| auto* option = To<HTMLOptionElement>(select->firstChild()->firstChild()); |
| EXPECT_EQ(1, select->ActiveSelectionEndListIndex()); |
| select->firstChild()->removeChild(option); |
| EXPECT_EQ(-1, select->ActiveSelectionEndListIndex()); |
| select->AppendChild(option); |
| EXPECT_EQ(1, select->ActiveSelectionEndListIndex()); |
| } |
| |
| TEST_F(HTMLSelectElementTest, DefaultToolTip) { |
| SetHtmlInnerHTML( |
| "<select size=4><option value=" |
| ">Placeholder</option><optgroup><option>o2</option></optgroup></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| auto* option = To<Element>(select->firstChild()); |
| auto* optgroup = To<Element>(option->nextSibling()); |
| |
| EXPECT_EQ(String(), select->DefaultToolTip()) |
| << "defaultToolTip for SELECT without FORM and without required " |
| "attribute should return null string."; |
| EXPECT_EQ(select->DefaultToolTip(), option->DefaultToolTip()); |
| EXPECT_EQ(select->DefaultToolTip(), optgroup->DefaultToolTip()); |
| |
| select->SetBooleanAttribute(html_names::kRequiredAttr, true); |
| EXPECT_EQ("<<ValidationValueMissingForSelect>>", select->DefaultToolTip()) |
| << "defaultToolTip for SELECT without FORM and with required attribute " |
| "should return a valueMissing message."; |
| EXPECT_EQ(select->DefaultToolTip(), option->DefaultToolTip()); |
| EXPECT_EQ(select->DefaultToolTip(), optgroup->DefaultToolTip()); |
| |
| auto* form = MakeGarbageCollected<HTMLFormElement>(GetDocument()); |
| GetDocument().body()->AppendChild(form); |
| form->AppendChild(select); |
| EXPECT_EQ("<<ValidationValueMissingForSelect>>", select->DefaultToolTip()) |
| << "defaultToolTip for SELECT with FORM and required attribute should " |
| "return a valueMissing message."; |
| EXPECT_EQ(select->DefaultToolTip(), option->DefaultToolTip()); |
| EXPECT_EQ(select->DefaultToolTip(), optgroup->DefaultToolTip()); |
| |
| form->SetBooleanAttribute(html_names::kNovalidateAttr, true); |
| EXPECT_EQ(String(), select->DefaultToolTip()) |
| << "defaultToolTip for SELECT with FORM[novalidate] and required " |
| "attribute should return null string."; |
| EXPECT_EQ(select->DefaultToolTip(), option->DefaultToolTip()); |
| EXPECT_EQ(select->DefaultToolTip(), optgroup->DefaultToolTip()); |
| |
| option->remove(); |
| optgroup->remove(); |
| EXPECT_EQ(String(), option->DefaultToolTip()); |
| EXPECT_EQ(String(), optgroup->DefaultToolTip()); |
| } |
| |
| TEST_F(HTMLSelectElementTest, SetRecalcListItemsByOptgroupRemoval) { |
| SetHtmlInnerHTML( |
| "<select><optgroup><option>sub1</option><option>sub2</option></" |
| "optgroup></select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| select->setInnerHTML(""); |
| // PASS if setInnerHTML didn't have a check failure. |
| } |
| |
| TEST_F(HTMLSelectElementTest, ScrollToOptionAfterLayoutCrash) { |
| // crbug.com/737447 |
| // This test passes if no crash. |
| SetHtmlInnerHTML(R"HTML( |
| <style>*:checked { position:fixed; }</style> |
| <select multiple><<option>o1</option><option |
| selected>o2</option></select> |
| )HTML"); |
| } |
| |
| TEST_F(HTMLSelectElementTest, CrashOnAttachingMenuList) { |
| // crbug.com/1044834 |
| // This test passes if no crash. |
| SetHtmlInnerHTML("<select><option selected style='direction:rtl'>o1"); |
| GetDocument().UpdateStyleAndLayoutTree(); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| ASSERT_TRUE(select->GetLayoutObject()); |
| |
| // Detach LayoutMenuList. |
| select->setAttribute("style", "display:none;"); |
| GetDocument().UpdateStyleAndLayoutTree(); |
| ASSERT_FALSE(select->GetLayoutObject()); |
| |
| // Attach LayoutMenuList again. It triggered null-dereference in |
| // LayoutMenuList::AdjustInnerStyle(). |
| select->removeAttribute("style"); |
| GetDocument().UpdateStyleAndLayoutTree(); |
| ASSERT_TRUE(select->GetLayoutObject()); |
| } |
| |
| TEST_F(HTMLSelectElementTest, CrashOnAttachingMenuList2) { |
| // crbug.com/1065125 |
| // This test passes if no crash. |
| SetHtmlInnerHTML("<select><optgroup><option>o1</select>"); |
| auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild()); |
| select->setTextContent("foo"); |
| |
| // Detach LayoutObject. |
| select->setAttribute("style", "display:none;"); |
| GetDocument().UpdateStyleAndLayoutTree(); |
| |
| // Attach LayoutObject. It triggered a DCHECK failure in |
| // MenuListSelectType::OptionToBeShown() |
| select->removeAttribute("style"); |
| GetDocument().UpdateStyleAndLayoutTree(); |
| } |
| |
| TEST_F(HTMLSelectElementTest, SlotAssignmentRecalcDuringOptionRemoval) { |
| // crbug.com/1056094 |
| // This test passes if no CHECK failure about IsSlotAssignmentRecalcForbidden. |
| SetHtmlInnerHTML("<div dir=auto><select><option>option0"); |
| auto* select = GetDocument().body()->firstChild()->firstChild(); |
| auto* option = select->firstChild(); |
| select->appendChild(option); |
| option->remove(); |
| } |
| |
| // crbug.com/1060039 |
| TEST_F(HTMLSelectElementTest, SelectMultipleOptionsByPopup) { |
| GetDocument().GetSettings()->SetScriptEnabled(true); |
| LayoutTheme::GetTheme().SetDelegatesMenuListRenderingForTesting(true); |
| |
| // Select the same set of options. |
| { |
| SetHtmlInnerHTML( |
| "<select multiple onchange='this.remove();'>" |
| "<option>o0</option><option>o1</option></select>"); |
| EXPECT_TRUE(FirstSelectIsConnectedAfterSelectMultiple(Vector<int>{})) |
| << "Onchange handler should not be executed."; |
| } |
| { |
| SetHtmlInnerHTML( |
| "<select multiple onchange='this.remove();'>" |
| "<option>o0</option><option selected>o1</option></select>"); |
| EXPECT_TRUE(FirstSelectIsConnectedAfterSelectMultiple(Vector<int>{1})) |
| << "Onchange handler should not be executed."; |
| } |
| |
| // 0 old selected options -> 1+ selected options |
| { |
| SetHtmlInnerHTML( |
| "<select multiple onchange='this.remove();'>" |
| "<option>o0</option><option>o1</option></select>"); |
| EXPECT_FALSE(FirstSelectIsConnectedAfterSelectMultiple(Vector<int>{0})) |
| << "Onchange handler should be executed."; |
| } |
| |
| // 1+ old selected options -> more selected options |
| { |
| SetHtmlInnerHTML( |
| "<select multiple onchange='this.remove();'>" |
| "<option>o0</option><option selected>o1</option></select>"); |
| EXPECT_FALSE(FirstSelectIsConnectedAfterSelectMultiple(Vector<int>{0, 1})) |
| << "Onchange handler should be executed."; |
| } |
| |
| // 1+ old selected options -> 0 selected options |
| { |
| SetHtmlInnerHTML( |
| "<select multiple onchange='this.remove();'>" |
| "<option>o0</option><option selected>o1</option></select>"); |
| EXPECT_FALSE(FirstSelectIsConnectedAfterSelectMultiple(Vector<int>{})) |
| << "Onchange handler should be executed."; |
| } |
| |
| // Multiple old selected options -> less selected options |
| { |
| SetHtmlInnerHTML( |
| "<select multiple onchange='this.remove();'>" |
| "<option selected>o0</option><option selected>o1</option></select>"); |
| EXPECT_FALSE(FirstSelectIsConnectedAfterSelectMultiple(Vector<int>{1})) |
| << "Onchange handler should be executed."; |
| } |
| |
| // Check if the label is correctly updated. |
| { |
| SetHtmlInnerHTML( |
| "<select multiple>" |
| "<option selected>o0</option><option selected>o1</option></select>"); |
| EXPECT_EQ("2 selected", MenuListLabel()); |
| EXPECT_TRUE(FirstSelectIsConnectedAfterSelectMultiple(Vector<int>{1})); |
| EXPECT_EQ("o1", MenuListLabel()); |
| } |
| } |
| |
| TEST_F(HTMLSelectElementTest, IntrinsicInlineSizeOverflow) { |
| // crbug.com/1068338 |
| // This test passes if UBSAN doesn't complain. |
| SetHtmlInnerHTML( |
| "<select style='word-spacing:1073741824em;'>" |
| "<option>abc def</option></select>"); |
| GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest); |
| } |
| |
| TEST_F(HTMLSelectElementTest, AddingNotOwnedOption) { |
| // crbug.com/1077556 |
| auto& doc = GetDocument(); |
| SetHtmlInnerHTML("<select>"); |
| auto* select = To<HTMLSelectElement>(doc.body()->firstChild()); |
| // Append <div><optgroup></optgroup></div> to the SELECT. |
| // We can't do it with the HTML parser. |
| auto* optgroup = doc.CreateRawElement(html_names::kOptgroupTag); |
| select->appendChild(doc.CreateRawElement(html_names::kDivTag)) |
| ->appendChild(optgroup); |
| optgroup->appendChild(doc.CreateRawElement(html_names::kOptionTag)); |
| // This test passes if the above appendChild() doesn't cause a DCHECK failure. |
| } |
| |
| } // namespace blink |