summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/core/html/forms
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-07-16 11:45:35 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-07-17 08:59:23 +0000
commit552906b0f222c5d5dd11b9fd73829d510980461a (patch)
tree3a11e6ed0538a81dd83b20cf3a4783e297f26d91 /chromium/third_party/blink/renderer/core/html/forms
parent1b05827804eaf047779b597718c03e7d38344261 (diff)
downloadqtwebengine-chromium-552906b0f222c5d5dd11b9fd73829d510980461a.tar.gz
BASELINE: Update Chromium to 83.0.4103.122
Change-Id: Ie3a82f5bb0076eec2a7c6a6162326b4301ee291e Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/third_party/blink/renderer/core/html/forms')
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/DEPS4
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/base_button_input_type.cc4
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/base_button_input_type.h1
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/base_temporal_input_type.cc16
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/base_temporal_input_type.h3
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/base_text_input_type.cc12
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/clear_button_element.h6
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/color_chooser.h4
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/color_chooser_client.h3
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.cc58
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.h10
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/color_chooser_ui_controller.cc18
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/color_chooser_ui_controller.h8
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/color_input_type.cc29
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/color_input_type.h2
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/date_input_type.cc4
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/date_input_type.h1
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/date_time_chooser_impl.cc24
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/date_time_chooser_impl.h1
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/date_time_edit_element.cc9
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/date_time_edit_element.h16
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/date_time_field_element.cc14
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/date_time_field_element.h8
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/date_time_local_input_type.cc4
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/date_time_local_input_type.h1
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.cc5
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.h3
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/external_date_time_chooser.cc21
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/external_date_time_chooser.h8
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/external_date_time_chooser_test.cc13
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/external_popup_menu.cc4
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/external_popup_menu_test.cc38
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/file_input_type.cc98
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/file_input_type.h6
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/file_input_type_test.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/form_controller.cc17
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/form_controller_test.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/form_data.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/form_data.idl3
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/form_data_event.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/form_data_event.idl5
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/hidden_input_type.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_button_element.cc64
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_button_element.h7
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_data_list_element.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_data_list_element.h3
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_data_list_options_collection.h12
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_field_set_element.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_form_control_element.cc23
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_form_control_element.h23
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_form_control_element_test.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.cc12
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.h16
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_form_controls_collection.h5
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_form_element.cc218
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_form_element.h23
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_input_element.cc210
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_input_element.h55
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_input_element.idl2
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_input_element_test.cc24
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_label_element.cc14
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_opt_group_element.cc24
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_opt_group_element.h2
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_option_element.cc29
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_option_element.h9
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_option_element.idl8
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_options_collection.cc6
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_options_collection.h17
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_select_element.cc1211
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_select_element.h86
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_select_element_test.cc235
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_text_area_element.cc43
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/html_text_area_element.h1
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/image_input_type.cc28
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/image_input_type.h4
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/input_type.cc111
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/input_type.h21
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/input_type_view.cc39
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/input_type_view.h19
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc13
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/internal_popup_menu.h3
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/internal_popup_menu_test.cc11
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/labels_node_list.h1
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/listed_element.cc15
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/menu_list_inner_element.cc76
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/menu_list_inner_element.h21
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/month_input_type.cc5
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/month_input_type.h1
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.cc75
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.h17
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/number_input_type.cc11
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/option_list.cc3
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/option_list.h10
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/option_list_test.cc8
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/password_input_type_test.cc14
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/picker_indicator_element.cc32
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/picker_indicator_element.h18
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/radio_button_group_scope.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/radio_button_group_scope.h1
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/radio_input_type.cc82
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/radio_input_type.h2
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/range_input_type.cc19
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/range_input_type.h2
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/resources/PRESUBMIT.py10
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js928
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/resources/calendar_picker_refresh.css120
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/resources/colorSuggestionPicker.css10
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/resources/color_picker.css68
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/resources/color_picker.js273
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/resources/color_picker_common.js3
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/resources/datetimelocal_picker.js94
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/resources/listPicker.css10
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/resources/listPicker.js4
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/resources/month_picker.js107
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/resources/suggestionPicker.css4
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/resources/suggestionPicker.js3
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/resources/time_picker.css72
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/resources/time_picker.js421
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/search_input_type.cc9
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/search_input_type.h2
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/select_type.cc1252
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/select_type.h84
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc36
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/slider_thumb_element.h1
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/spin_button_element.cc16
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/step_range.cc14
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/step_range.h4
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/step_range_test.cc33
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/submit_event.cc32
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/submit_event.h33
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/submit_event.idl12
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/submit_event_init.idl9
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/text_control_element.cc18
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/text_control_element.h12
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/text_control_element_test.cc5
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/text_control_inner_elements.cc33
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/text_control_inner_elements.h9
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/text_field_input_type.cc48
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/text_field_input_type.h4
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/text_input_type.cc4
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/time_input_type.cc15
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/time_input_type.h3
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/type_ahead_test.cc2
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/week_input_type.cc5
-rw-r--r--chromium/third_party/blink/renderer/core/html/forms/week_input_type.h1
145 files changed, 4872 insertions, 2494 deletions
diff --git a/chromium/third_party/blink/renderer/core/html/forms/DEPS b/chromium/third_party/blink/renderer/core/html/forms/DEPS
index 4a90b3a5a0b..95b04a4250f 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/DEPS
+++ b/chromium/third_party/blink/renderer/core/html/forms/DEPS
@@ -1,5 +1,5 @@
specific_include_rules = {
"file_input_type_test.cc": [
"+base/run_loop.h",
- ]
-} \ No newline at end of file
+ ],
+}
diff --git a/chromium/third_party/blink/renderer/core/html/forms/base_button_input_type.cc b/chromium/third_party/blink/renderer/core/html/forms/base_button_input_type.cc
index c4525556389..6fc142b3b06 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/base_button_input_type.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/base_button_input_type.cc
@@ -74,6 +74,10 @@ bool BaseButtonInputType::ShouldSaveAndRestoreFormControlState() const {
void BaseButtonInputType::AppendToFormData(FormData&) const {}
+bool BaseButtonInputType::TypeShouldForceLegacyLayout() const {
+ return true;
+}
+
LayoutObject* BaseButtonInputType::CreateLayoutObject(const ComputedStyle&,
LegacyLayout) const {
return new LayoutButton(&GetElement());
diff --git a/chromium/third_party/blink/renderer/core/html/forms/base_button_input_type.h b/chromium/third_party/blink/renderer/core/html/forms/base_button_input_type.h
index e0310279d72..b048c471560 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/base_button_input_type.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/base_button_input_type.h
@@ -54,6 +54,7 @@ class BaseButtonInputType : public InputType,
InputTypeView* CreateView() override;
bool ShouldSaveAndRestoreFormControlState() const override;
void AppendToFormData(FormData&) const override;
+ bool TypeShouldForceLegacyLayout() const override;
LayoutObject* CreateLayoutObject(const ComputedStyle&,
LegacyLayout) const override;
ValueMode GetValueMode() const override;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/base_temporal_input_type.cc b/chromium/third_party/blink/renderer/core/html/forms/base_temporal_input_type.cc
index 4e007f8fc74..ad6d6a6e29a 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/base_temporal_input_type.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/base_temporal_input_type.cc
@@ -104,6 +104,16 @@ String BaseTemporalInputType::RangeUnderflowText(const Decimal& minimum) const {
LocalizeValue(Serialize(minimum)));
}
+String BaseTemporalInputType::RangeInvalidText(const Decimal& minimum,
+ const Decimal& maximum) const {
+ DCHECK(minimum > maximum)
+ << "RangeInvalidText should only be called with minimum>maximum";
+
+ return GetLocale().QueryString(IDS_FORM_VALIDATION_RANGE_INVALID_DATETIME,
+ LocalizeValue(Serialize(minimum)),
+ LocalizeValue(Serialize(maximum)));
+}
+
Decimal BaseTemporalInputType::DefaultValueForStepUp() const {
return Decimal::FromDouble(
ConvertToLocalTime(base::Time::Now()).InMillisecondsF());
@@ -190,7 +200,11 @@ bool BaseTemporalInputType::ShouldRespectListAttribute() {
}
bool BaseTemporalInputType::ValueMissing(const String& value) const {
- return GetElement().IsRequired() && value.IsEmpty();
+ // For text-mode input elements (including dates), the value is missing only
+ // if it is mutable.
+ // https://html.spec.whatwg.org/multipage/input.html#the-required-attribute
+ return GetElement().IsRequired() && value.IsEmpty() &&
+ !GetElement().IsDisabledOrReadOnly();
}
bool BaseTemporalInputType::MayTriggerVirtualKeyboard() const {
diff --git a/chromium/third_party/blink/renderer/core/html/forms/base_temporal_input_type.h b/chromium/third_party/blink/renderer/core/html/forms/base_temporal_input_type.h
index 13359fabe8b..b1e6cafa63e 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/base_temporal_input_type.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/base_temporal_input_type.h
@@ -69,6 +69,7 @@ class BaseTemporalInputType : public InputType {
bool has_hour,
bool has_minute,
bool has_second) const = 0;
+ virtual String AriaRoleForPickerIndicator() const = 0;
protected:
BaseTemporalInputType(HTMLInputElement& element) : InputType(element) {}
@@ -96,6 +97,8 @@ class BaseTemporalInputType : public InputType {
bool ValueMissing(const String&) const override;
String RangeOverflowText(const Decimal& maximum) const override;
String RangeUnderflowText(const Decimal& minimum) const override;
+ String RangeInvalidText(const Decimal& minimum,
+ const Decimal& maximum) const override;
Decimal DefaultValueForStepUp() const override;
bool IsSteppable() const override;
virtual String SerializeWithDate(const base::Optional<base::Time>&) const;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/base_text_input_type.cc b/chromium/third_party/blink/renderer/core/html/forms/base_text_input_type.cc
index 8fdc4798ee3..0f838dc1992 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/base_text_input_type.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/base_text_input_type.cc
@@ -28,6 +28,7 @@
#include "third_party/blink/renderer/core/html/forms/html_input_element.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
namespace blink {
@@ -88,11 +89,12 @@ bool BaseTextInputType::PatternMismatch(const String& value) const {
ScriptRegexp::UTF16));
if (!raw_regexp->IsValid()) {
GetElement().GetDocument().AddConsoleMessage(
- ConsoleMessage::Create(mojom::ConsoleMessageSource::kRendering,
- mojom::ConsoleMessageLevel::kError,
- "Pattern attribute value " + raw_pattern +
- " is not a valid regular expression: " +
- raw_regexp->ExceptionMessage()));
+ MakeGarbageCollected<ConsoleMessage>(
+ mojom::ConsoleMessageSource::kRendering,
+ mojom::ConsoleMessageLevel::kError,
+ "Pattern attribute value " + raw_pattern +
+ " is not a valid regular expression: " +
+ raw_regexp->ExceptionMessage()));
regexp_.reset(raw_regexp.release());
pattern_for_regexp_ = raw_pattern;
return false;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/clear_button_element.h b/chromium/third_party/blink/renderer/core/html/forms/clear_button_element.h
index 1afa8214130..bcedfe36f09 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/clear_button_element.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/clear_button_element.h
@@ -56,12 +56,6 @@ class ClearButtonElement final : public HTMLDivElement {
Member<ClearButtonOwner> clear_button_owner_;
};
-DEFINE_TYPE_CASTS(ClearButtonElement,
- Element,
- element,
- element->IsClearButtonElement(),
- element.IsClearButtonElement());
-
template <>
struct DowncastTraits<ClearButtonElement> {
static bool AllowFrom(const Element& element) {
diff --git a/chromium/third_party/blink/renderer/core/html/forms/color_chooser.h b/chromium/third_party/blink/renderer/core/html/forms/color_chooser.h
index 87bdbc0d9a5..051e20a4bdd 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/color_chooser.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/color_chooser.h
@@ -38,13 +38,17 @@ namespace blink {
class AXObject;
class Color;
+// This interface respresents a UI to choose a color.
class CORE_EXPORT ColorChooser : public GarbageCollectedMixin {
public:
ColorChooser();
virtual ~ColorChooser();
void Trace(Visitor* visitor) override {}
+ // Call to update the selection in the UI. Used to reflect value changes made
+ // by JS.
virtual void SetSelectedColor(const Color&) {}
+ // Call to close the UI.
virtual void EndChooser() {}
// Returns a root AXObject in the ColorChooser if it's available.
virtual AXObject* RootAXObject() = 0;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/color_chooser_client.h b/chromium/third_party/blink/renderer/core/html/forms/color_chooser_client.h
index 828b2cfba30..292e216ff7a 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/color_chooser_client.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/color_chooser_client.h
@@ -42,12 +42,15 @@ namespace blink {
class Element;
+// This class is the client for the ColorChooser.
class CORE_EXPORT ColorChooserClient : public GarbageCollectedMixin {
public:
virtual ~ColorChooserClient();
void Trace(Visitor* visitor) override {}
+ // Called when a color is chosen by the user in the ColorChooser UI.
virtual void DidChooseColor(const Color&) = 0;
+ // Called when ColorChooser UI was closed by the user.
virtual void DidEndChooser() = 0;
virtual Element& OwnerElement() const = 0;
virtual IntRect ElementRectRelativeToViewport() const = 0;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.cc b/chromium/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.cc
index 4d48b286df1..f61b77e16fe 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.cc
@@ -25,6 +25,8 @@
#include "third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.h"
+#include "build/build_config.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/strings/grit/blink_strings.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
@@ -32,8 +34,10 @@
#include "third_party/blink/renderer/core/html/forms/chooser_resource_loader.h"
#include "third_party/blink/renderer/core/html/forms/color_chooser_client.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
+#include "third_party/blink/renderer/core/page/color_page_popup_controller.h"
#include "third_party/blink/renderer/core/page/page_popup.h"
#include "third_party/blink/renderer/platform/geometry/int_rect.h"
+#include "ui/base/ui_base_features.h"
namespace blink {
@@ -53,12 +57,8 @@ ColorChooserPopupUIController::ColorChooserPopupUIController(
popup_(nullptr),
locale_(Locale::DefaultLocale()) {}
-ColorChooserPopupUIController::~ColorChooserPopupUIController() = default;
-
-void ColorChooserPopupUIController::Dispose() {
- // Finalized earlier so as to access chrome_client_ while alive.
- CancelPopup();
- // ~ColorChooserUIController calls EndChooser().
+ColorChooserPopupUIController::~ColorChooserPopupUIController() {
+ DCHECK(!popup_);
}
void ColorChooserPopupUIController::Trace(Visitor* visitor) {
@@ -68,7 +68,7 @@ void ColorChooserPopupUIController::Trace(Visitor* visitor) {
void ColorChooserPopupUIController::OpenUI() {
if (client_->ShouldShowSuggestions() ||
- RuntimeEnabledFeatures::FormControlsRefreshEnabled())
+ features::IsFormControlsRefreshEnabled())
OpenPopup();
else
OpenColorChooser();
@@ -93,7 +93,7 @@ void ColorChooserPopupUIController::WriteDocument(SharedBuffer* data) {
void ColorChooserPopupUIController::WriteColorPickerDocument(
SharedBuffer* data) {
- DCHECK(RuntimeEnabledFeatures::FormControlsRefreshEnabled());
+ DCHECK(features::IsFormControlsRefreshEnabled());
IntRect anchor_rect_in_screen = chrome_client_->ViewportToScreen(
client_->ElementRectRelativeToViewport(), frame_->View());
@@ -102,7 +102,6 @@ void ColorChooserPopupUIController::WriteColorPickerDocument(
"<!DOCTYPE html><head><meta charset='UTF-8'><style>\n", data);
data->Append(ChooserResourceLoader::GetPickerCommonStyleSheet());
data->Append(ChooserResourceLoader::GetColorPickerStyleSheet());
-
PagePopupClient::AddString(
"</style></head><body>\n"
"<div id='main'>Loading...</div><script>\n"
@@ -113,6 +112,11 @@ void ColorChooserPopupUIController::WriteColorPickerDocument(
AddProperty("anchorRectInScreen", anchor_rect_in_screen, data);
AddProperty("zoomFactor", ScaledZoomFactor(), data);
AddProperty("shouldShowColorSuggestionPicker", false, data);
+ AddProperty("isEyeDropperEnabled", features::IsEyeDropperEnabled(), data);
+#if defined(OS_MACOSX)
+ AddProperty("isBorderTransparent", features::IsFormControlsRefreshEnabled(),
+ data);
+#endif
PagePopupClient::AddString("};\n", data);
data->Append(ChooserResourceLoader::GetPickerCommonJS());
data->Append(ChooserResourceLoader::GetColorPickerJS());
@@ -134,9 +138,8 @@ void ColorChooserPopupUIController::WriteColorSuggestionPickerDocument(
"<!DOCTYPE html><head><meta charset='UTF-8'><style>\n", data);
data->Append(ChooserResourceLoader::GetPickerCommonStyleSheet());
data->Append(ChooserResourceLoader::GetColorSuggestionPickerStyleSheet());
- if (RuntimeEnabledFeatures::FormControlsRefreshEnabled())
+ if (features::IsFormControlsRefreshEnabled())
data->Append(ChooserResourceLoader::GetColorPickerStyleSheet());
-
PagePopupClient::AddString(
"</style></head><body>\n"
"<div id='main'>Loading...</div><script>\n"
@@ -146,7 +149,7 @@ void ColorChooserPopupUIController::WriteColorSuggestionPickerDocument(
PagePopupClient::AddProperty(
"otherColorLabel", GetLocale().QueryString(IDS_FORM_OTHER_COLOR_LABEL),
data);
- if (RuntimeEnabledFeatures::FormControlsRefreshEnabled()) {
+ if (features::IsFormControlsRefreshEnabled()) {
PagePopupClient::AddProperty("selectedColor",
client_->CurrentColor().Serialized(), data);
}
@@ -154,11 +157,16 @@ void ColorChooserPopupUIController::WriteColorSuggestionPickerDocument(
AddProperty("zoomFactor", ScaledZoomFactor(), data);
AddProperty("shouldShowColorSuggestionPicker", true, data);
AddProperty("isFormControlsRefreshEnabled",
- RuntimeEnabledFeatures::FormControlsRefreshEnabled(), data);
+ features::IsFormControlsRefreshEnabled(), data);
+ AddProperty("isEyeDropperEnabled", features::IsEyeDropperEnabled(), data);
+#if defined(OS_MACOSX)
+ AddProperty("isBorderTransparent", features::IsFormControlsRefreshEnabled(),
+ data);
+#endif
PagePopupClient::AddString("};\n", data);
data->Append(ChooserResourceLoader::GetPickerCommonJS());
data->Append(ChooserResourceLoader::GetColorSuggestionPickerJS());
- if (RuntimeEnabledFeatures::FormControlsRefreshEnabled())
+ if (features::IsFormControlsRefreshEnabled())
data->Append(ChooserResourceLoader::GetColorPickerJS());
data->Append(ChooserResourceLoader::GetColorPickerCommonJS());
PagePopupClient::AddString("</script></body>\n", data);
@@ -176,7 +184,7 @@ void ColorChooserPopupUIController::SetValueAndClosePopup(
if (num_value == kColorPickerPopupActionSetValue)
SetValue(string_value);
if (num_value == kColorPickerPopupActionChooseOtherColor) {
- DCHECK(!RuntimeEnabledFeatures::FormControlsRefreshEnabled());
+ DCHECK(!features::IsFormControlsRefreshEnabled());
OpenColorChooser();
}
CancelPopup();
@@ -216,4 +224,24 @@ void ColorChooserPopupUIController::CancelPopup() {
chrome_client_->ClosePagePopup(popup_);
}
+PagePopupController* ColorChooserPopupUIController::CreatePagePopupController(
+ PagePopup& popup) {
+ return MakeGarbageCollected<ColorPagePopupController>(popup, this);
+}
+
+void ColorChooserPopupUIController::EyeDropperResponseHandler(bool success,
+ uint32_t color) {
+ // TODO(crbug.com/992297): update the color popup with the chosen value.
+}
+
+void ColorChooserPopupUIController::OpenEyeDropper() {
+ frame_->GetBrowserInterfaceBroker().GetInterface(
+ eye_dropper_chooser_.BindNewPipeAndPassReceiver());
+ eye_dropper_chooser_.set_disconnect_handler(WTF::Bind(
+ &ColorChooserPopupUIController::EndChooser, WrapWeakPersistent(this)));
+ eye_dropper_chooser_->Choose(
+ WTF::Bind(&ColorChooserPopupUIController::EyeDropperResponseHandler,
+ WrapWeakPersistent(this)));
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.h b/chromium/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.h
index 736b8697680..fd20189266e 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/color_chooser_popup_ui_controller.h
@@ -35,12 +35,11 @@ namespace blink {
class ChromeClient;
class ColorChooserClient;
class PagePopup;
+class PagePopupController;
class CORE_EXPORT ColorChooserPopupUIController final
: public ColorChooserUIController,
public PagePopupClient {
- USING_PRE_FINALIZER(ColorChooserPopupUIController, Dispose);
-
public:
ColorChooserPopupUIController(LocalFrame*,
ChromeClient*,
@@ -57,19 +56,21 @@ class CORE_EXPORT ColorChooserPopupUIController final
// PagePopupClient functions:
void WriteDocument(SharedBuffer*) override;
- void SelectFontsFromOwnerDocument(Document&) override {}
Locale& GetLocale() override;
void SetValueAndClosePopup(int, const String&) override;
void SetValue(const String&) override;
void CancelPopup() override;
Element& OwnerElement() override;
void DidClosePopup() override;
+ PagePopupController* CreatePagePopupController(PagePopup&) override;
+
+ void OpenEyeDropper();
+ void EyeDropperResponseHandler(bool success, uint32_t color);
private:
ChromeClient& GetChromeClient() override;
void OpenPopup();
- void Dispose();
void WriteColorPickerDocument(SharedBuffer*);
void WriteColorSuggestionPickerDocument(SharedBuffer*);
@@ -77,6 +78,7 @@ class CORE_EXPORT ColorChooserPopupUIController final
Member<ChromeClient> chrome_client_;
PagePopup* popup_;
Locale& locale_;
+ mojo::Remote<mojom::blink::EyeDropperChooser> eye_dropper_chooser_;
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/color_chooser_ui_controller.cc b/chromium/third_party/blink/renderer/core/html/forms/color_chooser_ui_controller.cc
index bd31182d982..8bce77ac6c3 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/color_chooser_ui_controller.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/color_chooser_ui_controller.cc
@@ -27,6 +27,7 @@
#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
#include "third_party/blink/public/web/web_local_frame_client.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
#include "third_party/blink/renderer/core/html/forms/color_chooser_client.h"
#include "third_party/blink/renderer/platform/graphics/color.h"
@@ -37,20 +38,19 @@ namespace blink {
ColorChooserUIController::ColorChooserUIController(
LocalFrame* frame,
blink::ColorChooserClient* client)
- : client_(client), frame_(frame) {}
+ : client_(client),
+ frame_(frame),
+ receiver_(this, frame->DomWindow()->GetExecutionContext()) {}
-ColorChooserUIController::~ColorChooserUIController() {}
+ColorChooserUIController::~ColorChooserUIController() = default;
void ColorChooserUIController::Trace(Visitor* visitor) {
+ visitor->Trace(receiver_);
visitor->Trace(frame_);
visitor->Trace(client_);
ColorChooser::Trace(visitor);
}
-void ColorChooserUIController::Dispose() {
- receiver_.reset();
-}
-
void ColorChooserUIController::OpenUI() {
OpenColorChooser();
}
@@ -80,8 +80,10 @@ void ColorChooserUIController::OpenColorChooser() {
color_chooser_factory_.BindNewPipeAndPassReceiver());
color_chooser_factory_->OpenColorChooser(
chooser_.BindNewPipeAndPassReceiver(),
- receiver_.BindNewPipeAndPassRemote(), client_->CurrentColor().Rgb(),
- client_->Suggestions());
+ receiver_.BindNewPipeAndPassRemote(
+ frame_->DomWindow()->GetExecutionContext()->GetTaskRunner(
+ TaskType::kUserInteraction)),
+ client_->CurrentColor().Rgb(), client_->Suggestions());
receiver_.set_disconnect_handler(WTF::Bind(
&ColorChooserUIController::EndChooser, WrapWeakPersistent(this)));
}
diff --git a/chromium/third_party/blink/renderer/core/html/forms/color_chooser_ui_controller.h b/chromium/third_party/blink/renderer/core/html/forms/color_chooser_ui_controller.h
index 6d0012ac143..69168e082da 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/color_chooser_ui_controller.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/color_chooser_ui_controller.h
@@ -27,12 +27,13 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_COLOR_CHOOSER_UI_CONTROLLER_H_
#include <memory>
-#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "third_party/blink/public/mojom/choosers/color_chooser.mojom-blink.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/html/forms/color_chooser.h"
#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_receiver.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h"
#include "third_party/blink/renderer/platform/text/platform_locale.h"
namespace blink {
@@ -45,7 +46,6 @@ class CORE_EXPORT ColorChooserUIController
public mojom::blink::ColorChooserClient,
public ColorChooser {
USING_GARBAGE_COLLECTED_MIXIN(ColorChooserUIController);
- USING_PRE_FINALIZER(ColorChooserUIController, Dispose);
public:
ColorChooserUIController(LocalFrame*, blink::ColorChooserClient*);
@@ -73,7 +73,9 @@ class CORE_EXPORT ColorChooserUIController
private:
mojo::Remote<mojom::blink::ColorChooserFactory> color_chooser_factory_;
- mojo::Receiver<mojom::blink::ColorChooserClient> receiver_{this};
+ HeapMojoReceiver<mojom::blink::ColorChooserClient,
+ HeapMojoWrapperMode::kWithoutContextObserver>
+ receiver_;
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/color_input_type.cc b/chromium/third_party/blink/renderer/core/html/forms/color_input_type.cc
index 60a03ab4a07..75a2290469c 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/color_input_type.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/color_input_type.cc
@@ -45,6 +45,7 @@
#include "third_party/blink/renderer/core/html/forms/html_option_element.h"
#include "third_party/blink/renderer/core/html/html_div_element.h"
#include "third_party/blink/renderer/core/input_type_names.h"
+#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/layout/layout_theme.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
@@ -52,6 +53,7 @@
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "ui/base/ui_base_features.h"
namespace blink {
@@ -150,7 +152,7 @@ void ColorInputType::HandleDOMActivateEvent(Event& event) {
return;
ChromeClient* chrome_client = GetChromeClient();
- if (chrome_client && !chooser_) {
+ if (chrome_client && !HasOpenedPopup()) {
UseCounter::Count(
document,
(event.UnderlyingEvent() && event.UnderlyingEvent()->isTrusted())
@@ -158,13 +160,23 @@ void ColorInputType::HandleDOMActivateEvent(Event& event) {
: WebFeature::kColorInputTypeChooserByUntrustedClick);
chooser_ = chrome_client->OpenColorChooser(document.GetFrame(), this,
ValueAsColor());
+ if (::features::IsFormControlsRefreshEnabled() &&
+ GetElement().GetLayoutObject()) {
+ // Invalidate paint to ensure that the focus ring is removed.
+ GetElement().GetLayoutObject()->SetShouldDoFullPaintInvalidation();
+ }
}
event.SetDefaultHandled();
}
void ColorInputType::ClosePopupView() {
- EndColorChooser();
+ if (chooser_)
+ chooser_->EndChooser();
+}
+
+bool ColorInputType::HasOpenedPopup() const {
+ return chooser_;
}
bool ColorInputType::ShouldRespectListAttribute() {
@@ -176,7 +188,7 @@ bool ColorInputType::TypeMismatchFor(const String& value) const {
}
void ColorInputType::WarnIfValueIsInvalid(const String& value) const {
- if (!DeprecatedEqualIgnoringCase(value, GetElement().SanitizeValue(value)))
+ if (!EqualIgnoringASCIICase(value, GetElement().SanitizeValue(value)))
AddWarningToConsole(
"The specified value %s does not conform to the required format. The "
"format is \"#rrggbb\" where rr, gg, bb are two-digit hexadecimal "
@@ -203,11 +215,11 @@ void ColorInputType::DidEndChooser() {
if (LayoutTheme::GetTheme().IsModalColorChooser())
GetElement().EnqueueChangeEvent();
chooser_.Clear();
-}
-
-void ColorInputType::EndColorChooser() {
- if (chooser_)
- chooser_->EndChooser();
+ if (::features::IsFormControlsRefreshEnabled() &&
+ GetElement().GetLayoutObject()) {
+ // Invalidate paint to ensure that the focus ring is shown.
+ GetElement().GetLayoutObject()->SetShouldDoFullPaintInvalidation();
+ }
}
void ColorInputType::UpdateView() {
@@ -275,4 +287,5 @@ ColorChooserClient* ColorInputType::GetColorChooserClient() {
return this;
}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/color_input_type.h b/chromium/third_party/blink/renderer/core/html/forms/color_input_type.h
index 19cfe68ddb0..2e75cb058aa 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/color_input_type.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/color_input_type.h
@@ -72,6 +72,7 @@ class ColorInputType final : public InputType,
void DidSetValue(const String&, bool value_changed) override;
void HandleDOMActivateEvent(Event&) override;
void ClosePopupView() override;
+ bool HasOpenedPopup() const override;
bool ShouldRespectListAttribute() override;
bool TypeMismatchFor(const String&) const override;
void WarnIfValueIsInvalid(const String&) const override;
@@ -79,7 +80,6 @@ class ColorInputType final : public InputType,
AXObject* PopupRootAXObject() override;
Color ValueAsColor() const;
- void EndColorChooser();
HTMLElement* ShadowColorSwatch() const;
Member<ColorChooser> chooser_;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/date_input_type.cc b/chromium/third_party/blink/renderer/core/html/forms/date_input_type.cc
index 4eb86fe8704..b0008e357be 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/date_input_type.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/date_input_type.cc
@@ -134,4 +134,8 @@ bool DateInputType::IsValidFormat(bool has_year,
return has_year && has_month && has_day;
}
+String DateInputType::AriaRoleForPickerIndicator() const {
+ return GetLocale().QueryString(IDS_AX_CALENDAR_SHOW_DATE_PICKER);
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/date_input_type.h b/chromium/third_party/blink/renderer/core/html/forms/date_input_type.h
index 24bac539a94..e2758b1e561 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/date_input_type.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/date_input_type.h
@@ -60,6 +60,7 @@ class DateInputType final : public BaseTemporalInputType {
bool has_hour,
bool has_minute,
bool has_second) const override;
+ String AriaRoleForPickerIndicator() const override;
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/date_time_chooser_impl.cc b/chromium/third_party/blink/renderer/core/html/forms/date_time_chooser_impl.cc
index ab96006c6cd..beb631c63c6 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/date_time_chooser_impl.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/date_time_chooser_impl.cc
@@ -30,6 +30,7 @@
#include "third_party/blink/renderer/core/html/forms/date_time_chooser_impl.h"
+#include "build/build_config.h"
#include "third_party/blink/public/mojom/choosers/date_time_chooser.mojom-blink.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/strings/grit/blink_strings.h"
@@ -46,6 +47,7 @@
#include "third_party/blink/renderer/platform/language.h"
#include "third_party/blink/renderer/platform/text/date_components.h"
#include "third_party/blink/renderer/platform/text/platform_locale.h"
+#include "ui/base/ui_base_features.h"
namespace blink {
@@ -124,11 +126,11 @@ void DateTimeChooserImpl::WriteDocument(SharedBuffer* data) {
AddString("<!DOCTYPE html><head><meta charset='UTF-8'><style>\n", data);
data->Append(ChooserResourceLoader::GetPickerCommonStyleSheet());
- if (!RuntimeEnabledFeatures::FormControlsRefreshEnabled())
+ if (!features::IsFormControlsRefreshEnabled())
data->Append(ChooserResourceLoader::GetPickerButtonStyleSheet());
data->Append(ChooserResourceLoader::GetSuggestionPickerStyleSheet());
data->Append(ChooserResourceLoader::GetCalendarPickerStyleSheet());
- if (RuntimeEnabledFeatures::FormControlsRefreshEnabled()) {
+ if (features::IsFormControlsRefreshEnabled()) {
data->Append(ChooserResourceLoader::GetCalendarPickerRefreshStyleSheet());
if (parameters_->type == input_type_names::kTime ||
parameters_->type == input_type_names::kDatetimeLocal) {
@@ -168,6 +170,16 @@ void DateTimeChooserImpl::WriteDocument(SharedBuffer* data) {
AddProperty("axShowPreviousMonth",
GetLocale().QueryString(IDS_AX_CALENDAR_SHOW_PREVIOUS_MONTH),
data);
+ AddProperty("axHourLabel", GetLocale().QueryString(IDS_AX_HOUR_FIELD_TEXT),
+ data);
+ AddProperty("axMinuteLabel",
+ GetLocale().QueryString(IDS_AX_MINUTE_FIELD_TEXT), data);
+ AddProperty("axSecondLabel",
+ GetLocale().QueryString(IDS_AX_SECOND_FIELD_TEXT), data);
+ AddProperty("axMillisecondLabel",
+ GetLocale().QueryString(IDS_AX_MILLISECOND_FIELD_TEXT), data);
+ AddProperty("axAmPmLabel", GetLocale().QueryString(IDS_AX_AM_PM_FIELD_TEXT),
+ data);
AddProperty("weekStartDay", locale_->FirstDayOfWeek(), data);
AddProperty("shortMonthLabels", locale_->ShortMonthLabels(), data);
AddProperty("dayLabels", locale_->WeekDayShortLabels(), data);
@@ -175,7 +187,11 @@ void DateTimeChooserImpl::WriteDocument(SharedBuffer* data) {
AddProperty("isLocaleRTL", locale_->IsRTL(), data);
AddProperty("isRTL", parameters_->is_anchor_element_rtl, data);
AddProperty("isFormControlsRefreshEnabled",
- RuntimeEnabledFeatures::FormControlsRefreshEnabled(), data);
+ features::IsFormControlsRefreshEnabled(), data);
+#if defined(OS_MACOSX)
+ AddProperty("isBorderTransparent", features::IsFormControlsRefreshEnabled(),
+ data);
+#endif
AddProperty("mode", parameters_->type.GetString(), data);
AddProperty("isAMPMFirst", parameters_->is_ampm_first, data);
AddProperty("hasAMPM", parameters_->has_ampm, data);
@@ -224,7 +240,7 @@ void DateTimeChooserImpl::WriteDocument(SharedBuffer* data) {
data->Append(ChooserResourceLoader::GetPickerCommonJS());
data->Append(ChooserResourceLoader::GetSuggestionPickerJS());
- if (RuntimeEnabledFeatures::FormControlsRefreshEnabled()) {
+ if (features::IsFormControlsRefreshEnabled()) {
data->Append(ChooserResourceLoader::GetMonthPickerJS());
if (parameters_->type == input_type_names::kTime) {
data->Append(ChooserResourceLoader::GetTimePickerJS());
diff --git a/chromium/third_party/blink/renderer/core/html/forms/date_time_chooser_impl.h b/chromium/third_party/blink/renderer/core/html/forms/date_time_chooser_impl.h
index 1f9602acd70..22bca47ad27 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/date_time_chooser_impl.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/date_time_chooser_impl.h
@@ -60,7 +60,6 @@ class CORE_EXPORT DateTimeChooserImpl final : public DateTimeChooser,
private:
// PagePopupClient functions:
void WriteDocument(SharedBuffer*) override;
- void SelectFontsFromOwnerDocument(Document&) override {}
Locale& GetLocale() override;
void SetValueAndClosePopup(int, const String&) override;
void SetValue(const String&) override;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/date_time_edit_element.cc b/chromium/third_party/blink/renderer/core/html/forms/date_time_edit_element.cc
index 3e0d42db81c..a22fc3b2088 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/date_time_edit_element.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/date_time_edit_element.cc
@@ -75,7 +75,7 @@ class DateTimeEditBuilder : private DateTimeFormat::TokenHandler {
DateTimeEditElement& EditElement() const;
- Member<DateTimeEditElement> edit_element_;
+ DateTimeEditElement* edit_element_;
const DateComponents date_value_;
const DateTimeEditElement::LayoutParameters& parameters_;
DateTimeNumericFieldElement::Range day_range_;
@@ -574,8 +574,7 @@ void DateTimeEditElement::BlurByOwner() {
scoped_refptr<ComputedStyle> DateTimeEditElement::CustomStyleForLayoutObject() {
// FIXME: This is a kind of layout. We might want to introduce new
// layoutObject.
- scoped_refptr<ComputedStyle> original_style = OriginalStyleForLayoutObject();
- scoped_refptr<ComputedStyle> style = ComputedStyle::Clone(*original_style);
+ scoped_refptr<ComputedStyle> style = OriginalStyleForLayoutObject();
float width = 0;
for (Node* child = FieldsWrapperElement()->firstChild(); child;
child = child->nextSibling()) {
@@ -598,12 +597,12 @@ scoped_refptr<ComputedStyle> DateTimeEditElement::CustomStyleForLayoutObject() {
return style;
}
-void DateTimeEditElement::DidBlurFromField(WebFocusType focus_type) {
+void DateTimeEditElement::DidBlurFromField(mojom::blink::FocusType focus_type) {
if (edit_control_owner_)
edit_control_owner_->DidBlurFromControl(focus_type);
}
-void DateTimeEditElement::DidFocusOnField(WebFocusType focus_type) {
+void DateTimeEditElement::DidFocusOnField(mojom::blink::FocusType focus_type) {
if (edit_control_owner_)
edit_control_owner_->DidFocusOnControl(focus_type);
}
diff --git a/chromium/third_party/blink/renderer/core/html/forms/date_time_edit_element.h b/chromium/third_party/blink/renderer/core/html/forms/date_time_edit_element.h
index ba1e86be80c..b241d8d00df 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/date_time_edit_element.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/date_time_edit_element.h
@@ -27,7 +27,7 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_DATE_TIME_EDIT_ELEMENT_H_
#include "base/macros.h"
-#include "third_party/blink/public/platform/web_focus_type.h"
+#include "third_party/blink/public/mojom/input/focus_type.mojom-blink-forward.h"
#include "third_party/blink/renderer/core/html/forms/date_time_field_element.h"
#include "third_party/blink/renderer/core/html/forms/step_range.h"
#include "third_party/blink/renderer/platform/text/date_components.h"
@@ -54,8 +54,8 @@ class DateTimeEditElement final : public HTMLDivElement,
class EditControlOwner : public GarbageCollectedMixin {
public:
virtual ~EditControlOwner();
- virtual void DidBlurFromControl(WebFocusType) = 0;
- virtual void DidFocusOnControl(WebFocusType) = 0;
+ virtual void DidBlurFromControl(mojom::blink::FocusType) = 0;
+ virtual void DidFocusOnControl(mojom::blink::FocusType) = 0;
virtual void EditControlValueChanged() = 0;
virtual String FormatDateTimeFieldsState(
const DateTimeFieldsState&) const = 0;
@@ -143,8 +143,8 @@ class DateTimeEditElement final : public HTMLDivElement,
bool IsDateTimeEditElement() const override;
// DateTimeFieldElement::FieldOwner functions.
- void DidBlurFromField(WebFocusType) override;
- void DidFocusOnField(WebFocusType) override;
+ void DidBlurFromField(mojom::blink::FocusType) override;
+ void DidFocusOnField(mojom::blink::FocusType) override;
void FieldValueChanged() override;
bool FocusOnNextField(const DateTimeFieldElement&) override;
bool FocusOnPreviousField(const DateTimeFieldElement&) override;
@@ -159,12 +159,6 @@ class DateTimeEditElement final : public HTMLDivElement,
DISALLOW_COPY_AND_ASSIGN(DateTimeEditElement);
};
-DEFINE_TYPE_CASTS(DateTimeEditElement,
- Element,
- element,
- element->IsDateTimeEditElement(),
- element.IsDateTimeEditElement());
-
template <>
struct DowncastTraits<DateTimeEditElement> {
static bool AllowFrom(const Element& element) {
diff --git a/chromium/third_party/blink/renderer/core/html/forms/date_time_field_element.cc b/chromium/third_party/blink/renderer/core/html/forms/date_time_field_element.cc
index e60c7175803..13dbe846f5b 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/date_time_field_element.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/date_time_field_element.cc
@@ -55,20 +55,19 @@ float DateTimeFieldElement::ComputeTextWidth(const ComputedStyle& style,
}
void DateTimeFieldElement::DefaultEventHandler(Event& event) {
- if (event.IsKeyboardEvent()) {
- auto& keyboard_event = ToKeyboardEvent(event);
+ if (auto* keyboard_event = DynamicTo<KeyboardEvent>(event)) {
if (!IsDisabled() && !IsFieldOwnerDisabled() && !IsFieldOwnerReadOnly()) {
- HandleKeyboardEvent(keyboard_event);
- if (keyboard_event.DefaultHandled()) {
+ HandleKeyboardEvent(*keyboard_event);
+ if (keyboard_event->DefaultHandled()) {
if (field_owner_)
field_owner_->FieldDidChangeValueByKeyboard();
return;
}
}
- DefaultKeyboardEventHandler(keyboard_event);
+ DefaultKeyboardEventHandler(*keyboard_event);
if (field_owner_)
field_owner_->FieldDidChangeValueByKeyboard();
- if (keyboard_event.DefaultHandled())
+ if (keyboard_event->DefaultHandled())
return;
}
@@ -130,7 +129,8 @@ void DateTimeFieldElement::DefaultKeyboardEventHandler(
}
}
-void DateTimeFieldElement::SetFocused(bool value, WebFocusType focus_type) {
+void DateTimeFieldElement::SetFocused(bool value,
+ mojom::blink::FocusType focus_type) {
if (field_owner_) {
if (value) {
field_owner_->DidFocusOnField(focus_type);
diff --git a/chromium/third_party/blink/renderer/core/html/forms/date_time_field_element.h b/chromium/third_party/blink/renderer/core/html/forms/date_time_field_element.h
index ca56250f6d2..80abb99baa9 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/date_time_field_element.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/date_time_field_element.h
@@ -27,7 +27,7 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_DATE_TIME_FIELD_ELEMENT_H_
#include "base/macros.h"
-#include "third_party/blink/public/platform/web_focus_type.h"
+#include "third_party/blink/public/mojom/input/focus_type.mojom-blink-forward.h"
#include "third_party/blink/renderer/core/html/html_div_element.h"
#include "third_party/blink/renderer/core/html/html_span_element.h"
@@ -61,8 +61,8 @@ class DateTimeFieldElement : public HTMLSpanElement {
class FieldOwner : public GarbageCollectedMixin {
public:
virtual ~FieldOwner();
- virtual void DidBlurFromField(WebFocusType) = 0;
- virtual void DidFocusOnField(WebFocusType) = 0;
+ virtual void DidBlurFromField(mojom::blink::FocusType) = 0;
+ virtual void DidFocusOnField(mojom::blink::FocusType) = 0;
virtual void FieldValueChanged() = 0;
virtual bool FocusOnNextField(const DateTimeFieldElement&) = 0;
virtual bool FocusOnPreviousField(const DateTimeFieldElement&) = 0;
@@ -108,7 +108,7 @@ class DateTimeFieldElement : public HTMLSpanElement {
virtual int ValueForARIAValueNow() const;
// Node functions.
- void SetFocused(bool, WebFocusType) override;
+ void SetFocused(bool, mojom::blink::FocusType) override;
private:
void DefaultKeyboardEventHandler(KeyboardEvent&);
diff --git a/chromium/third_party/blink/renderer/core/html/forms/date_time_local_input_type.cc b/chromium/third_party/blink/renderer/core/html/forms/date_time_local_input_type.cc
index 1fb0cb7203c..eb50355f5a2 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/date_time_local_input_type.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/date_time_local_input_type.cc
@@ -191,4 +191,8 @@ bool DateTimeLocalInputType::IsValidFormat(bool has_year,
return has_year && has_month && has_day && has_ampm && has_hour && has_minute;
}
+String DateTimeLocalInputType::AriaRoleForPickerIndicator() const {
+ return GetLocale().QueryString(IDS_AX_CALENDAR_SHOW_DATE_TIME_LOCAL_PICKER);
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/date_time_local_input_type.h b/chromium/third_party/blink/renderer/core/html/forms/date_time_local_input_type.h
index 65428f6a7ed..01a9e573efc 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/date_time_local_input_type.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/date_time_local_input_type.h
@@ -67,6 +67,7 @@ class DateTimeLocalInputType final : public BaseTemporalInputType {
bool has_hour,
bool has_minute,
bool has_second) const override;
+ String AriaRoleForPickerIndicator() const override;
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.cc b/chromium/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.cc
index 3d15ec0fdce..68cc7e418b7 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.cc
@@ -94,8 +94,9 @@ int DateTimeNumericFieldElement::DefaultValueForStepUp() const {
return range_.minimum;
}
-void DateTimeNumericFieldElement::SetFocused(bool value,
- WebFocusType focus_type) {
+void DateTimeNumericFieldElement::SetFocused(
+ bool value,
+ mojom::blink::FocusType focus_type) {
if (!value) {
int type_ahead_value = TypeAheadValue();
type_ahead_buffer_.Clear();
diff --git a/chromium/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.h b/chromium/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.h
index 1f7dd54292e..02c576c9ed0 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.h
@@ -27,7 +27,6 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_DATE_TIME_NUMERIC_FIELD_ELEMENT_H_
#include "base/macros.h"
-#include "third_party/blink/public/platform/web_focus_type.h"
#include "third_party/blink/renderer/core/html/forms/date_time_field_element.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
@@ -95,7 +94,7 @@ class DateTimeNumericFieldElement : public DateTimeFieldElement {
String Value() const final;
// Node functions.
- void SetFocused(bool, WebFocusType) final;
+ void SetFocused(bool, mojom::blink::FocusType) final;
String FormatValue(int) const;
int RoundUp(int) const;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/external_date_time_chooser.cc b/chromium/third_party/blink/renderer/core/html/forms/external_date_time_chooser.cc
index 8f61483d746..8ad4132abe3 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/external_date_time_chooser.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/external_date_time_chooser.cc
@@ -26,6 +26,7 @@
#include "third_party/blink/renderer/core/html/forms/external_date_time_chooser.h"
#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
+#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/core/dom/element.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/html/forms/date_time_chooser_client.h"
@@ -57,23 +58,18 @@ static ui::mojom::TextInputType ToTextInputType(const AtomicString& source) {
ExternalDateTimeChooser::~ExternalDateTimeChooser() = default;
void ExternalDateTimeChooser::Trace(Visitor* visitor) {
+ visitor->Trace(date_time_chooser_);
visitor->Trace(client_);
DateTimeChooser::Trace(visitor);
}
ExternalDateTimeChooser::ExternalDateTimeChooser(DateTimeChooserClient* client)
- : client_(client) {
+ : date_time_chooser_(client->OwnerElement().GetExecutionContext()),
+ client_(client) {
DCHECK(!RuntimeEnabledFeatures::InputMultipleFieldsUIEnabled());
DCHECK(client);
}
-ExternalDateTimeChooser* ExternalDateTimeChooser::Create(
- DateTimeChooserClient* client) {
- ExternalDateTimeChooser* chooser =
- MakeGarbageCollected<ExternalDateTimeChooser>(client);
- return chooser;
-}
-
void ExternalDateTimeChooser::OpenDateTimeChooser(
LocalFrame* frame,
const DateTimeChooserParameters& parameters) {
@@ -108,12 +104,15 @@ bool ExternalDateTimeChooser::IsShowingDateTimeChooserUI() const {
mojom::blink::DateTimeChooser& ExternalDateTimeChooser::GetDateTimeChooser(
LocalFrame* frame) {
- if (!date_time_chooser_) {
+ if (!date_time_chooser_.is_bound()) {
frame->GetBrowserInterfaceBroker().GetInterface(
- date_time_chooser_.BindNewPipeAndPassReceiver());
+ date_time_chooser_.BindNewPipeAndPassReceiver(
+ // Per the spec, this is a user interaction.
+ // https://html.spec.whatwg.org/multipage/input.html#common-input-element-events
+ frame->GetTaskRunner(TaskType::kUserInteraction)));
}
- DCHECK(date_time_chooser_);
+ DCHECK(date_time_chooser_.is_bound());
return *date_time_chooser_.get();
}
diff --git a/chromium/third_party/blink/renderer/core/html/forms/external_date_time_chooser.h b/chromium/third_party/blink/renderer/core/html/forms/external_date_time_chooser.h
index ae2a00139fc..af6ff3007cf 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/external_date_time_chooser.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/external_date_time_chooser.h
@@ -26,10 +26,10 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_EXTERNAL_DATE_TIME_CHOOSER_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_EXTERNAL_DATE_TIME_CHOOSER_H_
-#include "mojo/public/cpp/bindings/remote.h"
#include "third_party/blink/public/mojom/choosers/date_time_chooser.mojom-blink.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/html/forms/date_time_chooser.h"
+#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h"
namespace blink {
@@ -38,8 +38,6 @@ class LocalFrame;
class CORE_EXPORT ExternalDateTimeChooser final : public DateTimeChooser {
public:
- static ExternalDateTimeChooser* Create(DateTimeChooserClient*);
-
explicit ExternalDateTimeChooser(DateTimeChooserClient*);
~ExternalDateTimeChooser() override;
void Trace(Visitor*) override;
@@ -61,9 +59,9 @@ class CORE_EXPORT ExternalDateTimeChooser final : public DateTimeChooser {
mojom::blink::DateTimeChooser& GetDateTimeChooser(LocalFrame* frame);
- mojo::Remote<mojom::blink::DateTimeChooser> date_time_chooser_;
+ HeapMojoRemote<mojom::blink::DateTimeChooser> date_time_chooser_;
Member<DateTimeChooserClient> client_;
};
-}
+} // namespace blink
#endif
diff --git a/chromium/third_party/blink/renderer/core/html/forms/external_date_time_chooser_test.cc b/chromium/third_party/blink/renderer/core/html/forms/external_date_time_chooser_test.cc
index d47d454033b..4f705495a9b 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/external_date_time_chooser_test.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/external_date_time_chooser_test.cc
@@ -9,6 +9,7 @@
#include "third_party/blink/renderer/core/html/forms/date_time_chooser_client.h"
#include "third_party/blink/renderer/core/html/forms/html_input_element.h"
#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
namespace blink {
@@ -64,7 +65,8 @@ TEST_F(ExternalDateTimeChooserTest, EndChooserShouldNotCrash) {
auto* document = MakeGarbageCollected<Document>();
auto* element = document->CreateRawElement(html_names::kInputTag);
auto* client = MakeGarbageCollected<TestDateTimeChooserClient>(element);
- auto* external_date_time_chooser = ExternalDateTimeChooser::Create(client);
+ auto* external_date_time_chooser =
+ MakeGarbageCollected<ExternalDateTimeChooser>(client);
client->SetDateTimeChooser(external_date_time_chooser);
external_date_time_chooser->ResponseHandler(true, 0);
}
@@ -77,7 +79,7 @@ TEST_F(ExternalDateTimeChooserTest, EndChooserShouldNotCrash) {
TEST_F(ExternalDateTimeChooserTest,
OpenDateTimeChooserShouldNotCrashWhenLabelAndValueIsTheSame) {
ScopedInputMultipleFieldsUIForTest input_multiple_fields_ui(false);
- GetDocument().documentElement()->SetInnerHTMLFromString(R"HTML(
+ GetDocument().documentElement()->setInnerHTML(R"HTML(
<input id=test type="date" list="src" />
<datalist id="src">
<option value='2019-12-31'>Hint</option>
@@ -86,8 +88,8 @@ TEST_F(ExternalDateTimeChooserTest,
// value attribute.
</datalist>
)HTML");
- GetDocument().View()->UpdateAllLifecyclePhases(
- DocumentLifecycle::LifecycleUpdateReason::kTest);
+ GetDocument().View()->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest);
+
GetDocument().View()->RunPostLifecycleSteps();
auto* input = To<HTMLInputElement>(GetDocument().getElementById("test"));
ASSERT_TRUE(input);
@@ -98,7 +100,8 @@ TEST_F(ExternalDateTimeChooserTest,
auto* client = MakeGarbageCollected<TestDateTimeChooserClient>(
GetDocument().documentElement());
- auto* external_date_time_chooser = ExternalDateTimeChooser::Create(client);
+ auto* external_date_time_chooser =
+ MakeGarbageCollected<ExternalDateTimeChooser>(client);
client->SetDateTimeChooser(external_date_time_chooser);
external_date_time_chooser->OpenDateTimeChooser(GetDocument().GetFrame(),
params);
diff --git a/chromium/third_party/blink/renderer/core/html/forms/external_popup_menu.cc b/chromium/third_party/blink/renderer/core/html/forms/external_popup_menu.cc
index b86f742cbd4..f7aab03b544 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/external_popup_menu.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/external_popup_menu.cc
@@ -31,9 +31,9 @@
#include "third_party/blink/renderer/core/html/forms/external_popup_menu.h"
#include "build/build_config.h"
+#include "third_party/blink/public/common/input/web_mouse_event.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/public/platform/web_coalesced_input_event.h"
-#include "third_party/blink/public/platform/web_mouse_event.h"
#include "third_party/blink/public/platform/web_vector.h"
#include "third_party/blink/public/web/web_external_popup_menu.h"
#include "third_party/blink/public/web/web_local_frame_client.h"
@@ -262,7 +262,7 @@ void ExternalPopupMenu::GetPopupMenuInfo(WebPopupMenuInfo& info,
}
popup_item.enabled = !item_element.IsDisabledFormControl();
const ComputedStyle& style = *owner_element.ItemComputedStyle(item_element);
- popup_item.text_direction = ToWebTextDirection(style.Direction());
+ popup_item.text_direction = ToBaseTextDirection(style.Direction());
popup_item.has_text_direction_override = IsOverride(style.GetUnicodeBidi());
}
diff --git a/chromium/third_party/blink/renderer/core/html/forms/external_popup_menu_test.cc b/chromium/third_party/blink/renderer/core/html/forms/external_popup_menu_test.cc
index 383f6a853c8..a493fa8b0c7 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/external_popup_menu_test.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/external_popup_menu_test.cc
@@ -17,7 +17,7 @@
#include "third_party/blink/renderer/core/html/forms/html_select_element.h"
#include "third_party/blink/renderer/core/html/forms/popup_menu.h"
#include "third_party/blink/renderer/core/html_names.h"
-#include "third_party/blink/renderer/core/layout/layout_menu_list.h"
+#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
@@ -35,12 +35,12 @@ class ExternalPopupMenuDisplayNoneItemsTest : public PageTestBase {
PageTestBase::SetUp();
auto* element = MakeGarbageCollected<HTMLSelectElement>(GetDocument());
// Set the 4th an 5th items to have "display: none" property
- element->SetInnerHTMLFromString(
+ element->setInnerHTML(
"<option><option><option><option style='display:none;'><option "
"style='display:none;'><option><option>");
GetDocument().body()->AppendChild(element, ASSERT_NO_EXCEPTION);
owner_element_ = element;
- GetDocument().UpdateStyleAndLayout();
+ GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
}
Persistent<HTMLSelectElement> owner_element_;
@@ -117,7 +117,7 @@ class ExternalPopupMenuTest : public testing::Test {
frame_test_helpers::LoadFrame(MainFrame(), base_url_ + file_name);
WebView()->MainFrameWidget()->Resize(WebSize(800, 600));
WebView()->MainFrameWidget()->UpdateAllLifecyclePhases(
- WebWidget::LifecycleUpdateReason::kTest);
+ DocumentUpdateReason::kTest);
}
WebViewImpl* WebView() const { return helper_.GetWebView(); }
@@ -138,16 +138,16 @@ TEST_F(ExternalPopupMenuTest, PopupAccountsForVisualViewportTransform) {
WebView()->MainFrameWidget()->Resize(WebSize(100, 100));
WebView()->MainFrameWidget()->UpdateAllLifecyclePhases(
- WebWidget::LifecycleUpdateReason::kTest);
+ DocumentUpdateReason::kTest);
auto* select = To<HTMLSelectElement>(
MainFrame()->GetFrame()->GetDocument()->getElementById("select"));
- LayoutMenuList* menu_list = ToLayoutMenuList(select->GetLayoutObject());
- ASSERT_TRUE(menu_list);
+ auto* layout_object = select->GetLayoutObject();
+ ASSERT_TRUE(layout_object);
VisualViewport& visual_viewport = WebView()->GetPage()->GetVisualViewport();
- IntRect rect_in_document = menu_list->AbsoluteBoundingBoxRect();
+ IntRect rect_in_document = layout_object->AbsoluteBoundingBoxRect();
constexpr int kScaleFactor = 2;
ScrollOffset scroll_delta(20, 30);
@@ -171,17 +171,17 @@ TEST_F(ExternalPopupMenuTest, DidAcceptIndex) {
auto* select = To<HTMLSelectElement>(
MainFrame()->GetFrame()->GetDocument()->getElementById("select"));
- LayoutMenuList* menu_list = ToLayoutMenuList(select->GetLayoutObject());
- ASSERT_TRUE(menu_list);
+ auto* layout_object = select->GetLayoutObject();
+ ASSERT_TRUE(layout_object);
select->ShowPopup();
ASSERT_TRUE(select->PopupIsVisible());
WebExternalPopupMenuClient* client =
- static_cast<ExternalPopupMenu*>(select->Popup());
+ static_cast<ExternalPopupMenu*>(select->PopupForTesting());
client->DidAcceptIndex(2);
EXPECT_FALSE(select->PopupIsVisible());
- ASSERT_EQ("2", menu_list->GetText().Utf8());
+ ASSERT_EQ("2", select->InnerElement().innerText().Utf8());
EXPECT_EQ(2, select->selectedIndex());
}
@@ -191,19 +191,19 @@ TEST_F(ExternalPopupMenuTest, DidAcceptIndices) {
auto* select = To<HTMLSelectElement>(
MainFrame()->GetFrame()->GetDocument()->getElementById("select"));
- LayoutMenuList* menu_list = ToLayoutMenuList(select->GetLayoutObject());
- ASSERT_TRUE(menu_list);
+ auto* layout_object = select->GetLayoutObject();
+ ASSERT_TRUE(layout_object);
select->ShowPopup();
ASSERT_TRUE(select->PopupIsVisible());
WebExternalPopupMenuClient* client =
- static_cast<ExternalPopupMenu*>(select->Popup());
+ static_cast<ExternalPopupMenu*>(select->PopupForTesting());
int indices[] = {2};
WebVector<int> indices_vector(indices, 1);
client->DidAcceptIndices(indices_vector);
EXPECT_FALSE(select->PopupIsVisible());
- EXPECT_EQ("2", menu_list->GetText());
+ EXPECT_EQ("2", select->InnerElement().innerText());
EXPECT_EQ(2, select->selectedIndex());
}
@@ -213,14 +213,14 @@ TEST_F(ExternalPopupMenuTest, DidAcceptIndicesClearSelect) {
auto* select = To<HTMLSelectElement>(
MainFrame()->GetFrame()->GetDocument()->getElementById("select"));
- LayoutMenuList* menu_list = ToLayoutMenuList(select->GetLayoutObject());
- ASSERT_TRUE(menu_list);
+ auto* layout_object = select->GetLayoutObject();
+ ASSERT_TRUE(layout_object);
select->ShowPopup();
ASSERT_TRUE(select->PopupIsVisible());
WebExternalPopupMenuClient* client =
- static_cast<ExternalPopupMenu*>(select->Popup());
+ static_cast<ExternalPopupMenu*>(select->PopupForTesting());
WebVector<int> indices;
client->DidAcceptIndices(indices);
EXPECT_FALSE(select->PopupIsVisible());
diff --git a/chromium/third_party/blink/renderer/core/html/forms/file_input_type.cc b/chromium/third_party/blink/renderer/core/html/forms/file_input_type.cc
index 5e07aa092bd..62d3be9b859 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/file_input_type.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/file_input_type.cc
@@ -24,7 +24,6 @@
#include "third_party/blink/public/platform/file_path_conversion.h"
#include "third_party/blink/public/strings/grit/blink_strings.h"
-#include "third_party/blink/renderer/core/css/style_change_reason.h"
#include "third_party/blink/renderer/core/dom/events/event.h"
#include "third_party/blink/renderer/core/dom/shadow_root.h"
#include "third_party/blink/renderer/core/events/keyboard_event.h"
@@ -34,10 +33,12 @@
#include "third_party/blink/renderer/core/html/forms/form_controller.h"
#include "third_party/blink/renderer/core/html/forms/form_data.h"
#include "third_party/blink/renderer/core/html/forms/html_input_element.h"
+#include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/input_type_names.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
-#include "third_party/blink/renderer/core/layout/layout_file_upload_control.h"
+#include "third_party/blink/renderer/core/layout/layout_block_flow.h"
+#include "third_party/blink/renderer/core/layout/layout_object_factory.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
#include "third_party/blink/renderer/core/page/drag_data.h"
#include "third_party/blink/renderer/core/probe/core_probes.h"
@@ -162,9 +163,9 @@ void FileInputType::HandleDOMActivateEvent(Event& event) {
if (!LocalFrame::HasTransientUserActivation(document.GetFrame())) {
String message =
"File chooser dialog can only be shown with a user activation.";
- document.AddConsoleMessage(
- ConsoleMessage::Create(mojom::ConsoleMessageSource::kJavaScript,
- mojom::ConsoleMessageLevel::kWarning, message));
+ document.AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
+ mojom::ConsoleMessageSource::kJavaScript,
+ mojom::ConsoleMessageLevel::kWarning, message));
return;
}
@@ -202,9 +203,18 @@ void FileInputType::HandleDOMActivateEvent(Event& event) {
event.SetDefaultHandled();
}
-LayoutObject* FileInputType::CreateLayoutObject(const ComputedStyle&,
- LegacyLayout) const {
- return new LayoutFileUploadControl(&GetElement());
+void FileInputType::CustomStyleForLayoutObject(ComputedStyle& style) {
+ style.SetShouldIgnoreOverflowPropertyForInlineBlockBaseline();
+}
+
+bool FileInputType::TypeShouldForceLegacyLayout() const {
+ return !RuntimeEnabledFeatures::LayoutNGForControlsEnabled();
+}
+
+LayoutObject* FileInputType::CreateLayoutObject(const ComputedStyle& style,
+ LegacyLayout legacy) const {
+ return LayoutObjectFactory::CreateFileUploadControl(GetElement(), style,
+ legacy);
}
InputType::ValueMode FileInputType::GetValueMode() const {
@@ -249,10 +259,8 @@ void FileInputType::SetValue(const String&,
return;
file_list_->clear();
- GetElement().SetNeedsStyleRecalc(
- kSubtreeStyleChange,
- StyleChangeReasonForTracing::Create(style_change_reason::kControlValue));
GetElement().SetNeedsValidityCheck();
+ UpdateView();
}
FileList* FileInputType::CreateFileList(const FileChooserFileInfoList& files,
@@ -317,8 +325,10 @@ void FileInputType::CountUsage() {
void FileInputType::CreateShadowSubtree() {
DCHECK(IsShadowHost(GetElement()));
- auto* button = MakeGarbageCollected<HTMLInputElement>(
- GetElement().GetDocument(), CreateElementFlags());
+ Document& document = GetElement().GetDocument();
+
+ auto* button =
+ MakeGarbageCollected<HTMLInputElement>(document, CreateElementFlags());
button->setType(input_type_names::kButton);
button->setAttribute(
html_names::kValueAttr,
@@ -326,25 +336,43 @@ void FileInputType::CreateShadowSubtree() {
GetElement().Multiple() ? IDS_FORM_MULTIPLE_FILES_BUTTON_LABEL
: IDS_FORM_FILE_BUTTON_LABEL)));
button->SetShadowPseudoId(AtomicString("-webkit-file-upload-button"));
+ button->setAttribute(html_names::kIdAttr,
+ shadow_element_names::FileUploadButton());
+ button->SetActive(GetElement().CanReceiveDroppedFiles());
GetElement().UserAgentShadowRoot()->AppendChild(button);
+
+ // The following element is used only in LayoutNG.
+ // See LayoutFileUploadControl::IsChildAllowed().
+ auto* span = document.CreateRawElement(html_names::kSpanTag);
+ // This element is hidden from AX trees for a historical reason.
+ span->setAttribute(html_names::kAriaHiddenAttr, "true");
+ GetElement().UserAgentShadowRoot()->AppendChild(span);
+
+ UpdateView();
+}
+
+HTMLInputElement* FileInputType::UploadButton() const {
+ Element* element = GetElement().UserAgentShadowRoot()->getElementById(
+ shadow_element_names::FileUploadButton());
+ CHECK(!element || IsA<HTMLInputElement>(element));
+ return To<HTMLInputElement>(element);
+}
+
+Node* FileInputType::FileStatusElement() const {
+ return GetElement().UserAgentShadowRoot()->lastChild();
}
void FileInputType::DisabledAttributeChanged() {
DCHECK(IsShadowHost(GetElement()));
- CHECK(!GetElement().UserAgentShadowRoot()->firstChild() ||
- IsA<Element>(GetElement().UserAgentShadowRoot()->firstChild()));
- if (Element* button =
- To<Element>(GetElement().UserAgentShadowRoot()->firstChild()))
+ if (Element* button = UploadButton()) {
button->SetBooleanAttribute(html_names::kDisabledAttr,
GetElement().IsDisabledFormControl());
+ }
}
void FileInputType::MultipleAttributeChanged() {
DCHECK(IsShadowHost(GetElement()));
- CHECK(!GetElement().UserAgentShadowRoot()->firstChild() ||
- IsA<Element>(GetElement().UserAgentShadowRoot()->firstChild()));
- if (Element* button =
- To<Element>(GetElement().UserAgentShadowRoot()->firstChild())) {
+ if (Element* button = UploadButton()) {
button->setAttribute(
html_names::kValueAttr,
AtomicString(GetLocale().QueryString(
@@ -373,10 +401,7 @@ bool FileInputType::SetFiles(FileList* files) {
GetElement().NotifyFormStateChanged();
GetElement().SetNeedsValidityCheck();
-
- if (GetElement().GetLayoutObject())
- GetElement().GetLayoutObject()->SetShouldDoFullPaintInvalidation();
-
+ UpdateView();
return files_changed;
}
@@ -520,4 +545,27 @@ void FileInputType::WillOpenPopup() {
}
}
+String FileInputType::FileStatusText() const {
+ Locale& locale = GetLocale();
+
+ if (file_list_->IsEmpty())
+ return locale.QueryString(IDS_FORM_FILE_NO_FILE_LABEL);
+
+ if (file_list_->length() == 1)
+ return LayoutTheme::GetTheme().DisplayNameForFile(*file_list_->item(0));
+
+ return locale.QueryString(
+ IDS_FORM_FILE_MULTIPLE_UPLOAD,
+ locale.ConvertToLocalizedNumber(String::Number(file_list_->length())));
+}
+
+void FileInputType::UpdateView() {
+ auto* layout_object = GetElement().GetLayoutObject();
+ if (layout_object && layout_object->IsFileUploadControl())
+ layout_object->SetShouldDoFullPaintInvalidation();
+
+ if (auto* span = FileStatusElement())
+ span->setTextContent(FileStatusText());
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/file_input_type.h b/chromium/third_party/blink/renderer/core/html/forms/file_input_type.h
index 256a75272dd..425bf6b8226 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/file_input_type.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/file_input_type.h
@@ -72,6 +72,8 @@ class CORE_EXPORT FileInputType final : public InputType,
bool ValueMissing(const String&) const override;
String ValueMissingText() const override;
void HandleDOMActivateEvent(Event&) override;
+ void CustomStyleForLayoutObject(ComputedStyle& style) override;
+ bool TypeShouldForceLegacyLayout() const override;
LayoutObject* CreateLayoutObject(const ComputedStyle&,
LegacyLayout) const override;
bool CanSetStringValue() const override;
@@ -88,10 +90,13 @@ class CORE_EXPORT FileInputType final : public InputType,
bool ReceiveDroppedFiles(const DragData*) override;
String DroppedFileSystemId() override;
void CreateShadowSubtree() override;
+ HTMLInputElement* UploadButton() const override;
void DisabledAttributeChanged() override;
void MultipleAttributeChanged() override;
String DefaultToolTip(const InputTypeView&) const override;
void CopyNonAttributeProperties(const HTMLInputElement&) override;
+ String FileStatusText() const override;
+ void UpdateView() override;
// KeyboardClickableInputTypeView overrides.
void HandleKeypressEvent(KeyboardEvent&) override;
@@ -106,6 +111,7 @@ class CORE_EXPORT FileInputType final : public InputType,
void WillOpenPopup() override;
void SetFilesFromDirectory(const String&);
+ Node* FileStatusElement() const;
Member<FileList> file_list_;
String dropped_file_system_id_;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/file_input_type_test.cc b/chromium/third_party/blink/renderer/core/html/forms/file_input_type_test.cc
index 3d30730b162..ebbb62c3b57 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/file_input_type_test.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/file_input_type_test.cc
@@ -138,7 +138,7 @@ TEST(FileInputTypeTest, DropTouchesNoPopupOpeningObserver) {
std::make_unique<DummyPageHolder>(IntSize(), &page_clients);
Document& doc = page_holder->GetDocument();
- doc.body()->SetInnerHTMLFromString("<input type=file webkitdirectory>");
+ doc.body()->setInnerHTML("<input type=file webkitdirectory>");
auto& input = *To<HTMLInputElement>(doc.body()->firstChild());
base::RunLoop run_loop;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/form_controller.cc b/chromium/third_party/blink/renderer/core/html/forms/form_controller.cc
index b9b161e40a4..681786d2c40 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/form_controller.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/form_controller.cc
@@ -42,10 +42,6 @@ namespace blink {
namespace {
-// TODO(crbug.com/1008708): Remove this flag when we're sure the new behavior
-// is better than the previous one.
-constexpr bool kRestoreOnLoad = true;
-
inline HTMLFormElement* OwnerFormForState(const ListedElement& control) {
// Assume controls with form attribute have no owners because we restore
// state during parsing and form owners of such controls might be
@@ -62,8 +58,9 @@ const AtomicString& ControlType(const ListedElement& control) {
}
bool IsDirtyControl(const ListedElement& control) {
- if (control.IsFormControlElementWithState())
- return ToHTMLFormControlElementWithState(control).UserHasEditedTheField();
+ if (auto* form_control_element =
+ DynamicTo<HTMLFormControlElementWithState>(control))
+ return form_control_element->UserHasEditedTheField();
if (control.IsElementInternals()) {
// We have no ways to know the dirtiness of a form-associated custom
// element. Assume it is dirty if it has focus.
@@ -567,7 +564,7 @@ void FormController::WillDeleteForm(HTMLFormElement* form) {
}
void FormController::RestoreControlStateFor(ListedElement& control) {
- if (kRestoreOnLoad && !document_->HasFinishedParsing())
+ if (!document_->HasFinishedParsing())
return;
if (OwnerFormForState(control))
return;
@@ -575,7 +572,7 @@ void FormController::RestoreControlStateFor(ListedElement& control) {
}
void FormController::RestoreControlStateIn(HTMLFormElement& form) {
- if (kRestoreOnLoad && !document_->HasFinishedParsing())
+ if (!document_->HasFinishedParsing())
return;
EventQueueScope scope;
const ListedElement::List& elements = form.ListedElements();
@@ -619,8 +616,6 @@ void FormController::RestoreControlStateOnUpgrade(ListedElement& control) {
}
void FormController::ScheduleRestore() {
- if (!kRestoreOnLoad)
- return;
document_->GetTaskRunner(TaskType::kInternalLoading)
->PostTask(FROM_HERE,
WTF::Bind(&FormController::RestoreAllControlsInDocumentOrder,
@@ -628,8 +623,6 @@ void FormController::ScheduleRestore() {
}
void FormController::RestoreImmediately() {
- if (!kRestoreOnLoad)
- return;
if (did_restore_all_ || !HasControlStates())
return;
RestoreAllControlsInDocumentOrder();
diff --git a/chromium/third_party/blink/renderer/core/html/forms/form_controller_test.cc b/chromium/third_party/blink/renderer/core/html/forms/form_controller_test.cc
index b278e95decd..e54eefa6018 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/form_controller_test.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/form_controller_test.cc
@@ -20,7 +20,7 @@ TEST(DocumentStateTest, ToStateVectorConnected) {
Element* html = doc.CreateRawElement(html_names::kHTMLTag);
doc.appendChild(html);
Node* body = html->appendChild(doc.CreateRawElement(html_names::kBodyTag));
- To<Element>(body)->SetInnerHTMLFromString("<select form='ff'></select>");
+ To<Element>(body)->setInnerHTML("<select form='ff'></select>");
DocumentState* document_state = doc.GetFormController().ControlStates();
Vector<String> state1 = document_state->ToStateVector();
// <signature>, <control-size>, <form-key>, <name>, <type>, <data-size(0)>
diff --git a/chromium/third_party/blink/renderer/core/html/forms/form_data.cc b/chromium/third_party/blink/renderer/core/html/forms/form_data.cc
index 4ef6dba5b2d..1918cac770a 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/form_data.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/form_data.cc
@@ -302,7 +302,7 @@ scoped_refptr<EncodedFormData> FormData::EncodeMultiPartFormData() {
auto* file = To<File>(entry->GetBlob());
// Do not add the file if the path is empty.
if (!file->GetPath().IsEmpty())
- form_data->AppendFile(file->GetPath());
+ form_data->AppendFile(file->GetPath(), file->LastModifiedTime());
} else {
form_data->AppendBlob(entry->GetBlob()->Uuid(),
entry->GetBlob()->GetBlobDataHandle());
diff --git a/chromium/third_party/blink/renderer/core/html/forms/form_data.idl b/chromium/third_party/blink/renderer/core/html/forms/form_data.idl
index 79c8e499142..cc98012eb62 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/form_data.idl
+++ b/chromium/third_party/blink/renderer/core/html/forms/form_data.idl
@@ -33,10 +33,9 @@
typedef (File or USVString) FormDataEntryValue;
[
- Constructor(optional HTMLFormElement form),
- RaisesException=Constructor,
Exposed=(Window,Worker)
] interface FormData {
+ [RaisesException] constructor(optional HTMLFormElement form);
void append(USVString name, USVString value);
[CallWith=ScriptState] void append(USVString name, Blob value, optional USVString filename);
[ImplementedAs=deleteEntry] void delete(USVString name);
diff --git a/chromium/third_party/blink/renderer/core/html/forms/form_data_event.cc b/chromium/third_party/blink/renderer/core/html/forms/form_data_event.cc
index 9b077df8cc7..3e1d9bf0d70 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/form_data_event.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/form_data_event.cc
@@ -4,9 +4,9 @@
#include "third_party/blink/renderer/core/html/forms/form_data_event.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_form_data_event_init.h"
#include "third_party/blink/renderer/core/event_interface_names.h"
#include "third_party/blink/renderer/core/html/forms/form_data.h"
-#include "third_party/blink/renderer/core/html/forms/form_data_event_init.h"
namespace blink {
diff --git a/chromium/third_party/blink/renderer/core/html/forms/form_data_event.idl b/chromium/third_party/blink/renderer/core/html/forms/form_data_event.idl
index 0b74add3f2a..84c7af0cc1e 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/form_data_event.idl
+++ b/chromium/third_party/blink/renderer/core/html/forms/form_data_event.idl
@@ -5,9 +5,8 @@
// https://html.spec.whatwg.org/C/#formdataevent
[
- Constructor(DOMString type, optional FormDataEventInit eventInitDict),
Exposed=Window
-]
-interface FormDataEvent : Event {
+] interface FormDataEvent : Event {
+ constructor(DOMString type, optional FormDataEventInit eventInitDict = {});
readonly attribute FormData formData;
};
diff --git a/chromium/third_party/blink/renderer/core/html/forms/hidden_input_type.cc b/chromium/third_party/blink/renderer/core/html/forms/hidden_input_type.cc
index ce51f88b4ca..f8fef662ad8 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/hidden_input_type.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/hidden_input_type.cc
@@ -92,7 +92,7 @@ void HiddenInputType::SetValue(const String& sanitized_value,
}
void HiddenInputType::AppendToFormData(FormData& form_data) const {
- if (DeprecatedEqualIgnoringCase(GetElement().GetName(), "_charset_")) {
+ if (EqualIgnoringASCIICase(GetElement().GetName(), "_charset_")) {
form_data.AppendFromElement(GetElement().GetName(),
String(form_data.Encoding().GetName()));
return;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_button_element.cc b/chromium/third_party/blink/renderer/core/html/forms/html_button_element.cc
index 0171b21b096..e062da100d7 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_button_element.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_button_element.cc
@@ -27,7 +27,6 @@
#include "third_party/blink/renderer/core/dom/attribute.h"
#include "third_party/blink/renderer/core/events/keyboard_event.h"
-#include "third_party/blink/renderer/core/events/mouse_event.h"
#include "third_party/blink/renderer/core/html/forms/form_data.h"
#include "third_party/blink/renderer/core/html/forms/html_form_element.h"
#include "third_party/blink/renderer/core/html_names.h"
@@ -45,8 +44,15 @@ void HTMLButtonElement::setType(const AtomicString& type) {
setAttribute(html_names::kTypeAttr, type);
}
-LayoutObject* HTMLButtonElement::CreateLayoutObject(const ComputedStyle&,
- LegacyLayout) {
+LayoutObject* HTMLButtonElement::CreateLayoutObject(const ComputedStyle& style,
+ LegacyLayout legacy) {
+ // https://html.spec.whatwg.org/C/#button-layout
+ EDisplay display = style.Display();
+ if (display == EDisplay::kInlineGrid || display == EDisplay::kGrid ||
+ display == EDisplay::kInlineFlex || display == EDisplay::kFlex ||
+ display == EDisplay::kInlineLayoutCustom ||
+ display == EDisplay::kLayoutCustom)
+ return HTMLFormControlElement::CreateLayoutObject(style, legacy);
return new LayoutButton(this);
}
@@ -84,9 +90,9 @@ bool HTMLButtonElement::IsPresentationAttribute(
void HTMLButtonElement::ParseAttribute(
const AttributeModificationParams& params) {
if (params.name == html_names::kTypeAttr) {
- if (DeprecatedEqualIgnoringCase(params.new_value, "reset"))
+ if (EqualIgnoringASCIICase(params.new_value, "reset"))
type_ = RESET;
- else if (DeprecatedEqualIgnoringCase(params.new_value, "button"))
+ else if (EqualIgnoringASCIICase(params.new_value, "button"))
type_ = BUTTON;
else
type_ = SUBMIT;
@@ -101,13 +107,6 @@ void HTMLButtonElement::ParseAttribute(
}
void HTMLButtonElement::DefaultEventHandler(Event& event) {
- DefaultEventHandlerInternal(event);
-
- if (event.type() == event_type_names::kDOMActivate && formOwner())
- formOwner()->DidActivateSubmitButton(this);
-}
-
-void HTMLButtonElement::DefaultEventHandlerInternal(Event& event) {
if (event.type() == event_type_names::kDOMActivate &&
!IsDisabledFormControl()) {
if (Form() && type_ == SUBMIT) {
@@ -120,33 +119,8 @@ void HTMLButtonElement::DefaultEventHandlerInternal(Event& event) {
}
}
- if (event.IsKeyboardEvent()) {
- if (event.type() == event_type_names::kKeydown &&
- ToKeyboardEvent(event).key() == " ") {
- SetActive(true);
- // No setDefaultHandled() - IE dispatches a keypress in this case.
- return;
- }
- if (event.type() == event_type_names::kKeypress) {
- switch (ToKeyboardEvent(event).charCode()) {
- case '\r':
- DispatchSimulatedClick(&event);
- event.SetDefaultHandled();
- return;
- case ' ':
- // Prevent scrolling down the page.
- event.SetDefaultHandled();
- return;
- }
- }
- if (event.type() == event_type_names::kKeyup &&
- ToKeyboardEvent(event).key() == " ") {
- if (IsActive())
- DispatchSimulatedClick(&event);
- event.SetDefaultHandled();
- return;
- }
- }
+ if (HandleKeyboardActivation(event))
+ return;
HTMLFormControlElement::DefaultEventHandler(event);
}
@@ -223,16 +197,4 @@ Node::InsertionNotificationRequest HTMLButtonElement::InsertedInto(
return request;
}
-EventDispatchHandlingState* HTMLButtonElement::PreDispatchEventHandler(
- Event& event) {
- if (Form() && CanBeSuccessfulSubmitButton())
- Form()->WillActivateSubmitButton(this);
- return nullptr;
-}
-
-void HTMLButtonElement::DidPreventDefault(const Event& event) {
- if (auto* form = formOwner())
- form->DidActivateSubmitButton(this);
-}
-
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_button_element.h b/chromium/third_party/blink/renderer/core/html/forms/html_button_element.h
index 6071e0f1bc4..44437eeda26 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_button_element.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_button_element.h
@@ -78,13 +78,6 @@ class HTMLButtonElement final : public HTMLFormControlElement {
int DefaultTabIndex() const override;
- // TODO(crbug.com/1013385): Remove PreDispatchEventHandler, DidPreventDefault,
- // and DefaultEventHandlerInternal. They are here to temporarily fix form
- // double-submit.
- EventDispatchHandlingState* PreDispatchEventHandler(Event&) override;
- void DidPreventDefault(const Event&) final;
- void DefaultEventHandlerInternal(Event&);
-
Type type_;
bool is_activated_submit_;
};
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_data_list_element.cc b/chromium/third_party/blink/renderer/core/html/forms/html_data_list_element.cc
index d7faa3935f7..e6bfaae178f 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_data_list_element.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_data_list_element.cc
@@ -52,7 +52,7 @@ HTMLDataListOptionsCollection* HTMLDataListElement::options() {
void HTMLDataListElement::ChildrenChanged(const ChildrenChange& change) {
HTMLElement::ChildrenChanged(change);
- if (!change.by_parser) {
+ if (!change.ByParser()) {
GetTreeScope().GetIdTargetObserverRegistry().NotifyObservers(
GetIdAttribute());
}
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_data_list_element.h b/chromium/third_party/blink/renderer/core/html/forms/html_data_list_element.h
index 07460b9469e..84fb8779116 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_data_list_element.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_data_list_element.h
@@ -32,12 +32,11 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_HTML_DATA_LIST_ELEMENT_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_HTML_DATA_LIST_ELEMENT_H_
+#include "third_party/blink/renderer/core/html/forms/html_data_list_options_collection.h"
#include "third_party/blink/renderer/core/html/html_element.h"
namespace blink {
-class HTMLDataListOptionsCollection;
-
class CORE_EXPORT HTMLDataListElement final : public HTMLElement {
DEFINE_WRAPPERTYPEINFO();
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_data_list_options_collection.h b/chromium/third_party/blink/renderer/core/html/forms/html_data_list_options_collection.h
index 4b68f900289..94d5f68907e 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_data_list_options_collection.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_data_list_options_collection.h
@@ -7,6 +7,7 @@
#include "third_party/blink/renderer/core/html/forms/html_option_element.h"
#include "third_party/blink/renderer/core/html/html_collection.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
namespace blink {
@@ -28,11 +29,12 @@ class HTMLDataListOptionsCollection : public HTMLCollection {
bool ElementMatches(const HTMLElement&) const;
};
-DEFINE_TYPE_CASTS(HTMLDataListOptionsCollection,
- LiveNodeListBase,
- collection,
- collection->GetType() == kDataListOptions,
- collection.GetType() == kDataListOptions);
+template <>
+struct DowncastTraits<HTMLDataListOptionsCollection> {
+ static bool AllowFrom(const LiveNodeListBase& collection) {
+ return collection.GetType() == kDataListOptions;
+ }
+};
inline bool HTMLDataListOptionsCollection::ElementMatches(
const HTMLElement& element) const {
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_field_set_element.cc b/chromium/third_party/blink/renderer/core/html/forms/html_field_set_element.cc
index 4b63cb11b2a..e06b9e85ef3 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_field_set_element.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_field_set_element.cc
@@ -72,7 +72,7 @@ HTMLFieldSetElement::InvalidateDescendantDisabledStateAndFindFocusedOne(
{
EventDispatchForbiddenScope event_forbidden;
for (HTMLElement& element : Traversal<HTMLElement>::DescendantsOf(base)) {
- if (auto* control = ToHTMLFormControlElementOrNull(element))
+ if (auto* control = DynamicTo<HTMLFormControlElement>(element))
control->AncestorDisabledStateWasChanged();
else if (element.IsFormAssociatedCustomElement())
element.EnsureElementInternals().AncestorDisabledStateWasChanged();
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_form_control_element.cc b/chromium/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
index 1204ae261b3..7ef8db7a852 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
@@ -202,18 +202,6 @@ const AtomicString& HTMLFormControlElement::autocapitalize() const {
return g_empty_atom;
}
-void HTMLFormControlElement::AttachLayoutTree(AttachContext& context) {
- HTMLElement::AttachLayoutTree(context);
-
- if (!GetLayoutObject())
- return;
-
- // The call to updateFromElement() needs to go after the call through
- // to the base class's attachLayoutTree() because that can sometimes do a
- // close on the layoutObject.
- GetLayoutObject()->UpdateFromElement();
-}
-
void HTMLFormControlElement::DidMoveToNewDocument(Document& old_document) {
ListedElement::DidMoveToNewDocument(old_document);
HTMLElement::DidMoveToNewDocument(old_document);
@@ -243,10 +231,6 @@ void HTMLFormControlElement::DidChangeForm() {
formOwner()->InvalidateDefaultButtonStyle();
}
-void HTMLFormControlElement::DispatchChangeEvent() {
- DispatchScopedEvent(*Event::CreateBubble(event_type_names::kChange));
-}
-
HTMLFormElement* HTMLFormControlElement::formOwner() const {
return ListedElement::Form();
}
@@ -273,13 +257,6 @@ String HTMLFormControlElement::ResultForDialogSubmit() {
return FastGetAttribute(html_names::kValueAttr);
}
-void HTMLFormControlElement::DidRecalcStyle(const StyleRecalcChange change) {
- if (change.ReattachLayoutTree())
- return;
- if (LayoutObject* layout_object = GetLayoutObject())
- layout_object->UpdateFromElement();
-}
-
bool HTMLFormControlElement::SupportsFocus() const {
return !IsDisabledFormControl();
}
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_form_control_element.h b/chromium/third_party/blink/renderer/core/html/forms/html_form_control_element.h
index 6761900c783..62c7a77395e 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_form_control_element.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_form_control_element.h
@@ -59,8 +59,6 @@ class CORE_EXPORT HTMLFormControlElement : public HTMLElement,
void Reset();
- void DispatchChangeEvent();
-
HTMLFormElement* formOwner() const final;
bool IsDisabledFormControl() const override;
@@ -137,7 +135,6 @@ class CORE_EXPORT HTMLFormControlElement : public HTMLElement,
void ParseAttribute(const AttributeModificationParams&) override;
virtual void RequiredAttributeChanged();
void DisabledAttributeChanged() override;
- void AttachLayoutTree(AttachContext&) override;
InsertionNotificationRequest InsertedInto(ContainerNode&) override;
void RemovedFrom(ContainerNode&) override;
void WillChangeForm() override;
@@ -146,9 +143,7 @@ class CORE_EXPORT HTMLFormControlElement : public HTMLElement,
bool SupportsFocus() const override;
bool IsKeyboardFocusable() const override;
- bool ShouldHaveFocusAppearance() const final;
-
- void DidRecalcStyle(const StyleRecalcChange) override;
+ bool ShouldHaveFocusAppearance() const override;
virtual void ResetImpl() {}
@@ -167,22 +162,24 @@ class CORE_EXPORT HTMLFormControlElement : public HTMLElement,
bool blocks_form_submission_ : 1;
};
-inline bool IsHTMLFormControlElement(const Element& element) {
- return element.IsFormControlElement();
+template <>
+inline bool IsElementOfType<const HTMLFormControlElement>(const Node& node) {
+ return IsA<HTMLFormControlElement>(node);
}
-
-DEFINE_HTMLELEMENT_TYPE_CASTS_WITH_FUNCTION(HTMLFormControlElement);
-
template <>
struct DowncastTraits<HTMLFormControlElement> {
static bool AllowFrom(const Node& node) {
- auto* element = DynamicTo<Element>(node);
- return element && element->IsFormControlElement();
+ auto* html_element = DynamicTo<HTMLElement>(node);
+ return html_element && AllowFrom(*html_element);
}
static bool AllowFrom(const ListedElement& control) {
return control.IsFormControlElement();
}
+ static bool AllowFrom(const HTMLElement& html_element) {
+ return html_element.IsFormControlElement();
+ }
};
+
} // namespace blink
#endif
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_form_control_element_test.cc b/chromium/third_party/blink/renderer/core/html/forms/html_form_control_element_test.cc
index da02126b3c2..db9caa6e8b2 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_form_control_element_test.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_form_control_element_test.cc
@@ -133,7 +133,7 @@ TEST_F(HTMLFormControlElementTest, DoNotUpdateLayoutDuringDOMMutation) {
// ShowValidationMessage(). So calling it during DOM mutation is
// dangerous. This test ensures ShowValidationMessage() is NOT called in
// appendChild(). crbug.com/756408
- GetDocument().documentElement()->SetInnerHTMLFromString("<select></select>");
+ GetDocument().documentElement()->setInnerHTML("<select></select>");
auto* const select =
To<HTMLFormControlElement>(GetDocument().QuerySelector("select"));
auto* const optgroup =
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.cc b/chromium/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.cc
index 888e83b4120..f8f5337c034 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.cc
@@ -24,6 +24,7 @@
#include "third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.h"
+#include "third_party/blink/renderer/core/dom/events/event.h"
#include "third_party/blink/renderer/core/html/forms/html_form_element.h"
#include "third_party/blink/renderer/core/input_type_names.h"
@@ -275,6 +276,17 @@ bool HTMLFormControlElementWithState::ShouldSaveAndRestoreFormControlState()
return isConnected() && ShouldAutocomplete();
}
+void HTMLFormControlElementWithState::DispatchInputEvent() {
+ // Legacy 'input' event for forms set value and checked.
+ Event* event = Event::CreateBubble(event_type_names::kInput);
+ event->SetComposed(true);
+ DispatchScopedEvent(*event);
+}
+
+void HTMLFormControlElementWithState::DispatchChangeEvent() {
+ DispatchScopedEvent(*Event::CreateBubble(event_type_names::kChange));
+}
+
void HTMLFormControlElementWithState::FinishParsingChildren() {
HTMLFormControlElement::FinishParsingChildren();
ListedElement::TakeStateAndRestore();
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.h b/chromium/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.h
index 6f93afebb70..88b71eccf45 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.h
@@ -27,6 +27,7 @@
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/html/forms/html_form_control_element.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
namespace blink {
@@ -50,6 +51,9 @@ class CORE_EXPORT HTMLFormControlElementWithState
// This is only used in tests, to fake the user's action
void SetUserHasEditedTheFieldForTest() { user_has_edited_the_field_ = true; }
+ void DispatchInputEvent();
+ void DispatchChangeEvent();
+
protected:
bool user_has_edited_the_field_ = false;
HTMLFormControlElementWithState(const QualifiedName& tag_name, Document&);
@@ -58,18 +62,18 @@ class CORE_EXPORT HTMLFormControlElementWithState
bool IsFormControlElementWithState() const final;
private:
- bool TypeShouldForceLegacyLayout() const final { return true; }
int DefaultTabIndex() const override;
// https://html.spec.whatwg.org/C/#autofill-anchor-mantle
bool IsWearingAutofillAnchorMantle() const;
};
-DEFINE_TYPE_CASTS(HTMLFormControlElementWithState,
- ListedElement,
- control,
- control->IsFormControlElementWithState(),
- control.IsFormControlElementWithState());
+template <>
+struct DowncastTraits<HTMLFormControlElementWithState> {
+ static bool AllowFrom(const ListedElement& control) {
+ return control.IsFormControlElementWithState();
+ }
+};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_form_controls_collection.h b/chromium/third_party/blink/renderer/core/html/forms/html_form_controls_collection.h
index 8a33fc9fb8b..684265f7b26 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_form_controls_collection.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_form_controls_collection.h
@@ -68,11 +68,6 @@ class HTMLFormControlsCollection final : public HTMLCollection {
mutable Member<HTMLElement> cached_element_;
mutable unsigned cached_element_offset_in_array_;
};
-DEFINE_TYPE_CASTS(HTMLFormControlsCollection,
- LiveNodeListBase,
- collection,
- collection->GetType() == kFormControls,
- collection.GetType() == kFormControls);
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_form_element.cc b/chromium/third_party/blink/renderer/core/html/forms/html_form_element.cc
index 0f277cd101a..aa0f521211e 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_form_element.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_form_element.cc
@@ -28,10 +28,12 @@
#include <limits>
#include "base/auto_reset.h"
-#include "third_party/blink/public/platform/web_insecure_request_policy.h"
+#include "third_party/blink/public/common/security_context/insecure_request_policy.h"
+#include "third_party/blink/public/mojom/security_context/insecure_request_policy.mojom-blink.h"
#include "third_party/blink/renderer/bindings/core/v8/radio_node_list_or_element.h"
#include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
#include "third_party/blink/renderer/bindings/core/v8/script_event_listener.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_submit_event_init.h"
#include "third_party/blink/renderer/core/dom/attribute.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/element_traversal.h"
@@ -51,6 +53,7 @@
#include "third_party/blink/renderer/core/html/forms/html_form_controls_collection.h"
#include "third_party/blink/renderer/core/html/forms/html_input_element.h"
#include "third_party/blink/renderer/core/html/forms/radio_node_list.h"
+#include "third_party/blink/renderer/core/html/forms/submit_event.h"
#include "third_party/blink/renderer/core/html/html_collection.h"
#include "third_party/blink/renderer/core/html/html_dialog_element.h"
#include "third_party/blink/renderer/core/html/html_image_element.h"
@@ -89,8 +92,6 @@ void HTMLFormElement::Trace(Visitor* visitor) {
visitor->Trace(radio_button_group_scope_);
visitor->Trace(listed_elements_);
visitor->Trace(image_elements_);
- visitor->Trace(planned_navigation_);
- visitor->Trace(activated_submit_button_);
HTMLElement::Trace(visitor);
}
@@ -180,7 +181,7 @@ HTMLElement* HTMLFormElement::item(unsigned index) {
return elements()->item(index);
}
-void HTMLFormElement::SubmitImplicitly(Event& event,
+void HTMLFormElement::SubmitImplicitly(const Event& event,
bool from_implicit_submission_trigger) {
int submission_trigger_count = 0;
bool seen_default_button = false;
@@ -222,7 +223,7 @@ bool HTMLFormElement::ValidateInteractively() {
// Needs to update layout now because we'd like to call isFocusable(), which
// has !layoutObject()->needsLayout() assertion.
- GetDocument().UpdateStyleAndLayout();
+ GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kFocus);
// Focus on the first focusable control and show a validation message.
for (const auto& unhandled : unhandled_invalid_controls) {
@@ -241,31 +242,31 @@ bool HTMLFormElement::ValidateInteractively() {
String message(
"An invalid form control with name='%name' is not focusable.");
message.Replace("%name", unhandled->GetName());
- GetDocument().AddConsoleMessage(
- ConsoleMessage::Create(mojom::ConsoleMessageSource::kRendering,
- mojom::ConsoleMessageLevel::kError, message));
+ GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
+ mojom::ConsoleMessageSource::kRendering,
+ mojom::ConsoleMessageLevel::kError, message));
}
}
return false;
}
void HTMLFormElement::PrepareForSubmission(
- Event* event,
+ const Event* event,
HTMLFormControlElement* submit_button) {
LocalFrame* frame = GetDocument().GetFrame();
if (!frame || is_submitting_ || in_user_js_submit_event_)
return;
if (!isConnected()) {
- GetDocument().AddConsoleMessage(ConsoleMessage::Create(
+ GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
mojom::ConsoleMessageSource::kJavaScript,
mojom::ConsoleMessageLevel::kWarning,
"Form submission canceled because the form is not connected"));
return;
}
- if (GetDocument().IsSandboxed(WebSandboxFlags::kForms)) {
- GetDocument().AddConsoleMessage(ConsoleMessage::Create(
+ if (GetDocument().IsSandboxed(mojom::blink::WebSandboxFlags::kForms)) {
+ GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
mojom::ConsoleMessageSource::kSecurity,
mojom::ConsoleMessageLevel::kError,
"Blocked form submission to '" + attributes_.Action() +
@@ -282,7 +283,7 @@ void HTMLFormElement::PrepareForSubmission(
WebFeature::kFormSubmittedWithUnclosedFormControl);
if (RuntimeEnabledFeatures::UnclosedFormControlIsInvalidEnabled()) {
String tag_name = To<HTMLFormControlElement>(element)->tagName();
- GetDocument().AddConsoleMessage(ConsoleMessage::Create(
+ GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
mojom::ConsoleMessageSource::kSecurity,
mojom::ConsoleMessageLevel::kError,
"Form submission failed, as the <" + tag_name +
@@ -314,41 +315,23 @@ void HTMLFormElement::PrepareForSubmission(
should_submit = false;
} else {
frame->Client()->DispatchWillSendSubmitEvent(this);
- should_submit =
- DispatchEvent(*Event::CreateCancelableBubble(
- event_type_names::kSubmit)) == DispatchEventResult::kNotCanceled;
+ SubmitEventInit* submit_event_init = SubmitEventInit::Create();
+ submit_event_init->setBubbles(true);
+ submit_event_init->setCancelable(true);
+ submit_event_init->setSubmitter(
+ submit_button ? &submit_button->ToHTMLElement() : nullptr);
+ should_submit = DispatchEvent(*MakeGarbageCollected<SubmitEvent>(
+ event_type_names::kSubmit, submit_event_init)) ==
+ DispatchEventResult::kNotCanceled;
}
}
if (should_submit) {
- planned_navigation_ = nullptr;
- Submit(event, submit_button);
+ ScheduleFormSubmission(event, submit_button);
}
- if (!planned_navigation_ || activated_submit_button_)
- return;
- base::AutoReset<bool> submit_scope(&is_submitting_, true);
- SubmitForm(planned_navigation_);
- planned_navigation_ = nullptr;
-}
-
-void HTMLFormElement::WillActivateSubmitButton(
- HTMLFormControlElement* element) {
- if (!activated_submit_button_)
- activated_submit_button_ = element;
-}
-
-void HTMLFormElement::DidActivateSubmitButton(HTMLFormControlElement* element) {
- if (activated_submit_button_ != element)
- return;
- activated_submit_button_ = nullptr;
- if (!planned_navigation_)
- return;
- base::AutoReset<bool> submit_scope(&is_submitting_, true);
- SubmitForm(planned_navigation_);
- planned_navigation_ = nullptr;
}
void HTMLFormElement::submitFromJavaScript() {
- Submit(nullptr, nullptr);
+ ScheduleFormSubmission(nullptr, nullptr);
}
void HTMLFormElement::requestSubmit(ExceptionState& exception_state) {
@@ -362,7 +345,7 @@ void HTMLFormElement::requestSubmit(HTMLElement* submitter,
// 1. If submitter was given, then:
if (submitter) {
// 1.1. If submitter is not a submit button, then throw a TypeError.
- control = ToHTMLFormControlElementOrNull(submitter);
+ control = DynamicTo<HTMLFormControlElement>(submitter);
// button[type] is a subset of input[type]. So it's ok to compare button's
// type and input_type_names.
if (!control || (control->type() != input_type_names::kSubmit &&
@@ -393,8 +376,9 @@ void HTMLFormElement::SubmitDialog(FormSubmission* form_submission) {
}
}
-void HTMLFormElement::Submit(Event* event,
- HTMLFormControlElement* submit_button) {
+void HTMLFormElement::ScheduleFormSubmission(
+ const Event* event,
+ HTMLFormControlElement* submit_button) {
LocalFrameView* view = GetDocument().View();
LocalFrame* frame = GetDocument().GetFrame();
if (!view || !frame || !frame->GetPage())
@@ -405,7 +389,7 @@ void HTMLFormElement::Submit(Event* event,
// or its active sandboxing flag set has its sandboxed forms browsing
// context flag set, then abort these steps without doing anything.
if (!isConnected()) {
- GetDocument().AddConsoleMessage(ConsoleMessage::Create(
+ GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
mojom::ConsoleMessageSource::kJavaScript,
mojom::ConsoleMessageLevel::kWarning,
"Form submission canceled because the form is not connected"));
@@ -413,11 +397,11 @@ void HTMLFormElement::Submit(Event* event,
}
if (is_constructing_entry_list_) {
- GetDocument().AddConsoleMessage(
- ConsoleMessage::Create(mojom::ConsoleMessageSource::kJavaScript,
- mojom::ConsoleMessageLevel::kWarning,
- "Form submission canceled because the form is "
- "constructing entry list"));
+ GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
+ mojom::ConsoleMessageSource::kJavaScript,
+ mojom::ConsoleMessageLevel::kWarning,
+ "Form submission canceled because the form is "
+ "constructing entry list"));
return;
}
@@ -447,25 +431,90 @@ void HTMLFormElement::Submit(Event* event,
FormSubmission* form_submission =
FormSubmission::Create(this, attributes_, event, submit_button);
+ Frame* target_frame = form_submission->TargetFrame();
+
// 'formdata' event handlers might disconnect the form.
if (!isConnected()) {
- GetDocument().AddConsoleMessage(ConsoleMessage::Create(
+ GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
mojom::ConsoleMessageSource::kJavaScript,
mojom::ConsoleMessageLevel::kWarning,
"Form submission canceled because the form is not connected"));
return;
}
+
if (form_submission->Method() == FormSubmission::kDialogMethod) {
SubmitDialog(form_submission);
- } else if (in_user_js_submit_event_ || activated_submit_button_) {
- // Need to postpone the submission in order to make this cancelable by
- // another submission request.
- planned_navigation_ = form_submission;
- } else {
- // This runs JavaScript code if action attribute value is javascript:
- // protocol.
- SubmitForm(form_submission);
+ return;
+ }
+
+ DCHECK(form_submission->Method() == FormSubmission::kPostMethod ||
+ form_submission->Method() == FormSubmission::kGetMethod);
+ DCHECK(form_submission->Data());
+ DCHECK(form_submission->Form());
+ if (form_submission->Action().IsEmpty())
+ return;
+ if (GetDocument().IsSandboxed(mojom::blink::WebSandboxFlags::kForms)) {
+ // FIXME: This message should be moved off the console once a solution to
+ // https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
+ GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
+ mojom::blink::ConsoleMessageSource::kSecurity,
+ mojom::blink::ConsoleMessageLevel::kError,
+ "Blocked form submission to '" +
+ form_submission->Action().ElidedString() +
+ "' because the form's frame is sandboxed and the 'allow-forms' "
+ "permission is not set."));
+ return;
+ }
+
+ if (!GetDocument().GetContentSecurityPolicy()->AllowFormAction(
+ form_submission->Action())) {
+ return;
+ }
+
+ UseCounter::Count(GetDocument(), WebFeature::kFormsSubmitted);
+ if (MixedContentChecker::IsMixedFormAction(GetDocument().GetFrame(),
+ form_submission->Action())) {
+ UseCounter::Count(GetDocument(), WebFeature::kMixedContentFormsSubmitted);
+ }
+ if (FastHasAttribute(html_names::kDisabledAttr)) {
+ UseCounter::Count(GetDocument(),
+ WebFeature::kFormDisabledAttributePresentAndSubmit);
}
+
+ if (!target_frame)
+ return;
+
+ if (form_submission->Action().ProtocolIsJavaScript()) {
+ // For javascript urls, don't post a task to execute the form submission
+ // because we already get another task posted for it in
+ // Document::ProcessJavascriptUrl. If we post two tasks, the javascript will
+ // be run too late according to some tests.
+ form_submission->Navigate();
+ return;
+ }
+
+ FrameScheduler* scheduler = GetDocument().GetFrame()->GetFrameScheduler();
+
+ if (auto* target_local_frame = DynamicTo<LocalFrame>(target_frame)) {
+ if (!target_local_frame->IsNavigationAllowed())
+ return;
+
+ // Cancel parsing if the form submission is targeted at this frame.
+ if (target_local_frame == GetDocument().GetFrame() &&
+ !form_submission->Action().ProtocolIsJavaScript()) {
+ target_local_frame->GetDocument()->CancelParsing();
+ }
+
+ // Use the target frame's frame scheduler. If we can't due to targeting a
+ // RemoteFrame, then use the frame scheduler from the frame this form is in.
+ scheduler = target_local_frame->GetFrameScheduler();
+
+ // Cancel pending javascript url navigations for the target frame. This new
+ // form submission should take precedence over them.
+ target_local_frame->GetDocument()->CancelPendingJavaScriptUrls();
+ }
+
+ target_frame->ScheduleFormSubmission(scheduler, form_submission);
}
FormData* HTMLFormElement::ConstructEntryList(
@@ -496,46 +545,6 @@ FormData* HTMLFormElement::ConstructEntryList(
return &form_data;
}
-// Actually submit the form - navigate now.
-void HTMLFormElement::SubmitForm(FormSubmission* submission) {
- DCHECK(submission->Method() == FormSubmission::kPostMethod ||
- submission->Method() == FormSubmission::kGetMethod);
- DCHECK(submission->Data());
- DCHECK(submission->Form());
- if (submission->Action().IsEmpty())
- return;
- if (!GetDocument().IsActive())
- return;
- if (GetDocument().IsSandboxed(WebSandboxFlags::kForms)) {
- // FIXME: This message should be moved off the console once a solution to
- // https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
- GetDocument().AddConsoleMessage(ConsoleMessage::Create(
- mojom::ConsoleMessageSource::kSecurity,
- mojom::ConsoleMessageLevel::kError,
- "Blocked form submission to '" + submission->Action().ElidedString() +
- "' because the form's frame is sandboxed and the 'allow-forms' "
- "permission is not set."));
- return;
- }
-
- if (!GetDocument().GetContentSecurityPolicy()->AllowFormAction(
- submission->Action())) {
- return;
- }
-
- UseCounter::Count(GetDocument(), WebFeature::kFormsSubmitted);
- if (MixedContentChecker::IsMixedFormAction(GetDocument().GetFrame(),
- submission->Action())) {
- UseCounter::Count(GetDocument(), WebFeature::kMixedContentFormsSubmitted);
- }
- if (FastHasAttribute(html_names::kDisabledAttr)) {
- UseCounter::Count(GetDocument(),
- WebFeature::kFormDisabledAttributePresentAndSubmit);
- }
-
- submission->Navigate();
-}
-
void HTMLFormElement::reset() {
LocalFrame* frame = GetDocument().GetFrame();
if (is_in_reset_function_ || !frame)
@@ -573,7 +582,9 @@ void HTMLFormElement::ParseAttribute(
// If we're not upgrading insecure requests, and the new action attribute is
// pointing to an insecure "action" location from a secure page it is marked
// as "passive" mixed content.
- if (GetDocument().GetInsecureRequestPolicy() & kUpgradeInsecureRequests)
+ if ((GetDocument().GetSecurityContext().GetInsecureRequestPolicy() &
+ mojom::blink::InsecureRequestPolicy::kUpgradeInsecureRequests) !=
+ mojom::blink::InsecureRequestPolicy::kLeaveInsecureRequestsAlone)
return;
KURL action_url = GetDocument().CompleteURL(
attributes_.Action().IsEmpty() ? GetDocument().Url().GetString()
@@ -608,11 +619,6 @@ void HTMLFormElement::Disassociate(ListedElement& e) {
listed_elements_are_dirty_ = true;
listed_elements_.clear();
RemoveFromPastNamesMap(e.ToHTMLElement());
-
- if (activated_submit_button_ != &e)
- return;
- activated_submit_button_ = nullptr;
- planned_navigation_ = nullptr;
}
bool HTMLFormElement::IsURLAttribute(const Attribute& attribute) const {
@@ -835,7 +841,7 @@ void HTMLFormElement::GetNamedElements(
}
bool HTMLFormElement::ShouldAutocomplete() const {
- return !DeprecatedEqualIgnoringCase(
+ return !EqualIgnoringASCIICase(
FastGetAttribute(html_names::kAutocompleteAttr), "off");
}
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_form_element.h b/chromium/third_party/blink/renderer/core/html/forms/html_form_element.h
index 3375fb2eb17..620c05a3cc9 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_form_element.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_form_element.h
@@ -71,13 +71,14 @@ class CORE_EXPORT HTMLFormElement final : public HTMLElement {
void Disassociate(HTMLImageElement&);
void DidAssociateByParser();
- void PrepareForSubmission(Event*, HTMLFormControlElement* submit_button);
+ void PrepareForSubmission(const Event*,
+ HTMLFormControlElement* submit_button);
void submitFromJavaScript();
void requestSubmit(ExceptionState& exception_state);
void requestSubmit(HTMLElement* submitter, ExceptionState& exception_state);
void reset();
- void SubmitImplicitly(Event&, bool from_implicit_submission_trigger);
+ void SubmitImplicitly(const Event&, bool from_implicit_submission_trigger);
String GetName() const;
@@ -115,12 +116,6 @@ class CORE_EXPORT HTMLFormElement final : public HTMLElement {
unsigned UniqueRendererFormId() const { return unique_renderer_form_id_; }
- // TODO(crbug.com/1013385): Remove WillActivateSubmitButton,
- // DidActivateSubmitButton, and RemovedAssociatedControlElement. They are
- // here temporarily to fix form double-submit.
- void WillActivateSubmitButton(HTMLFormControlElement* element);
- void DidActivateSubmitButton(HTMLFormControlElement* element);
-
private:
InsertionNotificationRequest InsertedInto(ContainerNode&) override;
void RemovedFrom(ContainerNode&) override;
@@ -137,9 +132,8 @@ class CORE_EXPORT HTMLFormElement final : public HTMLElement {
}
void SubmitDialog(FormSubmission*);
- void Submit(Event*, HTMLFormControlElement* submit_button);
-
- void SubmitForm(FormSubmission*);
+ void ScheduleFormSubmission(const Event*,
+ HTMLFormControlElement* submit_button);
void CollectListedElements(Node& root, ListedElement::List&) const;
void CollectImageElements(Node& root, HeapVector<Member<HTMLImageElement>>&);
@@ -168,11 +162,6 @@ class CORE_EXPORT HTMLFormElement final : public HTMLElement {
// Do not access image_elements_ directly. Use ImageElements() instead.
HeapVector<Member<HTMLImageElement>> image_elements_;
- // https://html.spec.whatwg.org/C/#planned-navigation
- // Unlike the specification, we use this only for web-exposed submit()
- // function in 'submit' event handler.
- Member<FormSubmission> planned_navigation_;
-
unsigned unique_renderer_form_id_;
bool is_submitting_ = false;
@@ -185,8 +174,6 @@ class CORE_EXPORT HTMLFormElement final : public HTMLElement {
bool has_elements_associated_by_form_attribute_ : 1;
bool did_finish_parsing_children_ : 1;
bool is_in_reset_function_ : 1;
-
- Member<HTMLFormControlElement> activated_submit_button_;
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_input_element.cc b/chromium/third_party/blink/renderer/core/html/forms/html_input_element.cc
index 2ef0a61f534..ce0b6a33afb 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_input_element.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_input_element.cc
@@ -32,7 +32,6 @@
#include "third_party/blink/public/mojom/choosers/date_time_chooser.mojom-blink.h"
#include "third_party/blink/public/platform/task_type.h"
-#include "third_party/blink/public/platform/web_scroll_into_view_params.h"
#include "third_party/blink/public/strings/grit/blink_strings.h"
#include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
#include "third_party/blink/renderer/bindings/core/v8/script_event_listener.h"
@@ -50,6 +49,7 @@
#include "third_party/blink/renderer/core/events/before_text_inserted_event.h"
#include "third_party/blink/renderer/core/events/keyboard_event.h"
#include "third_party/blink/renderer/core/events/mouse_event.h"
+#include "third_party/blink/renderer/core/fileapi/file_list.h"
#include "third_party/blink/renderer/core/frame/deprecation.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
@@ -81,6 +81,7 @@
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/text/platform_locale.h"
#include "third_party/blink/renderer/platform/wtf/math_extras.h"
+#include "ui/base/ui_base_features.h"
namespace blink {
@@ -185,27 +186,27 @@ bool HTMLInputElement::IsValidValue(const String& value) const {
}
bool HTMLInputElement::TooLong() const {
- return willValidate() && TooLong(value(), kCheckDirtyFlag);
+ return TooLong(value(), kCheckDirtyFlag);
}
bool HTMLInputElement::TooShort() const {
- return willValidate() && TooShort(value(), kCheckDirtyFlag);
+ return TooShort(value(), kCheckDirtyFlag);
}
bool HTMLInputElement::TypeMismatch() const {
- return willValidate() && input_type_->TypeMismatch();
+ return input_type_->TypeMismatch();
}
bool HTMLInputElement::ValueMissing() const {
- return willValidate() && input_type_->ValueMissing(value());
+ return input_type_->ValueMissing(value());
}
bool HTMLInputElement::HasBadInput() const {
- return willValidate() && input_type_view_->HasBadInput();
+ return input_type_view_->HasBadInput();
}
bool HTMLInputElement::PatternMismatch() const {
- return willValidate() && input_type_->PatternMismatch(value());
+ return input_type_->PatternMismatch(value());
}
bool HTMLInputElement::TooLong(const String& value,
@@ -219,17 +220,16 @@ bool HTMLInputElement::TooShort(const String& value,
}
bool HTMLInputElement::RangeUnderflow() const {
- return willValidate() && input_type_->RangeUnderflow(value());
+ return input_type_->RangeUnderflow(value());
}
bool HTMLInputElement::RangeOverflow() const {
- return willValidate() && input_type_->RangeOverflow(value());
+ return input_type_->RangeOverflow(value());
}
String HTMLInputElement::validationMessage() const {
if (!willValidate())
return String();
-
if (CustomError())
return CustomValidationMessage();
@@ -237,7 +237,7 @@ String HTMLInputElement::validationMessage() const {
}
String HTMLInputElement::ValidationSubMessage() const {
- if (!willValidate() || CustomError())
+ if (CustomError())
return String();
return input_type_->ValidationMessage(*input_type_view_).second;
}
@@ -251,7 +251,7 @@ double HTMLInputElement::Maximum() const {
}
bool HTMLInputElement::StepMismatch() const {
- return willValidate() && input_type_->StepMismatch(value());
+ return input_type_->StepMismatch(value());
}
bool HTMLInputElement::GetAllowedValueStep(Decimal* step) const {
@@ -295,6 +295,16 @@ bool HTMLInputElement::MayTriggerVirtualKeyboard() const {
return input_type_->MayTriggerVirtualKeyboard();
}
+bool HTMLInputElement::ShouldHaveFocusAppearance() const {
+ // For FormControlsRefresh don't draw focus ring for an input that has its
+ // popup open.
+ if (::features::IsFormControlsRefreshEnabled() &&
+ input_type_view_->HasOpenedPopup())
+ return false;
+
+ return TextControlElement::ShouldHaveFocusAppearance();
+}
+
void HTMLInputElement::UpdateFocusAppearanceWithOptions(
SelectionBehaviorOnFocus selection_behavior,
const FocusOptions* options) {
@@ -312,11 +322,13 @@ void HTMLInputElement::UpdateFocusAppearanceWithOptions(
// TODO(tkent): scrollRectToVisible is a workaround of a bug of
// FrameSelection::revealSelection(). It doesn't scroll correctly in a
// case of RangeSelection. crbug.com/443061.
- GetDocument().EnsurePaintLocationDataValidForNode(this);
+ GetDocument().EnsurePaintLocationDataValidForNode(
+ this, DocumentUpdateReason::kFocus);
if (!options->preventScroll()) {
if (GetLayoutObject()) {
- GetLayoutObject()->ScrollRectToVisible(BoundingBoxForScrollIntoView(),
- WebScrollIntoViewParams());
+ GetLayoutObject()->ScrollRectToVisible(
+ BoundingBoxForScrollIntoView(),
+ ScrollAlignment::CreateScrollIntoViewParams());
}
if (GetDocument().GetFrame())
GetDocument().GetFrame()->Selection().RevealSelection();
@@ -343,7 +355,7 @@ void HTMLInputElement::EndEditing() {
void HTMLInputElement::DispatchFocusInEvent(
const AtomicString& event_type,
Element* old_focused_element,
- WebFocusType type,
+ mojom::blink::FocusType type,
InputDeviceCapabilities* source_capabilities) {
if (event_type == event_type_names::kDOMFocusIn)
input_type_view_->HandleFocusInEvent(old_focused_element, type);
@@ -597,6 +609,20 @@ bool HTMLInputElement::CanStartSelection() const {
return TextControlElement::CanStartSelection();
}
+base::Optional<uint32_t> HTMLInputElement::selectionStartForBinding(
+ ExceptionState& exception_state) const {
+ if (!input_type_->SupportsSelectionAPI())
+ return base::nullopt;
+ return TextControlElement::selectionStart();
+}
+
+base::Optional<uint32_t> HTMLInputElement::selectionEndForBinding(
+ ExceptionState& exception_state) const {
+ if (!input_type_->SupportsSelectionAPI())
+ return base::nullopt;
+ return TextControlElement::selectionEnd();
+}
+
unsigned HTMLInputElement::selectionStartForBinding(
bool& is_null,
ExceptionState& exception_state) const {
@@ -626,6 +652,32 @@ String HTMLInputElement::selectionDirectionForBinding(
}
void HTMLInputElement::setSelectionStartForBinding(
+ base::Optional<uint32_t> start,
+ ExceptionState& exception_state) {
+ if (!input_type_->SupportsSelectionAPI()) {
+ exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+ "The input element's type ('" +
+ input_type_->FormControlType() +
+ "') does not support selection.");
+ return;
+ }
+ TextControlElement::setSelectionStart(start.value_or(0));
+}
+
+void HTMLInputElement::setSelectionEndForBinding(
+ base::Optional<uint32_t> end,
+ ExceptionState& exception_state) {
+ if (!input_type_->SupportsSelectionAPI()) {
+ exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+ "The input element's type ('" +
+ input_type_->FormControlType() +
+ "') does not support selection.");
+ return;
+ }
+ TextControlElement::setSelectionEnd(end.value_or(0));
+}
+
+void HTMLInputElement::setSelectionStartForBinding(
unsigned start,
bool is_null,
ExceptionState& exception_state) {
@@ -751,7 +803,7 @@ void HTMLInputElement::ParseAttribute(
AddToRadioButtonGroup();
TextControlElement::ParseAttribute(params);
} else if (name == html_names::kAutocompleteAttr) {
- if (DeprecatedEqualIgnoringCase(value, "off")) {
+ if (EqualIgnoringASCIICase(value, "off")) {
autocomplete_ = kOff;
} else {
if (value.IsEmpty())
@@ -802,7 +854,7 @@ void HTMLInputElement::ParseAttribute(
size_ = size;
if (GetLayoutObject()) {
GetLayoutObject()
- ->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(
+ ->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
layout_invalidation_reason::kAttributeChanged);
}
}
@@ -885,6 +937,11 @@ bool HTMLInputElement::LayoutObjectIsNeeded(const ComputedStyle& style) const {
TextControlElement::LayoutObjectIsNeeded(style);
}
+// TODO(crbug.com/1040826): Remove this override.
+bool HTMLInputElement::TypeShouldForceLegacyLayout() const {
+ return input_type_view_->TypeShouldForceLegacyLayout();
+}
+
LayoutObject* HTMLInputElement::CreateLayoutObject(const ComputedStyle& style,
LegacyLayout legacy) {
return input_type_view_->CreateLayoutObject(style, legacy);
@@ -961,12 +1018,13 @@ bool HTMLInputElement::HasBeenPasswordField() const {
}
void HTMLInputElement::DispatchChangeEventIfNeeded() {
- if (input_type_->ShouldSendChangeEventAfterCheckedChanged())
+ if (isConnected() && input_type_->ShouldSendChangeEventAfterCheckedChanged())
DispatchChangeEvent();
}
void HTMLInputElement::DispatchInputAndChangeEventIfNeeded() {
- if (input_type_->ShouldSendChangeEventAfterCheckedChanged()) {
+ if (isConnected() &&
+ input_type_->ShouldSendChangeEventAfterCheckedChanged()) {
DispatchInputEvent();
DispatchChangeEvent();
}
@@ -983,6 +1041,7 @@ void HTMLInputElement::setChecked(bool now_checked,
if (checked() == now_checked)
return;
+ input_type_->WillUpdateCheckedness(now_checked);
is_checked_ = now_checked;
if (RadioButtonGroupScope* scope = GetRadioButtonGroupScope())
@@ -1069,6 +1128,10 @@ String HTMLInputElement::value() const {
return g_empty_string;
}
+String HTMLInputElement::rawValue() const {
+ return input_type_view_->RawValue();
+}
+
String HTMLInputElement::ValueOrDefaultLabel() const {
String value = this->value();
if (!value.IsNull())
@@ -1150,23 +1213,6 @@ void HTMLInputElement::setValue(const String& value,
if (value_changed)
NotifyFormStateChanged();
-
- if (isConnected()) {
- if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache()) {
- auto* page = GetDocument().GetPage();
- auto* view = GetDocument().View();
- // Run the document lifecycle to ensure AX notifications fire,
- // even if the value didn't change.
- if (page && view) {
- // TODO(aboxhall): add a lifecycle phase for accessibility updates.
- if (!view->CanThrottleRendering())
- page->Animator().ScheduleVisualUpdate(GetDocument().GetFrame());
-
- GetDocument().Lifecycle().EnsureStateAtMost(
- DocumentLifecycle::kVisualUpdatePending);
- }
- }
- }
}
void HTMLInputElement::SetNonAttributeValue(const String& sanitized_value) {
@@ -1214,7 +1260,7 @@ void HTMLInputElement::setValueAsDate(ScriptState* script_state,
ExceptionState& exception_state) {
UseCounter::Count(GetDocument(), WebFeature::kInputElementValueAsDateSetter);
base::Optional<base::Time> date =
- NativeValueTraits<IDLDateOrNull>::NativeValue(
+ NativeValueTraits<IDLNullable<IDLDate>>::NativeValue(
script_state->GetIsolate(), value.V8Value(), exception_state);
if (exception_state.HadException())
return;
@@ -1278,12 +1324,12 @@ EventDispatchHandlingState* HTMLInputElement::PreDispatchEventHandler(
}
if (event.type() != event_type_names::kClick)
return nullptr;
- if (!event.IsMouseEvent() ||
- ToMouseEvent(event).button() !=
+
+ auto* mouse_event = DynamicTo<MouseEvent>(event);
+ if (!mouse_event ||
+ mouse_event->button() !=
static_cast<int16_t>(WebPointerProperties::Button::kLeft))
return nullptr;
- if (formOwner() && CanBeSuccessfulSubmitButton())
- formOwner()->WillActivateSubmitButton(this);
return input_type_view_->WillDispatchClick();
}
@@ -1296,29 +1342,19 @@ void HTMLInputElement::PostDispatchEventHandler(
*static_cast<ClickHandlingState*>(state));
}
-void HTMLInputElement::DidPreventDefault(const Event& event) {
- if (auto* form = formOwner())
- form->DidActivateSubmitButton(this);
-}
-
-void HTMLInputElement::DefaultEventHandler(Event& event) {
- DefaultEventHandlerInternal(event);
-
- if (event.type() == event_type_names::kDOMActivate && formOwner())
- formOwner()->DidActivateSubmitButton(this);
-}
-
-void HTMLInputElement::DefaultEventHandlerInternal(Event& evt) {
- if (evt.IsMouseEvent() && evt.type() == event_type_names::kClick &&
- ToMouseEvent(evt).button() ==
+void HTMLInputElement::DefaultEventHandler(Event& evt) {
+ auto* mouse_event = DynamicTo<MouseEvent>(evt);
+ if (mouse_event && evt.type() == event_type_names::kClick &&
+ mouse_event->button() ==
static_cast<int16_t>(WebPointerProperties::Button::kLeft)) {
- input_type_view_->HandleClickEvent(ToMouseEvent(evt));
+ input_type_view_->HandleClickEvent(To<MouseEvent>(evt));
if (evt.DefaultHandled())
return;
}
- if (evt.IsKeyboardEvent() && evt.type() == event_type_names::kKeydown) {
- input_type_view_->HandleKeydownEvent(ToKeyboardEvent(evt));
+ auto* keyboad_event = DynamicTo<KeyboardEvent>(evt);
+ if (keyboad_event && evt.type() == event_type_names::kKeydown) {
+ input_type_view_->HandleKeydownEvent(*keyboad_event);
if (evt.DefaultHandled())
return;
}
@@ -1349,14 +1385,14 @@ void HTMLInputElement::DefaultEventHandlerInternal(Event& evt) {
// Use key press event here since sending simulated mouse events
// on key down blocks the proper sending of the key press event.
- if (evt.IsKeyboardEvent() && evt.type() == event_type_names::kKeypress) {
- input_type_view_->HandleKeypressEvent(ToKeyboardEvent(evt));
+ if (keyboad_event && evt.type() == event_type_names::kKeypress) {
+ input_type_view_->HandleKeypressEvent(*keyboad_event);
if (evt.DefaultHandled())
return;
}
- if (evt.IsKeyboardEvent() && evt.type() == event_type_names::kKeyup) {
- input_type_view_->HandleKeyupEvent(ToKeyboardEvent(evt));
+ if (keyboad_event && evt.type() == event_type_names::kKeyup) {
+ input_type_view_->HandleKeyupEvent(*keyboad_event);
if (evt.DefaultHandled())
return;
}
@@ -1390,8 +1426,8 @@ void HTMLInputElement::DefaultEventHandlerInternal(Event& evt) {
static_cast<BeforeTextInsertedEvent&>(evt));
}
- if (evt.IsMouseEvent() && evt.type() == event_type_names::kMousedown) {
- input_type_view_->HandleMouseDownEvent(ToMouseEvent(evt));
+ if (mouse_event && evt.type() == event_type_names::kMousedown) {
+ input_type_view_->HandleMouseDownEvent(*mouse_event);
if (evt.DefaultHandled())
return;
}
@@ -1540,8 +1576,12 @@ void HTMLInputElement::SetCanReceiveDroppedFiles(
if (!!can_receive_dropped_files_ == can_receive_dropped_files)
return;
can_receive_dropped_files_ = can_receive_dropped_files;
- if (GetLayoutObject())
- GetLayoutObject()->UpdateFromElement();
+ if (HTMLInputElement* button = UploadButton())
+ button->SetActive(can_receive_dropped_files);
+}
+
+HTMLInputElement* HTMLInputElement::UploadButton() const {
+ return input_type_view_->UploadButton();
}
String HTMLInputElement::SanitizeValue(const String& proposed_value) const {
@@ -1647,9 +1687,8 @@ void HTMLInputElement::SelectColorInColorChooser(const Color& color) {
client->DidChooseColor(color);
}
-void HTMLInputElement::EndColorChooser() {
- if (ColorChooserClient* client = input_type_->GetColorChooserClient())
- client->DidEndChooser();
+void HTMLInputElement::EndColorChooserForTesting() {
+ input_type_view_->ClosePopupView();
}
HTMLElement* HTMLInputElement::list() const {
@@ -1779,24 +1818,16 @@ String HTMLInputElement::DefaultToolTip() const {
return input_type_->DefaultToolTip(*input_type_view_);
}
-bool HTMLInputElement::ShouldAppearIndeterminate() const {
- return input_type_->ShouldAppearIndeterminate();
+String HTMLInputElement::FileStatusText() const {
+ return input_type_view_->FileStatusText();
}
-bool HTMLInputElement::IsInRequiredRadioButtonGroup() {
- // TODO(tkent): Remove type check.
- DCHECK_EQ(type(), input_type_names::kRadio);
- if (RadioButtonGroupScope* scope = GetRadioButtonGroupScope())
- return scope->IsInRequiredGroup(this);
- return false;
+bool HTMLInputElement::ShouldApplyMiddleEllipsis() const {
+ return files() && files()->length() <= 1;
}
-HTMLInputElement* HTMLInputElement::CheckedRadioButtonForGroup() {
- if (checked())
- return this;
- if (RadioButtonGroupScope* scope = GetRadioButtonGroupScope())
- return scope->CheckedButtonForGroup(GetName());
- return nullptr;
+bool HTMLInputElement::ShouldAppearIndeterminate() const {
+ return input_type_->ShouldAppearIndeterminate();
}
RadioButtonGroupScope* HTMLInputElement::GetRadioButtonGroupScope() const {
@@ -1962,8 +1993,9 @@ bool HTMLInputElement::IsInteractiveContent() const {
}
scoped_refptr<ComputedStyle> HTMLInputElement::CustomStyleForLayoutObject() {
- return input_type_view_->CustomStyleForLayoutObject(
- OriginalStyleForLayoutObject());
+ scoped_refptr<ComputedStyle> style = OriginalStyleForLayoutObject();
+ input_type_view_->CustomStyleForLayoutObject(*style);
+ return style;
}
void HTMLInputElement::DidRecalcStyle(const StyleRecalcChange change) {
@@ -2013,4 +2045,8 @@ PaintLayerScrollableArea* HTMLInputElement::GetScrollableArea() const {
return Element::GetScrollableArea();
}
+bool HTMLInputElement::IsDraggedSlider() const {
+ return input_type_view_->IsDraggedSlider();
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_input_element.h b/chromium/third_party/blink/renderer/core/html/forms/html_input_element.h
index 67291f93ee4..9b0f10e05d9 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_input_element.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_input_element.h
@@ -26,6 +26,7 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_HTML_INPUT_ELEMENT_H_
#include "base/gtest_prod_util.h"
+#include "third_party/blink/public/mojom/input/focus_type.mojom-blink-forward.h"
#include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/dom/create_element_flags.h"
@@ -127,6 +128,9 @@ class CORE_EXPORT HTMLInputElement
bool ShouldAppearChecked() const;
bool ShouldAppearIndeterminate() const override;
+ // Returns null if this isn't associated with any radio button group.
+ RadioButtonGroupScope* GetRadioButtonGroupScope() const;
+
unsigned size() const;
bool SizeShouldIncludeDecoration(int& preferred_size) const;
@@ -151,6 +155,8 @@ class CORE_EXPORT HTMLInputElement
bool IsValidValue(const String&) const;
bool HasDirtyValue() const;
+ String rawValue() const;
+
String SanitizeValue(const String&) const;
String LocalizeValue(const String&) const;
@@ -177,11 +183,22 @@ class CORE_EXPORT HTMLInputElement
// delay the 'input' event with EventQueueScope.
void SetValueFromRenderer(const String&);
- unsigned selectionStartForBinding(bool&, ExceptionState&) const;
- unsigned selectionEndForBinding(bool&, ExceptionState&) const;
+ base::Optional<uint32_t> selectionStartForBinding(ExceptionState&) const;
+ base::Optional<uint32_t> selectionEndForBinding(ExceptionState&) const;
+ // TODO(crbug.com/1060971): Remove |is_null| version.
+ unsigned selectionStartForBinding(bool&,
+ ExceptionState&) const; // DEPRECATED
+ unsigned selectionEndForBinding(bool&, ExceptionState&) const; // DEPRECATED
String selectionDirectionForBinding(ExceptionState&) const;
- void setSelectionStartForBinding(unsigned, bool is_null, ExceptionState&);
- void setSelectionEndForBinding(unsigned, bool is_null, ExceptionState&);
+ void setSelectionStartForBinding(base::Optional<uint32_t>, ExceptionState&);
+ void setSelectionEndForBinding(base::Optional<uint32_t>, ExceptionState&);
+ // TODO(crbug.com/1060971): Remove |is_null| version.
+ void setSelectionStartForBinding(unsigned,
+ bool is_null,
+ ExceptionState&); // DEPRECATED
+ void setSelectionEndForBinding(unsigned,
+ bool is_null,
+ ExceptionState&); // DEPRECATED
void setSelectionDirectionForBinding(const String&, ExceptionState&);
void setSelectionRangeForBinding(unsigned start,
unsigned end,
@@ -232,6 +249,10 @@ class CORE_EXPORT HTMLInputElement
bool CanReceiveDroppedFiles() const;
void SetCanReceiveDroppedFiles(bool);
+ // Returns 'Choose File(s)' button in a file control. This returns
+ // nullptr for other input types.
+ HTMLInputElement* UploadButton() const;
+
void OnSearch();
void UpdateClearButtonVisibility();
@@ -245,9 +266,6 @@ class CORE_EXPORT HTMLInputElement
// Associated <datalist> options which match to the current INPUT value.
HeapVector<Member<HTMLOptionElement>> FilteredDataListOptions() const;
- HTMLInputElement* CheckedRadioButtonForGroup();
- bool IsInRequiredRadioButtonGroup();
-
// Functions for InputType classes.
void SetNonAttributeValue(const String&);
void SetNonAttributeValueByUserEdit(const String&);
@@ -257,10 +275,18 @@ class CORE_EXPORT HTMLInputElement
// For test purposes.
void SelectColorInColorChooser(const Color&);
- void EndColorChooser();
+ void EndColorChooserForTesting();
String DefaultToolTip() const override;
+ // Type=file only: Text not in the button such as "No file chosen". The string
+ // is not truncated by ellipsis.
+ // Return a null string for other types.
+ String FileStatusText() const;
+ // Returns true if an ellipsis should be injected at the middle of the text.
+ // This function is called only if text-overflow:ellipsis is specified.
+ bool ShouldApplyMiddleEllipsis() const;
+
unsigned height() const;
unsigned width() const;
void setHeight(unsigned);
@@ -315,6 +341,8 @@ class CORE_EXPORT HTMLInputElement
void SetHasBeenPasswordField() { has_been_password_field_ = true; }
+ bool IsDraggedSlider() const;
+
protected:
void DefaultEventHandler(Event&) override;
void CreateShadowSubtree();
@@ -332,6 +360,7 @@ class CORE_EXPORT HTMLInputElement
bool HasCustomFocusLogic() const final;
bool IsKeyboardFocusable() const final;
bool MayTriggerVirtualKeyboard() const final;
+ bool ShouldHaveFocusAppearance() const final;
bool IsEnumeratable() const final;
bool IsInteractiveContent() const final;
bool IsLabelable() const final;
@@ -361,6 +390,7 @@ class CORE_EXPORT HTMLInputElement
void CloneNonAttributePropertiesFrom(const Element&, CloneChildrenFlag) final;
+ bool TypeShouldForceLegacyLayout() const final;
void AttachLayoutTree(AttachContext&) final;
void AppendToFormData(FormData&) final;
@@ -372,11 +402,6 @@ class CORE_EXPORT HTMLInputElement
EventDispatchHandlingState* PreDispatchEventHandler(Event&) final;
void PostDispatchEventHandler(Event&, EventDispatchHandlingState*) final;
- // TODO(crbug.com/1013385): Remove DidPreventDefault and
- // DefaultEventHandlerInternal. They are here as a temporary fix for form
- // double-submit.
- void DidPreventDefault(const Event&) final;
- void DefaultEventHandlerInternal(Event& evt);
bool IsURLAttribute(const Attribute&) const final;
bool HasLegalLinkAttribute(const QualifiedName&) const final;
@@ -392,7 +417,7 @@ class CORE_EXPORT HTMLInputElement
void HandleBlurEvent() final;
void DispatchFocusInEvent(const AtomicString& event_type,
Element* old_focused_element,
- WebFocusType,
+ mojom::blink::FocusType,
InputDeviceCapabilities* source_capabilities) final;
bool IsOptionalFormControl() const final { return !IsRequiredFormControl(); }
@@ -409,8 +434,6 @@ class CORE_EXPORT HTMLInputElement
void SetListAttributeTargetObserver(ListAttributeTargetObserver*);
void ResetListAttributeTargetObserver();
- // Returns null if this isn't associated with any radio button group.
- RadioButtonGroupScope* GetRadioButtonGroupScope() const;
void AddToRadioButtonGroup();
void RemoveFromRadioButtonGroup();
scoped_refptr<ComputedStyle> CustomStyleForLayoutObject() override;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_input_element.idl b/chromium/third_party/blink/renderer/core/html/forms/html_input_element.idl
index 48bb6afc2a6..3c153f49fd6 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_input_element.idl
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_input_element.idl
@@ -81,6 +81,8 @@ enum SelectionMode { "select", "start", "end", "preserve" };
readonly attribute NodeList labels;
+ [RuntimeEnabled=InputElementRawValue] readonly attribute DOMString rawValue;
+
void select();
[RaisesException, ImplementedAs=selectionStartForBinding] attribute unsigned long? selectionStart;
[RaisesException, ImplementedAs=selectionEndForBinding] attribute unsigned long? selectionEnd;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_input_element_test.cc b/chromium/third_party/blink/renderer/core/html/forms/html_input_element_test.cc
index f14da1cfcd8..baa2c174681 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_input_element_test.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_input_element_test.cc
@@ -7,9 +7,9 @@
#include <memory>
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_keyboard_event_init.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/events/keyboard_event.h"
-#include "third_party/blink/renderer/core/events/keyboard_event_init.h"
#include "third_party/blink/renderer/core/fileapi/file_list.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/visual_viewport.h"
@@ -35,16 +35,16 @@ class HTMLInputElementTest : public PageTestBase {
};
TEST_F(HTMLInputElementTest, FilteredDataListOptionsNoList) {
- GetDocument().documentElement()->SetInnerHTMLFromString("<input id=test>");
+ GetDocument().documentElement()->setInnerHTML("<input id=test>");
EXPECT_TRUE(TestElement().FilteredDataListOptions().IsEmpty());
- GetDocument().documentElement()->SetInnerHTMLFromString(
+ GetDocument().documentElement()->setInnerHTML(
"<input id=test list=dl1><datalist id=dl1></datalist>");
EXPECT_TRUE(TestElement().FilteredDataListOptions().IsEmpty());
}
TEST_F(HTMLInputElementTest, FilteredDataListOptionsContain) {
- GetDocument().documentElement()->SetInnerHTMLFromString(
+ GetDocument().documentElement()->setInnerHTML(
"<input id=test value=BC list=dl2>"
"<datalist id=dl2>"
"<option>AbC DEF</option>"
@@ -56,7 +56,7 @@ TEST_F(HTMLInputElementTest, FilteredDataListOptionsContain) {
EXPECT_EQ("AbC DEF", options[0]->value().Utf8());
EXPECT_EQ("ghi", options[1]->value().Utf8());
- GetDocument().documentElement()->SetInnerHTMLFromString(
+ GetDocument().documentElement()->setInnerHTML(
"<input id=test value=i list=dl2>"
"<datalist id=dl2>"
"<option>I</option>"
@@ -70,7 +70,7 @@ TEST_F(HTMLInputElementTest, FilteredDataListOptionsContain) {
}
TEST_F(HTMLInputElementTest, FilteredDataListOptionsForMultipleEmail) {
- GetDocument().documentElement()->SetInnerHTMLFromString(R"HTML(
+ GetDocument().documentElement()->setInnerHTML(R"HTML(
<input id=test value='foo@example.com, tkent' list=dl3 type=email
multiple>
<datalist id=dl3>
@@ -104,7 +104,7 @@ TEST_F(HTMLInputElementTest, NoAssertWhenMovedInNewDocument) {
// Create an input element with type "range" inside a document without frame.
To<HTMLBodyElement>(html->firstChild())
- ->SetInnerHTMLFromString("<input type='range' />");
+ ->setInnerHTML("<input type='range' />");
document_without_frame->AppendChild(html);
auto page_holder = std::make_unique<DummyPageHolder>();
@@ -154,7 +154,7 @@ TEST_F(HTMLInputElementTest, ImageTypeCrash) {
TEST_F(HTMLInputElementTest, RadioKeyDownDCHECKFailure) {
// crbug.com/697286
- GetDocument().body()->SetInnerHTMLFromString(
+ GetDocument().body()->setInnerHTML(
"<input type=radio name=g><input type=radio name=g>");
auto& radio1 = To<HTMLInputElement>(*GetDocument().body()->firstChild());
auto& radio2 = To<HTMLInputElement>(*radio1.nextSibling());
@@ -171,7 +171,7 @@ TEST_F(HTMLInputElementTest, RadioKeyDownDCHECKFailure) {
TEST_F(HTMLInputElementTest, DateTimeChooserSizeParamRespectsScale) {
GetDocument().SetCompatibilityMode(Document::kQuirksMode);
GetDocument().View()->GetFrame().GetPage()->GetVisualViewport().SetScale(2.f);
- GetDocument().body()->SetInnerHTMLFromString(
+ GetDocument().body()->setInnerHTML(
"<input type='date' style='width:200px;height:50px' />");
UpdateAllLifecyclePhasesForTest();
auto* input = To<HTMLInputElement>(GetDocument().body()->firstChild());
@@ -195,13 +195,13 @@ TEST_F(HTMLInputElementTest, StepDownOverflow) {
}
TEST_F(HTMLInputElementTest, CheckboxHasNoShadowRoot) {
- GetDocument().body()->SetInnerHTMLFromString("<input type='checkbox' />");
+ GetDocument().body()->setInnerHTML("<input type='checkbox' />");
auto* input = To<HTMLInputElement>(GetDocument().body()->firstChild());
EXPECT_EQ(nullptr, input->UserAgentShadowRoot());
}
TEST_F(HTMLInputElementTest, ChangingInputTypeCausesShadowRootToBeCreated) {
- GetDocument().body()->SetInnerHTMLFromString("<input type='checkbox' />");
+ GetDocument().body()->setInnerHTML("<input type='checkbox' />");
auto* input = To<HTMLInputElement>(GetDocument().body()->firstChild());
EXPECT_EQ(nullptr, input->UserAgentShadowRoot());
input->setAttribute(html_names::kTypeAttr, "text");
@@ -209,7 +209,7 @@ TEST_F(HTMLInputElementTest, ChangingInputTypeCausesShadowRootToBeCreated) {
}
TEST_F(HTMLInputElementTest, RepaintAfterClearingFile) {
- GetDocument().body()->SetInnerHTMLFromString("<input type='file' />");
+ GetDocument().body()->setInnerHTML("<input type='file' />");
auto* input = To<HTMLInputElement>(GetDocument().body()->firstChild());
FileChooserFileInfoList files;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_label_element.cc b/chromium/third_party/blink/renderer/core/html/forms/html_label_element.cc
index d7f564f3b8f..1e5895b2211 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_label_element.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_label_element.cc
@@ -24,6 +24,7 @@
#include "third_party/blink/renderer/core/html/forms/html_label_element.h"
+#include "third_party/blink/public/mojom/input/focus_type.mojom-blink.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/element_traversal.h"
#include "third_party/blink/renderer/core/editing/editing_utilities.h"
@@ -87,7 +88,7 @@ HTMLElement* HTMLLabelElement::control() const {
HTMLFormElement* HTMLLabelElement::form() const {
if (HTMLElement* control = this->control()) {
- if (auto* form_control_element = ToHTMLFormControlElementOrNull(control))
+ if (auto* form_control_element = DynamicTo<HTMLFormControlElement>(control))
return form_control_element->Form();
if (control->IsFormAssociatedCustomElement())
return control->EnsureElementInternals().Form();
@@ -169,7 +170,8 @@ void HTMLLabelElement::DefaultEventHandler(Event& evt) {
// click event to control element.
// Note: check if it is a MouseEvent because a click event may
// not be an instance of a MouseEvent if created by document.createEvent().
- if (evt.IsMouseEvent() && ToMouseEvent(evt).HasPosition()) {
+ auto* mouse_event = DynamicTo<MouseEvent>(evt);
+ if (mouse_event && mouse_event->HasPosition()) {
if (LocalFrame* frame = GetDocument().GetFrame()) {
// Check if there is a selection and click is not on the
// selection.
@@ -188,14 +190,14 @@ void HTMLLabelElement::DefaultEventHandler(Event& evt) {
// should pass click event to control element.
// Only in case of drag, *neither* we pass the click event,
// *nor* we focus the control element.
- if (is_label_text_selected && ToMouseEvent(evt).ClickCount() == 1)
+ if (is_label_text_selected && mouse_event->ClickCount() == 1)
return;
}
}
processing_click_ = true;
- GetDocument().UpdateStyleAndLayout();
+ GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kInput);
if (element->IsMouseFocusable()) {
// If the label is *not* selected, or if the click happened on
// selection of label, only then focus the control element.
@@ -203,7 +205,7 @@ void HTMLLabelElement::DefaultEventHandler(Event& evt) {
// so do not focus the control element.
if (!is_label_text_selected) {
element->focus(FocusParams(SelectionBehaviorOnFocus::kRestore,
- kWebFocusTypeMouse, nullptr));
+ mojom::blink::FocusType::kMouse, nullptr));
}
}
@@ -236,7 +238,7 @@ void HTMLLabelElement::focus(const FocusParams& params) {
return;
}
- if (params.type == blink::kWebFocusTypeAccessKey)
+ if (params.type == blink::mojom::blink::FocusType::kAccessKey)
return;
// To match other browsers, always restore previous selection.
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_opt_group_element.cc b/chromium/third_party/blink/renderer/core/html/forms/html_opt_group_element.cc
index 3c09fa5d8c1..ae6b2f65cfd 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_opt_group_element.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_opt_group_element.cc
@@ -84,6 +84,30 @@ bool HTMLOptGroupElement::MatchesEnabledPseudoClass() const {
return !IsDisabledFormControl();
}
+void HTMLOptGroupElement::ChildrenChanged(const ChildrenChange& change) {
+ HTMLElement::ChildrenChanged(change);
+ auto* select = OwnerSelectElement();
+ if (!select)
+ return;
+ if (change.type == ChildrenChangeType::kElementInserted) {
+ if (auto* option = DynamicTo<HTMLOptionElement>(change.sibling_changed))
+ select->OptionInserted(*option, option->Selected());
+ } else if (change.type == ChildrenChangeType::kElementRemoved) {
+ if (auto* option = DynamicTo<HTMLOptionElement>(change.sibling_changed))
+ select->OptionRemoved(*option);
+ } else if (change.type == ChildrenChangeType::kAllChildrenRemoved) {
+ DCHECK(change.removed_nodes);
+ for (Node* node : *change.removed_nodes) {
+ if (auto* option = DynamicTo<HTMLOptionElement>(node))
+ select->OptionRemoved(*option);
+ }
+ }
+}
+
+bool HTMLOptGroupElement::ChildrenChangedAllChildrenRemovedNeedsList() const {
+ return true;
+}
+
Node::InsertionNotificationRequest HTMLOptGroupElement::InsertedInto(
ContainerNode& insertion_point) {
HTMLElement::InsertedInto(insertion_point);
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_opt_group_element.h b/chromium/third_party/blink/renderer/core/html/forms/html_opt_group_element.h
index 693aa166b9b..a03db9bb975 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_opt_group_element.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_opt_group_element.h
@@ -52,6 +52,8 @@ class CORE_EXPORT HTMLOptGroupElement final : public HTMLElement {
~HTMLOptGroupElement() override;
bool SupportsFocus() const override;
+ void ChildrenChanged(const ChildrenChange& change) override;
+ bool ChildrenChangedAllChildrenRemovedNeedsList() const override;
void ParseAttribute(const AttributeModificationParams&) override;
void AccessKeyAction(bool send_mouse_events) override;
void DidAddUserAgentShadowRoot(ShadowRoot&) override;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_option_element.cc b/chromium/third_party/blink/renderer/core/html/forms/html_option_element.cc
index 01ce55d9a26..bce8b9e77da 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_option_element.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_option_element.cc
@@ -182,6 +182,8 @@ void HTMLOptionElement::ParseAttribute(
SetSelected(!params.new_value.IsNull());
PseudoStateChanged(CSSSelector::kPseudoDefault);
} else if (name == html_names::kLabelAttr) {
+ if (HTMLSelectElement* select = OwnerSelectElement())
+ select->OptionElementChildrenChanged(*this);
UpdateLabel();
} else {
HTMLElement::ParseAttribute(params);
@@ -249,8 +251,7 @@ void HTMLOptionElement::SetSelectedState(bool selected) {
// notifications only when it's a listbox (and not a menu list). If
// there's no layoutObject, fire them anyway just to be safe (to make sure
// the AX tree is in sync).
- if (!select->GetLayoutObject() ||
- select->GetLayoutObject()->IsListBox()) {
+ if (!select->GetLayoutObject() || !select->UsesMenuList()) {
cache->ListboxOptionStateChanged(this);
cache->ListboxSelectedChildrenChanged(select);
}
@@ -338,30 +339,6 @@ String HTMLOptionElement::DefaultToolTip() const {
return String();
}
-Node::InsertionNotificationRequest HTMLOptionElement::InsertedInto(
- ContainerNode& insertion_point) {
- HTMLElement::InsertedInto(insertion_point);
- if (HTMLSelectElement* select = OwnerSelectElement()) {
- if (&insertion_point == select ||
- (IsA<HTMLOptGroupElement>(insertion_point) &&
- insertion_point.parentNode() == select))
- select->OptionInserted(*this, is_selected_);
- }
- return kInsertionDone;
-}
-
-void HTMLOptionElement::RemovedFrom(ContainerNode& insertion_point) {
- if (auto* select = DynamicTo<HTMLSelectElement>(insertion_point)) {
- if (!parentNode() || IsA<HTMLOptGroupElement>(*parentNode()))
- select->OptionRemoved(*this);
- } else if (IsA<HTMLOptGroupElement>(insertion_point)) {
- select = DynamicTo<HTMLSelectElement>(insertion_point.parentNode());
- if (select)
- select->OptionRemoved(*this);
- }
- HTMLElement::RemovedFrom(insertion_point);
-}
-
String HTMLOptionElement::CollectOptionInnerText() const {
StringBuilder text;
for (Node* node = firstChild(); node;) {
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_option_element.h b/chromium/third_party/blink/renderer/core/html/forms/html_option_element.h
index ce663f6bab0..87e6b6a698e 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_option_element.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_option_element.h
@@ -38,6 +38,13 @@ class CORE_EXPORT HTMLOptionElement final : public HTMLElement {
DEFINE_WRAPPERTYPEINFO();
public:
+ static HTMLOptionElement* CreateForJSConstructor(
+ Document& document,
+ const String& data,
+ ExceptionState& exception_state) {
+ return CreateForJSConstructor(document, data, AtomicString(), false, false,
+ exception_state);
+ }
static HTMLOptionElement* CreateForJSConstructor(Document&,
const String& data,
const AtomicString& value,
@@ -101,8 +108,6 @@ class CORE_EXPORT HTMLOptionElement final : public HTMLElement {
bool MatchesDefaultPseudoClass() const override;
bool MatchesEnabledPseudoClass() const override;
void ParseAttribute(const AttributeModificationParams&) override;
- InsertionNotificationRequest InsertedInto(ContainerNode&) override;
- void RemovedFrom(ContainerNode&) override;
void AccessKeyAction(bool) override;
void ChildrenChanged(const ChildrenChange&) override;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_option_element.idl b/chromium/third_party/blink/renderer/core/html/forms/html_option_element.idl
index fd2eb010c43..a243648d99e 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_option_element.idl
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_option_element.idl
@@ -21,13 +21,15 @@
// https://html.spec.whatwg.org/C/#the-option-element
[
+ ConstructorCallWith=Document,
Exposed=Window,
HTMLConstructor,
- NamedConstructor=Option(optional DOMString data = null,
- optional DOMString value = null,
+ NamedConstructor=Option(optional DOMString data = "",
+ optional DOMString value,
optional boolean defaultSelected = false,
optional boolean selected = false),
- ConstructorCallWith=Document,
+ NamedConstructor_CallWith=Document,
+ NamedConstructor_RaisesException,
RaisesException=Constructor
] interface HTMLOptionElement : HTMLElement {
[CEReactions, Reflect] attribute boolean disabled;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_options_collection.cc b/chromium/third_party/blink/renderer/core/html/forms/html_options_collection.cc
index 13c3d226f3d..569dfb38923 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_options_collection.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_options_collection.cc
@@ -94,17 +94,17 @@ void HTMLOptionsCollection::setLength(unsigned length,
To<HTMLSelectElement>(ownerNode()).setLength(length, exception_state);
}
-bool HTMLOptionsCollection::AnonymousIndexedSetter(
+IndexedPropertySetterResult HTMLOptionsCollection::AnonymousIndexedSetter(
unsigned index,
HTMLOptionElement* value,
ExceptionState& exception_state) {
auto& base = To<HTMLSelectElement>(ownerNode());
if (!value) { // undefined or null
base.remove(index);
- return true;
+ return IndexedPropertySetterResult::kIntercepted;
}
base.SetOption(index, value, exception_state);
- return true;
+ return IndexedPropertySetterResult::kIntercepted;
}
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_options_collection.h b/chromium/third_party/blink/renderer/core/html/forms/html_options_collection.h
index 2f371ef293b..d9e50d98190 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_options_collection.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_options_collection.h
@@ -26,6 +26,8 @@
#include "third_party/blink/renderer/core/html/forms/html_option_element.h"
#include "third_party/blink/renderer/core/html/html_collection.h"
+#include "third_party/blink/renderer/platform/bindings/v8_binding.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
namespace blink {
@@ -53,7 +55,9 @@ class HTMLOptionsCollection final : public HTMLCollection {
void setSelectedIndex(int);
void setLength(unsigned, ExceptionState&);
- bool AnonymousIndexedSetter(unsigned, HTMLOptionElement*, ExceptionState&);
+ IndexedPropertySetterResult AnonymousIndexedSetter(unsigned,
+ HTMLOptionElement*,
+ ExceptionState&);
bool ElementMatches(const HTMLElement&) const;
@@ -61,11 +65,12 @@ class HTMLOptionsCollection final : public HTMLCollection {
void SupportedPropertyNames(Vector<String>& names) override;
};
-DEFINE_TYPE_CASTS(HTMLOptionsCollection,
- LiveNodeListBase,
- collection,
- collection->GetType() == kSelectOptions,
- collection.GetType() == kSelectOptions);
+template <>
+struct DowncastTraits<HTMLOptionsCollection> {
+ static bool AllowFrom(const LiveNodeListBase& collection) {
+ return collection.GetType() == kSelectOptions;
+ }
+};
inline bool HTMLOptionsCollection::ElementMatches(
const HTMLElement& element) const {
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_select_element.cc b/chromium/third_party/blink/renderer/core/html/forms/html_select_element.cc
index a02e8b059ac..a5f4eb0657c 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_select_element.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_select_element.cc
@@ -30,6 +30,7 @@
#include "third_party/blink/renderer/core/html/forms/html_select_element.h"
#include "build/build_config.h"
+#include "third_party/blink/public/mojom/input/focus_type.mojom-blink.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/public/strings/grit/blink_strings.h"
#include "third_party/blink/renderer/bindings/core/v8/html_element_or_long.h"
@@ -39,45 +40,38 @@
#include "third_party/blink/renderer/core/dom/attribute.h"
#include "third_party/blink/renderer/core/dom/element_traversal.h"
#include "third_party/blink/renderer/core/dom/events/scoped_event_queue.h"
-#include "third_party/blink/renderer/core/dom/mutation_observer.h"
-#include "third_party/blink/renderer/core/dom/mutation_observer_init.h"
-#include "third_party/blink/renderer/core/dom/mutation_record.h"
#include "third_party/blink/renderer/core/dom/node_computed_style.h"
#include "third_party/blink/renderer/core/dom/node_lists_node_data.h"
#include "third_party/blink/renderer/core/dom/node_traversal.h"
-#include "third_party/blink/renderer/core/events/gesture_event.h"
#include "third_party/blink/renderer/core/events/keyboard_event.h"
-#include "third_party/blink/renderer/core/events/mouse_event.h"
-#include "third_party/blink/renderer/core/frame/local_dom_window.h"
-#include "third_party/blink/renderer/core/frame/local_frame.h"
-#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/web_feature.h"
#include "third_party/blink/renderer/core/html/forms/form_controller.h"
#include "third_party/blink/renderer/core/html/forms/form_data.h"
#include "third_party/blink/renderer/core/html/forms/html_form_element.h"
#include "third_party/blink/renderer/core/html/forms/html_opt_group_element.h"
#include "third_party/blink/renderer/core/html/forms/html_option_element.h"
-#include "third_party/blink/renderer/core/html/forms/popup_menu.h"
+#include "third_party/blink/renderer/core/html/forms/menu_list_inner_element.h"
+#include "third_party/blink/renderer/core/html/forms/select_type.h"
#include "third_party/blink/renderer/core/html/html_hr_element.h"
#include "third_party/blink/renderer/core/html/html_slot_element.h"
#include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
#include "third_party/blink/renderer/core/html_names.h"
-#include "third_party/blink/renderer/core/input/event_handler.h"
-#include "third_party/blink/renderer/core/input/input_device_capabilities.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
#include "third_party/blink/renderer/core/layout/hit_test_request.h"
#include "third_party/blink/renderer/core/layout/hit_test_result.h"
-#include "third_party/blink/renderer/core/layout/layout_list_box.h"
-#include "third_party/blink/renderer/core/layout/layout_menu_list.h"
+#include "third_party/blink/renderer/core/layout/layout_block_flow.h"
+#include "third_party/blink/renderer/core/layout/layout_object_factory.h"
#include "third_party/blink/renderer/core/layout/layout_theme.h"
-#include "third_party/blink/renderer/core/page/autoscroll_controller.h"
#include "third_party/blink/renderer/core/page/chrome_client.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/page/spatial_navigation.h"
+#include "third_party/blink/renderer/core/paint/paint_layer.h"
+#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
#include "third_party/blink/renderer/platform/text/platform_locale.h"
+#include "ui/base/ui_base_features.h"
namespace blink {
@@ -86,18 +80,21 @@ namespace blink {
// signed.
static const unsigned kMaxListItems = INT_MAX;
+// Default size when the multiple attribute is present but size attribute is
+// absent.
+const int kDefaultListBoxSize = 4;
+
HTMLSelectElement::HTMLSelectElement(Document& document)
: HTMLFormControlElementWithState(html_names::kSelectTag, document),
type_ahead_(this),
size_(0),
last_on_change_option_(nullptr),
is_multiple_(false),
- is_in_non_contiguous_selection_(false),
- active_selection_state_(false),
should_recalc_list_items_(false),
is_autofilled_by_preview_(false),
- index_to_select_on_cancel_(-1),
- popup_is_visible_(false) {
+ index_to_select_on_cancel_(-1) {
+ // Make sure SelectType is created after initializing |uses_menu_list_|.
+ select_type_ = SelectType::Create(*this);
SetHasCustomStyleCallbacks();
EnsureUserAgentShadowRoot();
}
@@ -106,7 +103,7 @@ HTMLSelectElement::~HTMLSelectElement() = default;
// static
bool HTMLSelectElement::CanAssignToSelectSlot(const Node& node) {
- // Even if options/optgroups are not rendered as children of LayoutMenuList,
+ // Even if options/optgroups are not rendered as children of menulist SELECT,
// we still need to add them to the flat tree through slotting since we need
// their ComputedStyle for popup rendering.
return node.HasTagName(html_names::kOptionTag) ||
@@ -163,9 +160,6 @@ String HTMLSelectElement::validationMessage() const {
}
bool HTMLSelectElement::ValueMissing() const {
- if (!willValidate())
- return false;
-
if (!IsRequired())
return false;
@@ -187,21 +181,48 @@ void HTMLSelectElement::SelectMultipleOptionsByPopup(
const Vector<int>& list_indices) {
DCHECK(UsesMenuList());
DCHECK(IsMultiple());
- for (wtf_size_t i = 0; i < list_indices.size(); ++i) {
- bool add_selection_if_not_first = i > 0;
- if (HTMLOptionElement* option = OptionAtListIndex(list_indices[i]))
- UpdateSelectedState(option, add_selection_if_not_first, false);
+
+ HeapHashSet<Member<HTMLOptionElement>> old_selection;
+ for (auto* option : GetOptionList()) {
+ if (option->Selected()) {
+ old_selection.insert(option);
+ option->SetSelectedState(false);
+ }
}
+
+ bool has_new_selection = false;
+ for (int list_index : list_indices) {
+ if (auto* option = OptionAtListIndex(list_index)) {
+ option->SetSelectedState(true);
+ option->SetDirty(true);
+ auto iter = old_selection.find(option);
+ if (iter != old_selection.end())
+ old_selection.erase(iter);
+ else
+ has_new_selection = true;
+ }
+ }
+
SetNeedsValidityCheck();
- // TODO(tkent): Using listBoxOnChange() is very confusing.
- ListBoxOnChange();
+ if (has_new_selection || !old_selection.IsEmpty()) {
+ DispatchInputEvent();
+ DispatchChangeEvent();
+ }
}
-bool HTMLSelectElement::UsesMenuList() const {
- if (LayoutTheme::GetTheme().DelegatesMenuListRendering())
- return true;
+unsigned HTMLSelectElement::ListBoxSize() const {
+ DCHECK(!UsesMenuList());
+ const unsigned specified_size = size();
+ if (specified_size >= 1)
+ return specified_size;
+ return kDefaultListBoxSize;
+}
- return !is_multiple_ && size_ <= 1;
+void HTMLSelectElement::UpdateUsesMenuList() {
+ if (LayoutTheme::GetTheme().DelegatesMenuListRendering())
+ uses_menu_list_ = true;
+ else
+ uses_menu_list_ = !is_multiple_ && size_ <= 1;
}
int HTMLSelectElement::ActiveSelectionEndListIndex() const {
@@ -269,8 +290,8 @@ void HTMLSelectElement::setValue(const String& value, bool send_events) {
flags |= kDispatchInputAndChangeEventFlag;
SelectOption(option, flags);
- if (send_events && previous_selected_option != option && !UsesMenuList())
- ListBoxOnChange();
+ if (send_events && previous_selected_option != option)
+ select_type_->ListBoxOnChange();
}
String HTMLSelectElement::SuggestedValue() const {
@@ -314,9 +335,10 @@ void HTMLSelectElement::ParseAttribute(
SetNeedsValidityCheck();
if (size_ != old_size) {
ChangeRendering();
+ UpdateUserAgentShadowTree(*UserAgentShadowRoot());
ResetToDefaultSelection();
- if (!UsesMenuList())
- SaveListboxActiveSelection();
+ select_type_->UpdateTextStyleAndContent();
+ select_type_->SaveListboxActiveSelection();
}
} else if (params.name == html_names::kMultipleAttr) {
ParseMultipleAttribute(params.new_value);
@@ -332,15 +354,29 @@ bool HTMLSelectElement::MayTriggerVirtualKeyboard() const {
return true;
}
+bool HTMLSelectElement::ShouldHaveFocusAppearance() const {
+ // For FormControlsRefresh don't draw focus ring for a select that has its
+ // popup open.
+ if (::features::IsFormControlsRefreshEnabled() && PopupIsVisible())
+ return false;
+
+ return HTMLFormControlElementWithState::ShouldHaveFocusAppearance();
+}
+
bool HTMLSelectElement::CanSelectAll() const {
return !UsesMenuList();
}
-LayoutObject* HTMLSelectElement::CreateLayoutObject(const ComputedStyle&,
- LegacyLayout) {
+bool HTMLSelectElement::TypeShouldForceLegacyLayout() const {
+ return UsesMenuList();
+}
+
+LayoutObject* HTMLSelectElement::CreateLayoutObject(
+ const ComputedStyle& style,
+ LegacyLayout legacy_layout) {
if (UsesMenuList())
- return new LayoutMenuList(this);
- return new LayoutListBox(this);
+ return LayoutObjectFactory::CreateFlexibleBox(*this, style, legacy_layout);
+ return LayoutObjectFactory::CreateBlockFlow(*this, style, legacy_layout);
}
HTMLCollection* HTMLSelectElement::selectedOptions() {
@@ -355,9 +391,9 @@ void HTMLSelectElement::OptionElementChildrenChanged(
const HTMLOptionElement& option) {
SetNeedsValidityCheck();
+ if (option.Selected())
+ select_type_->UpdateTextStyleAndContent();
if (GetLayoutObject()) {
- if (option.Selected() && UsesMenuList())
- GetLayoutObject()->UpdateFromElement();
if (AXObjectCache* cache =
GetLayoutObject()->GetDocument().ExistingAXObjectCache())
cache->ChildrenChanged(this);
@@ -385,7 +421,7 @@ void HTMLSelectElement::SetOption(unsigned index,
// We should check |index >= maxListItems| first to avoid integer overflow.
if (index >= kMaxListItems ||
GetListItems().size() + diff + 1 > kMaxListItems) {
- GetDocument().AddConsoleMessage(ConsoleMessage::Create(
+ GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
mojom::ConsoleMessageSource::kJavaScript,
mojom::ConsoleMessageLevel::kWarning,
String::Format("Blocked to expand the option list and set an option at "
@@ -418,7 +454,7 @@ void HTMLSelectElement::setLength(unsigned new_len,
// We should check |newLen > maxListItems| first to avoid integer overflow.
if (new_len > kMaxListItems ||
GetListItems().size() + new_len - length() > kMaxListItems) {
- GetDocument().AddConsoleMessage(ConsoleMessage::Create(
+ GetDocument().AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
mojom::ConsoleMessageSource::kJavaScript,
mojom::ConsoleMessageLevel::kWarning,
String::Format("Blocked to expand the option list to %u items. The "
@@ -469,246 +505,19 @@ HTMLOptionElement* HTMLSelectElement::OptionAtListIndex(int list_index) const {
return DynamicTo<HTMLOptionElement>(items[list_index].Get());
}
-// Returns the 1st valid OPTION |skip| items from |listIndex| in direction
-// |direction| if there is one.
-// Otherwise, it returns the valid OPTION closest to that boundary which is past
-// |listIndex| if there is one.
-// Otherwise, it returns nullptr.
-// Valid means that it is enabled and visible.
-HTMLOptionElement* HTMLSelectElement::NextValidOption(int list_index,
- SkipDirection direction,
- int skip) const {
- DCHECK(direction == kSkipBackwards || direction == kSkipForwards);
- const ListItems& list_items = GetListItems();
- HTMLOptionElement* last_good_option = nullptr;
- int size = list_items.size();
- for (list_index += direction; list_index >= 0 && list_index < size;
- list_index += direction) {
- --skip;
- HTMLElement* element = list_items[list_index];
- auto* option_element = DynamicTo<HTMLOptionElement>(element);
- if (!option_element)
- continue;
- if (option_element->IsDisplayNone())
- continue;
- if (element->IsDisabledFormControl())
- continue;
- if (!UsesMenuList() && !element->GetLayoutObject())
- continue;
- last_good_option = option_element;
- if (skip <= 0)
- break;
- }
- return last_good_option;
-}
-
-HTMLOptionElement* HTMLSelectElement::NextSelectableOption(
- HTMLOptionElement* start_option) const {
- return NextValidOption(start_option ? start_option->ListIndex() : -1,
- kSkipForwards, 1);
-}
-
-HTMLOptionElement* HTMLSelectElement::PreviousSelectableOption(
- HTMLOptionElement* start_option) const {
- return NextValidOption(
- start_option ? start_option->ListIndex() : GetListItems().size(),
- kSkipBackwards, 1);
-}
-
-HTMLOptionElement* HTMLSelectElement::FirstSelectableOption() const {
- // TODO(tkent): This is not efficient. nextSlectableOption(nullptr) is
- // faster.
- return NextValidOption(GetListItems().size(), kSkipBackwards, INT_MAX);
-}
-
-HTMLOptionElement* HTMLSelectElement::LastSelectableOption() const {
- // TODO(tkent): This is not efficient. previousSlectableOption(nullptr) is
- // faster.
- return NextValidOption(-1, kSkipForwards, INT_MAX);
-}
-
-// Returns the index of the next valid item one page away from |startIndex| in
-// direction |direction|.
-HTMLOptionElement* HTMLSelectElement::NextSelectableOptionPageAway(
- HTMLOptionElement* start_option,
- SkipDirection direction) const {
- const ListItems& items = GetListItems();
- // Can't use size_ because LayoutObject forces a minimum size.
- int page_size = 0;
- if (GetLayoutObject()->IsListBox()) {
- // -1 so we still show context.
- page_size = ToLayoutListBox(GetLayoutObject())->size() - 1;
- }
-
- // One page away, but not outside valid bounds.
- // If there is a valid option item one page away, the index is chosen.
- // If there is no exact one page away valid option, returns startIndex or
- // the most far index.
- int start_index = start_option ? start_option->ListIndex() : -1;
- int edge_index = (direction == kSkipForwards) ? 0 : (items.size() - 1);
- int skip_amount =
- page_size +
- ((direction == kSkipForwards) ? start_index : (edge_index - start_index));
- return NextValidOption(edge_index, direction, skip_amount);
-}
-
void HTMLSelectElement::SelectAll() {
- DCHECK(!UsesMenuList());
- if (!GetLayoutObject() || !is_multiple_)
- return;
-
- // Save the selection so it can be compared to the new selectAll selection
- // when dispatching change events.
- SaveLastSelection();
-
- active_selection_state_ = true;
- SetActiveSelectionAnchor(NextSelectableOption(nullptr));
- SetActiveSelectionEnd(PreviousSelectableOption(nullptr));
-
- UpdateListBoxSelection(false, false);
- ListBoxOnChange();
- SetNeedsValidityCheck();
-}
-
-void HTMLSelectElement::SaveLastSelection() {
- if (UsesMenuList()) {
- last_on_change_option_ = SelectedOption();
- return;
- }
-
- last_on_change_selection_.clear();
- for (auto& element : GetListItems()) {
- auto* option_element = DynamicTo<HTMLOptionElement>(element.Get());
- last_on_change_selection_.push_back(option_element &&
- option_element->Selected());
- }
+ select_type_->SelectAll();
}
void HTMLSelectElement::SetActiveSelectionAnchor(HTMLOptionElement* option) {
active_selection_anchor_ = option;
- if (!UsesMenuList())
- SaveListboxActiveSelection();
-}
-
-void HTMLSelectElement::SaveListboxActiveSelection() {
- // Cache the selection state so we can restore the old selection as the new
- // selection pivots around this anchor index.
- // Example:
- // 1. Press the mouse button on the second OPTION
- // active_selection_anchor_ points the second OPTION.
- // 2. Drag the mouse pointer onto the fifth OPTION
- // active_selection_end_ points the fifth OPTION, OPTIONs at 1-4 indices
- // are selected.
- // 3. Drag the mouse pointer onto the fourth OPTION
- // active_selection_end_ points the fourth OPTION, OPTIONs at 1-3 indices
- // are selected.
- // UpdateListBoxSelection needs to clear selection of the fifth OPTION.
- cached_state_for_active_selection_.resize(0);
- for (auto* const option : GetOptionList()) {
- cached_state_for_active_selection_.push_back(option->Selected());
- }
+ select_type_->SaveListboxActiveSelection();
}
void HTMLSelectElement::SetActiveSelectionEnd(HTMLOptionElement* option) {
active_selection_end_ = option;
}
-void HTMLSelectElement::UpdateListBoxSelection(bool deselect_other_options,
- bool scroll) {
- DCHECK(GetLayoutObject());
- DCHECK(GetLayoutObject()->IsListBox() || is_multiple_);
-
- int active_selection_anchor_index =
- active_selection_anchor_ ? active_selection_anchor_->index() : -1;
- int active_selection_end_index =
- active_selection_end_ ? active_selection_end_->index() : -1;
- int start =
- std::min(active_selection_anchor_index, active_selection_end_index);
- int end = std::max(active_selection_anchor_index, active_selection_end_index);
-
- int i = 0;
- for (auto* const option : GetOptionList()) {
- if (option->IsDisabledFormControl() || !option->GetLayoutObject()) {
- ++i;
- continue;
- }
- if (i >= start && i <= end) {
- option->SetSelectedState(active_selection_state_);
- option->SetDirty(true);
- } else if (deselect_other_options ||
- i >= static_cast<int>(
- cached_state_for_active_selection_.size())) {
- option->SetSelectedState(false);
- option->SetDirty(true);
- } else {
- option->SetSelectedState(cached_state_for_active_selection_[i]);
- }
- ++i;
- }
-
- UpdateMultiSelectListBoxFocus();
- SetNeedsValidityCheck();
- if (scroll)
- ScrollToSelection();
- NotifyFormStateChanged();
-}
-
-void HTMLSelectElement::ListBoxOnChange() {
- DCHECK(!UsesMenuList() || is_multiple_);
-
- const ListItems& items = GetListItems();
-
- // If the cached selection list is empty, or the size has changed, then fire
- // dispatchFormControlChangeEvent, and return early.
- // FIXME: Why? This looks unreasonable.
- if (last_on_change_selection_.IsEmpty() ||
- last_on_change_selection_.size() != items.size()) {
- DispatchChangeEvent();
- return;
- }
-
- // Update last_on_change_selection_ and fire a 'change' event.
- bool fire_on_change = false;
- for (unsigned i = 0; i < items.size(); ++i) {
- HTMLElement* element = items[i];
- auto* option_element = DynamicTo<HTMLOptionElement>(element);
- bool selected = option_element && option_element->Selected();
- if (selected != last_on_change_selection_[i])
- fire_on_change = true;
- last_on_change_selection_[i] = selected;
- }
-
- if (fire_on_change) {
- DispatchInputEvent();
- DispatchChangeEvent();
- }
-}
-
-void HTMLSelectElement::UpdateMultiSelectListBoxFocus() {
- if (!is_multiple_)
- return;
-
- for (auto* const option : GetOptionList()) {
- if (option->IsDisabledFormControl() || !option->GetLayoutObject())
- continue;
- bool is_focused =
- (option == active_selection_end_) && is_in_non_contiguous_selection_;
- option->SetMultiSelectFocusedState(is_focused);
- }
- ScrollToSelection();
-}
-
-void HTMLSelectElement::DispatchInputAndChangeEventForMenuList() {
- DCHECK(UsesMenuList());
-
- HTMLOptionElement* selected_option = SelectedOption();
- if (last_on_change_option_.Get() != selected_option) {
- last_on_change_option_ = selected_option;
- DispatchInputEvent();
- DispatchChangeEvent();
- }
-}
-
void HTMLSelectElement::ScrollToSelection() {
if (!IsFinishedParsingChildren())
return;
@@ -719,16 +528,6 @@ void HTMLSelectElement::ScrollToSelection() {
cache->ListboxActiveIndexChanged(this);
}
-void HTMLSelectElement::SetOptionsChangedOnLayoutObject() {
- if (LayoutObject* layout_object = GetLayoutObject()) {
- if (!UsesMenuList())
- return;
- ToLayoutMenuList(layout_object)
- ->SetNeedsLayoutAndPrefWidthsRecalc(
- layout_invalidation_reason::kMenuOptionsChanged);
- }
-}
-
const HTMLSelectElement::ListItems& HTMLSelectElement::GetListItems() const {
if (should_recalc_list_items_) {
RecalcListItems();
@@ -755,7 +554,7 @@ void HTMLSelectElement::SetRecalcListItems() {
should_recalc_list_items_ = true;
- SetOptionsChangedOnLayoutObject();
+ select_type_->MaximumOptionWidthMightBeChanged();
if (!isConnected()) {
if (HTMLOptionsCollection* collection =
CachedCollection<HTMLOptionsCollection>(kSelectOptions))
@@ -903,12 +702,7 @@ void HTMLSelectElement::SetSuggestedOption(HTMLOptionElement* option) {
return;
suggested_option_ = option;
- if (LayoutObject* layout_object = GetLayoutObject()) {
- layout_object->UpdateFromElement();
- ScrollToOption(option);
- }
- if (PopupIsVisible())
- popup_->UpdateFromElement(PopupMenu::kBySelectionChange);
+ select_type_->DidSetSuggestedOption(option);
}
void HTMLSelectElement::ScrollToOption(HTMLOptionElement* option) {
@@ -936,11 +730,23 @@ void HTMLSelectElement::ScrollToOptionTask() {
// OptionRemoved() makes sure option_to_scroll_to_ doesn't have an option with
// another owner.
DCHECK_EQ(option->OwnerSelectElement(), this);
- GetDocument().UpdateStyleAndLayout();
- if (!GetLayoutObject() || !GetLayoutObject()->IsListBox())
+ GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kScroll);
+ if (!GetLayoutObject() || UsesMenuList())
return;
PhysicalRect bounds = option->BoundingBoxForScrollIntoView();
- ToLayoutListBox(GetLayoutObject())->ScrollToRect(bounds);
+
+ // The following code will not scroll parent boxes unlike ScrollRectToVisible.
+ auto* box = GetLayoutBox();
+ if (!box->HasOverflowClip())
+ return;
+ DCHECK(box->Layer());
+ DCHECK(box->Layer()->GetScrollableArea());
+ box->Layer()->GetScrollableArea()->ScrollIntoView(
+ bounds,
+ ScrollAlignment::CreateScrollIntoViewParams(
+ ScrollAlignment::ToEdgeIfNeeded(), ScrollAlignment::ToEdgeIfNeeded(),
+ mojom::blink::ScrollType::kProgrammatic, false,
+ mojom::blink::ScrollBehavior::kInstant));
}
void HTMLSelectElement::OptionSelectionStateChanged(HTMLOptionElement* option,
@@ -954,6 +760,41 @@ void HTMLSelectElement::OptionSelectionStateChanged(HTMLOptionElement* option,
ResetToDefaultSelection();
}
+void HTMLSelectElement::ChildrenChanged(const ChildrenChange& change) {
+ HTMLFormControlElementWithState::ChildrenChanged(change);
+ if (change.type == ChildrenChangeType::kElementInserted) {
+ if (auto* option = DynamicTo<HTMLOptionElement>(change.sibling_changed)) {
+ OptionInserted(*option, option->Selected());
+ } else if (auto* optgroup =
+ DynamicTo<HTMLOptGroupElement>(change.sibling_changed)) {
+ for (auto& option : Traversal<HTMLOptionElement>::ChildrenOf(*optgroup))
+ OptionInserted(option, option.Selected());
+ }
+ } else if (change.type == ChildrenChangeType::kElementRemoved) {
+ if (auto* option = DynamicTo<HTMLOptionElement>(change.sibling_changed)) {
+ OptionRemoved(*option);
+ } else if (auto* optgroup =
+ DynamicTo<HTMLOptGroupElement>(change.sibling_changed)) {
+ for (auto& option : Traversal<HTMLOptionElement>::ChildrenOf(*optgroup))
+ OptionRemoved(option);
+ }
+ } else if (change.type == ChildrenChangeType::kAllChildrenRemoved) {
+ DCHECK(change.removed_nodes);
+ for (Node* node : *change.removed_nodes) {
+ if (auto* option = DynamicTo<HTMLOptionElement>(node)) {
+ OptionRemoved(*option);
+ } else if (auto* optgroup = DynamicTo<HTMLOptGroupElement>(node)) {
+ for (auto& option : Traversal<HTMLOptionElement>::ChildrenOf(*optgroup))
+ OptionRemoved(option);
+ }
+ }
+ }
+}
+
+bool HTMLSelectElement::ChildrenChangedAllChildrenRemovedNeedsList() const {
+ return true;
+}
+
void HTMLSelectElement::OptionInserted(HTMLOptionElement& option,
bool option_is_selected) {
DCHECK_EQ(option.OwnerSelectElement(), this);
@@ -966,7 +807,7 @@ void HTMLSelectElement::OptionInserted(HTMLOptionElement& option,
ResetToDefaultSelection();
}
SetNeedsValidityCheck();
- last_on_change_selection_.clear();
+ select_type_->ClearLastOnChangeSelection();
if (!GetDocument().IsActive())
return;
@@ -997,7 +838,7 @@ void HTMLSelectElement::OptionRemoved(HTMLOptionElement& option) {
if (option.Selected())
SetAutofillState(WebAutofillState::kNotFilled);
SetNeedsValidityCheck();
- last_on_change_selection_.clear();
+ select_type_->ClearLastOnChangeSelection();
if (!GetDocument().IsActive())
return;
@@ -1013,12 +854,12 @@ void HTMLSelectElement::OptGroupInsertedOrRemoved(
HTMLOptGroupElement& optgroup) {
SetRecalcListItems();
SetNeedsValidityCheck();
- last_on_change_selection_.clear();
+ select_type_->ClearLastOnChangeSelection();
}
void HTMLSelectElement::HrInsertedOrRemoved(HTMLHRElement& hr) {
SetRecalcListItems();
- last_on_change_selection_.clear();
+ select_type_->ClearLastOnChangeSelection();
}
// TODO(tkent): This function is not efficient. It contains multiple O(N)
@@ -1057,40 +898,7 @@ void HTMLSelectElement::SelectOption(HTMLOptionElement* element,
SetActiveSelectionEnd(element);
}
- // Need to update last_on_change_option_ before
- // LayoutMenuList::UpdateFromElement.
- bool should_dispatch_events = false;
- if (UsesMenuList()) {
- should_dispatch_events = (flags & kDispatchInputAndChangeEventFlag) &&
- last_on_change_option_ != element;
- last_on_change_option_ = element;
- }
-
- // For the menu list case, this is what makes the selected element appear.
- if (LayoutObject* layout_object = GetLayoutObject())
- layout_object->UpdateFromElement();
- // PopupMenu::UpdateFromElement() posts an O(N) task.
- if (PopupIsVisible() && should_update_popup)
- popup_->UpdateFromElement(PopupMenu::kBySelectionChange);
-
- ScrollToSelection();
- SetNeedsValidityCheck();
-
- if (UsesMenuList()) {
- if (should_dispatch_events) {
- DispatchInputEvent();
- DispatchChangeEvent();
- }
- if (LayoutObject* layout_object = GetLayoutObject()) {
- // Need to check UsesMenuList() again because event handlers might
- // change the status.
- if (UsesMenuList()) {
- // DidSelectOption() is O(N) because of HTMLOptionElement::index().
- ToLayoutMenuList(layout_object)->DidSelectOption(element);
- }
- }
- }
-
+ select_type_->DidSelectOption(element, flags, should_update_popup);
NotifyFormStateChanged();
if (LocalFrame::HasTransientUserActivation(GetDocument().GetFrame()) &&
@@ -1104,29 +912,22 @@ void HTMLSelectElement::SelectOption(HTMLOptionElement* element,
void HTMLSelectElement::DispatchFocusEvent(
Element* old_focused_element,
- WebFocusType type,
+ mojom::blink::FocusType type,
InputDeviceCapabilities* source_capabilities) {
// Save the selection so it can be compared to the new selection when
// dispatching change events during blur event dispatch.
if (UsesMenuList())
- SaveLastSelection();
+ select_type_->SaveLastSelection();
HTMLFormControlElementWithState::DispatchFocusEvent(old_focused_element, type,
source_capabilities);
}
void HTMLSelectElement::DispatchBlurEvent(
Element* new_focused_element,
- WebFocusType type,
+ mojom::blink::FocusType type,
InputDeviceCapabilities* source_capabilities) {
type_ahead_.ResetSession();
- // We only need to fire change events here for menu lists, because we fire
- // change events for list boxes whenever the selection change is actually
- // made. This matches other browsers' behavior.
- if (UsesMenuList())
- DispatchInputAndChangeEventForMenuList();
- last_on_change_selection_.clear();
- if (PopupIsVisible())
- HidePopup();
+ select_type_->DidBlur();
HTMLFormControlElementWithState::DispatchBlurEvent(new_focused_element, type,
source_capabilities);
}
@@ -1240,6 +1041,7 @@ void HTMLSelectElement::RestoreFormControlState(const FormControlState& state) {
}
SetNeedsValidityCheck();
+ select_type_->UpdateTextStyleAndContent();
}
void HTMLSelectElement::ParseMultipleAttribute(const AtomicString& value) {
@@ -1248,6 +1050,7 @@ void HTMLSelectElement::ParseMultipleAttribute(const AtomicString& value) {
is_multiple_ = !value.IsNull();
SetNeedsValidityCheck();
ChangeRendering();
+ UpdateUserAgentShadowTree(*UserAgentShadowRoot());
// Restore selectedIndex after changing the multiple flag to preserve
// selection as single-line and multi-line has different defaults.
if (old_multiple != is_multiple_) {
@@ -1259,6 +1062,7 @@ void HTMLSelectElement::ParseMultipleAttribute(const AtomicString& value) {
else
ResetToDefaultSelection();
}
+ select_type_->UpdateTextStyleAndContent();
}
void HTMLSelectElement::AppendToFormData(FormData& form_data) {
@@ -1279,228 +1083,12 @@ void HTMLSelectElement::ResetImpl() {
option->SetDirty(false);
}
ResetToDefaultSelection();
+ select_type_->UpdateTextStyleAndContent();
SetNeedsValidityCheck();
}
-void HTMLSelectElement::HandlePopupOpenKeyboardEvent(Event& event) {
- focus();
- // Calling focus() may cause us to lose our layoutObject. Return true so
- // that our caller doesn't process the event further, but don't set
- // the event as handled.
- if (!GetLayoutObject() || !GetLayoutObject()->IsMenuList() ||
- IsDisabledFormControl())
- return;
- // Save the selection so it can be compared to the new selection when
- // dispatching change events during selectOption, which gets called from
- // selectOptionByPopup, which gets called after the user makes a selection
- // from the menu.
- SaveLastSelection();
- ShowPopup();
- event.SetDefaultHandled();
- return;
-}
-
-bool HTMLSelectElement::ShouldOpenPopupForKeyDownEvent(
- const KeyboardEvent& key_event) {
- const String& key = key_event.key();
- LayoutTheme& layout_theme = LayoutTheme::GetTheme();
-
- if (IsSpatialNavigationEnabled(GetDocument().GetFrame()))
- return false;
-
- return ((layout_theme.PopsMenuByArrowKeys() &&
- (key == "ArrowDown" || key == "ArrowUp")) ||
- (layout_theme.PopsMenuByAltDownUpOrF4Key() &&
- (key == "ArrowDown" || key == "ArrowUp") && key_event.altKey()) ||
- (layout_theme.PopsMenuByAltDownUpOrF4Key() &&
- (!key_event.altKey() && !key_event.ctrlKey() && key == "F4")));
-}
-
-bool HTMLSelectElement::ShouldOpenPopupForKeyPressEvent(
- const KeyboardEvent& event) {
- LayoutTheme& layout_theme = LayoutTheme::GetTheme();
- int key_code = event.keyCode();
-
- return ((layout_theme.PopsMenuBySpaceKey() && key_code == ' ' &&
- !type_ahead_.HasActiveSession(event)) ||
- (layout_theme.PopsMenuByReturnKey() && key_code == '\r'));
-}
-
-void HTMLSelectElement::MenuListDefaultEventHandler(Event& event) {
- // We need to make the layout tree up-to-date to have GetLayoutObject() give
- // the correct result below. An author event handler may have set display to
- // some element to none which will cause a layout tree detach.
- GetDocument().UpdateStyleAndLayoutTree();
-
- if (event.type() == event_type_names::kKeydown) {
- if (!GetLayoutObject() || !event.IsKeyboardEvent())
- return;
-
- auto& key_event = ToKeyboardEvent(event);
- if (ShouldOpenPopupForKeyDownEvent(key_event)) {
- HandlePopupOpenKeyboardEvent(event);
- return;
- }
-
- // When using spatial navigation, we want to be able to navigate away
- // from the select element when the user hits any of the arrow keys,
- // instead of changing the selection.
- if (IsSpatialNavigationEnabled(GetDocument().GetFrame())) {
- if (!active_selection_state_)
- return;
- }
-
- // The key handling below shouldn't be used for non spatial navigation
- // mode Mac
- if (LayoutTheme::GetTheme().PopsMenuByArrowKeys() &&
- !IsSpatialNavigationEnabled(GetDocument().GetFrame()))
- return;
-
- int ignore_modifiers = WebInputEvent::kShiftKey |
- WebInputEvent::kControlKey | WebInputEvent::kAltKey |
- WebInputEvent::kMetaKey;
- if (key_event.GetModifiers() & ignore_modifiers)
- return;
-
- const String& key = key_event.key();
- bool handled = true;
- const ListItems& list_items = GetListItems();
- HTMLOptionElement* option = SelectedOption();
- int list_index = option ? option->ListIndex() : -1;
-
- if (key == "ArrowDown" || key == "ArrowRight")
- option = NextValidOption(list_index, kSkipForwards, 1);
- else if (key == "ArrowUp" || key == "ArrowLeft")
- option = NextValidOption(list_index, kSkipBackwards, 1);
- else if (key == "PageDown")
- option = NextValidOption(list_index, kSkipForwards, 3);
- else if (key == "PageUp")
- option = NextValidOption(list_index, kSkipBackwards, 3);
- else if (key == "Home")
- option = NextValidOption(-1, kSkipForwards, 1);
- else if (key == "End")
- option = NextValidOption(list_items.size(), kSkipBackwards, 1);
- else
- handled = false;
-
- if (handled && option) {
- SelectOption(option, kDeselectOtherOptionsFlag | kMakeOptionDirtyFlag |
- kDispatchInputAndChangeEventFlag);
- }
-
- if (handled)
- event.SetDefaultHandled();
- }
-
- if (event.type() == event_type_names::kKeypress) {
- if (!GetLayoutObject() || !event.IsKeyboardEvent())
- return;
-
- int key_code = ToKeyboardEvent(event).keyCode();
- if (key_code == ' ' &&
- IsSpatialNavigationEnabled(GetDocument().GetFrame())) {
- // Use space to toggle arrow key handling for selection change or
- // spatial navigation.
- active_selection_state_ = !active_selection_state_;
- event.SetDefaultHandled();
- return;
- }
-
- auto& key_event = ToKeyboardEvent(event);
- if (ShouldOpenPopupForKeyPressEvent(key_event)) {
- HandlePopupOpenKeyboardEvent(event);
- return;
- }
-
- if (!LayoutTheme::GetTheme().PopsMenuByReturnKey() && key_code == '\r') {
- if (Form())
- Form()->SubmitImplicitly(event, false);
- DispatchInputAndChangeEventForMenuList();
- event.SetDefaultHandled();
- }
- }
-
- if (event.type() == event_type_names::kMousedown && event.IsMouseEvent() &&
- ToMouseEvent(event).button() ==
- static_cast<int16_t>(WebPointerProperties::Button::kLeft)) {
- InputDeviceCapabilities* source_capabilities =
- GetDocument()
- .domWindow()
- ->GetInputDeviceCapabilities()
- ->FiresTouchEvents(ToMouseEvent(event).FromTouch());
- focus(FocusParams(SelectionBehaviorOnFocus::kRestore, kWebFocusTypeNone,
- source_capabilities));
- if (GetLayoutObject() && GetLayoutObject()->IsMenuList() &&
- !IsDisabledFormControl()) {
- if (PopupIsVisible()) {
- HidePopup();
- } else {
- // Save the selection so it can be compared to the new selection
- // when we call onChange during selectOption, which gets called
- // from selectOptionByPopup, which gets called after the user
- // makes a selection from the menu.
- SaveLastSelection();
- // TODO(lanwei): Will check if we need to add
- // InputDeviceCapabilities here when select menu list gets
- // focus, see https://crbug.com/476530.
- ShowPopup();
- }
- }
- event.SetDefaultHandled();
- }
-}
-
-void HTMLSelectElement::UpdateSelectedState(HTMLOptionElement* clicked_option,
- bool multi,
- bool shift) {
- DCHECK(clicked_option);
- // Save the selection so it can be compared to the new selection when
- // dispatching change events during mouseup, or after autoscroll finishes.
- SaveLastSelection();
-
- active_selection_state_ = true;
-
- bool shift_select = is_multiple_ && shift;
- bool multi_select = is_multiple_ && multi && !shift;
-
- // Keep track of whether an active selection (like during drag selection),
- // should select or deselect.
- if (clicked_option->Selected() && multi_select) {
- active_selection_state_ = false;
- clicked_option->SetSelectedState(false);
- clicked_option->SetDirty(true);
- }
-
- // If we're not in any special multiple selection mode, then deselect all
- // other items, excluding the clicked option. If no option was clicked, then
- // this will deselect all items in the list.
- if (!shift_select && !multi_select)
- DeselectItemsWithoutValidation(clicked_option);
-
- // If the anchor hasn't been set, and we're doing a single selection or a
- // shift selection, then initialize the anchor to the first selected index.
- if (!active_selection_anchor_ && !multi_select)
- SetActiveSelectionAnchor(SelectedOption());
-
- // Set the selection state of the clicked option.
- if (!clicked_option->IsDisabledFormControl()) {
- clicked_option->SetSelectedState(true);
- clicked_option->SetDirty(true);
- }
-
- // If there was no selectedIndex() for the previous initialization, or If
- // we're doing a single selection, or a multiple selection (using cmd or
- // ctrl), then initialize the anchor index to the listIndex that just got
- // clicked.
- if (!active_selection_anchor_ || !shift_select)
- SetActiveSelectionAnchor(clicked_option);
-
- SetActiveSelectionEnd(clicked_option);
- UpdateListBoxSelection(!multi_select);
-}
-
-HTMLOptionElement* HTMLSelectElement::EventTargetOption(const Event& event) {
- return DynamicTo<HTMLOptionElement>(event.target()->ToNode());
+bool HTMLSelectElement::PopupIsVisible() const {
+ return select_type_->PopupIsVisible();
}
int HTMLSelectElement::ListIndexForOption(const HTMLOptionElement& option) {
@@ -1519,259 +1107,13 @@ AutoscrollController* HTMLSelectElement::GetAutoscrollController() const {
return nullptr;
}
-void HTMLSelectElement::HandleMouseRelease() {
- // We didn't start this click/drag on any options.
- if (last_on_change_selection_.IsEmpty())
- return;
- ListBoxOnChange();
-}
-
-void HTMLSelectElement::ListBoxDefaultEventHandler(Event& event) {
- if (event.type() == event_type_names::kGesturetap && event.IsGestureEvent()) {
- focus();
- // Calling focus() may cause us to lose our layoutObject or change the
- // layoutObject type, in which case do not want to handle the event.
- if (!GetLayoutObject() || !GetLayoutObject()->IsListBox())
- return;
-
- // Convert to coords relative to the list box if needed.
- auto& gesture_event = ToGestureEvent(event);
- if (HTMLOptionElement* option = EventTargetOption(gesture_event)) {
- if (!IsDisabledFormControl()) {
- UpdateSelectedState(option, true, gesture_event.shiftKey());
- ListBoxOnChange();
- }
- event.SetDefaultHandled();
- }
-
- } else if (event.type() == event_type_names::kMousedown &&
- event.IsMouseEvent() &&
- ToMouseEvent(event).button() ==
- static_cast<int16_t>(WebPointerProperties::Button::kLeft)) {
- focus();
- // Calling focus() may cause us to lose our layoutObject, in which case
- // do not want to handle the event.
- if (!GetLayoutObject() || !GetLayoutObject()->IsListBox() ||
- IsDisabledFormControl())
- return;
-
- // Convert to coords relative to the list box if needed.
- auto& mouse_event = ToMouseEvent(event);
- if (HTMLOptionElement* option = EventTargetOption(mouse_event)) {
- if (!option->IsDisabledFormControl()) {
-#if defined(OS_MACOSX)
- UpdateSelectedState(option, mouse_event.metaKey(),
- mouse_event.shiftKey());
-#else
- UpdateSelectedState(option, mouse_event.ctrlKey(),
- mouse_event.shiftKey());
-#endif
- }
- if (LocalFrame* frame = GetDocument().GetFrame())
- frame->GetEventHandler().SetMouseDownMayStartAutoscroll();
-
- event.SetDefaultHandled();
- }
-
- } else if (event.type() == event_type_names::kMousemove &&
- event.IsMouseEvent()) {
- auto& mouse_event = ToMouseEvent(event);
- if (mouse_event.button() !=
- static_cast<int16_t>(WebPointerProperties::Button::kLeft) ||
- !mouse_event.ButtonDown())
- return;
-
- LayoutObject* layout_object = GetLayoutObject();
- if (layout_object) {
- layout_object->GetFrameView()->UpdateAllLifecyclePhasesExceptPaint();
-
- if (Page* page = GetDocument().GetPage()) {
- page->GetAutoscrollController().StartAutoscrollForSelection(
- layout_object);
- }
- }
- // Mousedown didn't happen in this element.
- if (last_on_change_selection_.IsEmpty())
- return;
-
- if (HTMLOptionElement* option = EventTargetOption(mouse_event)) {
- if (!IsDisabledFormControl()) {
- if (is_multiple_) {
- // Only extend selection if there is something selected.
- if (!active_selection_anchor_)
- return;
-
- SetActiveSelectionEnd(option);
- UpdateListBoxSelection(false);
- } else {
- SetActiveSelectionAnchor(option);
- SetActiveSelectionEnd(option);
- UpdateListBoxSelection(true);
- }
- }
- }
-
- } else if (event.type() == event_type_names::kMouseup &&
- event.IsMouseEvent() &&
- ToMouseEvent(event).button() ==
- static_cast<int16_t>(WebPointerProperties::Button::kLeft) &&
- GetLayoutObject()) {
- if (GetDocument().GetPage() &&
- GetDocument()
- .GetPage()
- ->GetAutoscrollController()
- .AutoscrollInProgressFor(ToLayoutBox(GetLayoutObject())))
- GetDocument().GetPage()->GetAutoscrollController().StopAutoscroll();
- else
- HandleMouseRelease();
-
- } else if (event.type() == event_type_names::kKeydown) {
- if (!event.IsKeyboardEvent())
- return;
- const String& key = ToKeyboardEvent(event).key();
-
- bool handled = false;
- HTMLOptionElement* end_option = nullptr;
- if (!active_selection_end_) {
- // Initialize the end index
- if (key == "ArrowDown" || key == "PageDown") {
- HTMLOptionElement* start_option = LastSelectedOption();
- handled = true;
- if (key == "ArrowDown") {
- end_option = NextSelectableOption(start_option);
- } else {
- end_option =
- NextSelectableOptionPageAway(start_option, kSkipForwards);
- }
- } else if (key == "ArrowUp" || key == "PageUp") {
- HTMLOptionElement* start_option = SelectedOption();
- handled = true;
- if (key == "ArrowUp") {
- end_option = PreviousSelectableOption(start_option);
- } else {
- end_option =
- NextSelectableOptionPageAway(start_option, kSkipBackwards);
- }
- }
- } else {
- // Set the end index based on the current end index.
- if (key == "ArrowDown") {
- end_option = NextSelectableOption(active_selection_end_.Get());
- handled = true;
- } else if (key == "ArrowUp") {
- end_option = PreviousSelectableOption(active_selection_end_.Get());
- handled = true;
- } else if (key == "PageDown") {
- end_option = NextSelectableOptionPageAway(active_selection_end_.Get(),
- kSkipForwards);
- handled = true;
- } else if (key == "PageUp") {
- end_option = NextSelectableOptionPageAway(active_selection_end_.Get(),
- kSkipBackwards);
- handled = true;
- }
- }
- if (key == "Home") {
- end_option = FirstSelectableOption();
- handled = true;
- } else if (key == "End") {
- end_option = LastSelectableOption();
- handled = true;
- }
-
- if (IsSpatialNavigationEnabled(GetDocument().GetFrame())) {
- // Check if the selection moves to the boundary.
- if (key == "ArrowLeft" || key == "ArrowRight" ||
- ((key == "ArrowDown" || key == "ArrowUp") &&
- end_option == active_selection_end_))
- return;
- }
-
- bool is_control_key = false;
-#if defined(OS_MACOSX)
- is_control_key = ToKeyboardEvent(event).metaKey();
-#else
- is_control_key = ToKeyboardEvent(event).ctrlKey();
-#endif
-
- if (is_multiple_ && ToKeyboardEvent(event).keyCode() == ' ' &&
- is_control_key && active_selection_end_) {
- // Use ctrl+space to toggle selection change.
- ToggleSelection(*active_selection_end_);
- event.SetDefaultHandled();
- return;
- }
-
- if (end_option && handled) {
- // Save the selection so it can be compared to the new selection
- // when dispatching change events immediately after making the new
- // selection.
- SaveLastSelection();
-
- SetActiveSelectionEnd(end_option);
-
- is_in_non_contiguous_selection_ = is_multiple_ && is_control_key;
- bool select_new_item =
- !is_multiple_ || ToKeyboardEvent(event).shiftKey() ||
- (!IsSpatialNavigationEnabled(GetDocument().GetFrame()) &&
- !is_in_non_contiguous_selection_);
- if (select_new_item)
- active_selection_state_ = true;
- // If the anchor is uninitialized, or if we're going to deselect all
- // other options, then set the anchor index equal to the end index.
- bool deselect_others =
- !is_multiple_ ||
- (!ToKeyboardEvent(event).shiftKey() && select_new_item);
- if (!active_selection_anchor_ || deselect_others) {
- if (deselect_others)
- DeselectItemsWithoutValidation();
- SetActiveSelectionAnchor(active_selection_end_.Get());
- }
-
- ScrollToOption(end_option);
- if (select_new_item || is_in_non_contiguous_selection_) {
- if (select_new_item) {
- UpdateListBoxSelection(deselect_others);
- ListBoxOnChange();
- }
- UpdateMultiSelectListBoxFocus();
- } else {
- ScrollToSelection();
- }
-
- event.SetDefaultHandled();
- }
-
- } else if (event.type() == event_type_names::kKeypress) {
- if (!event.IsKeyboardEvent())
- return;
- int key_code = ToKeyboardEvent(event).keyCode();
-
- if (key_code == '\r') {
- if (Form())
- Form()->SubmitImplicitly(event, false);
- event.SetDefaultHandled();
- } else if (is_multiple_ && key_code == ' ' &&
- (IsSpatialNavigationEnabled(GetDocument().GetFrame()) ||
- is_in_non_contiguous_selection_)) {
- HTMLOptionElement* option = active_selection_end_;
- // If there's no active selection,
- // act as if "ArrowDown" had been pressed.
- if (!option)
- option = NextSelectableOption(LastSelectedOption());
- if (option) {
- // Use space to toggle selection change.
- ToggleSelection(*option);
- event.SetDefaultHandled();
- }
- }
- }
+LayoutBox* HTMLSelectElement::AutoscrollBox() {
+ return !UsesMenuList() ? GetLayoutBox() : nullptr;
}
-void HTMLSelectElement::ToggleSelection(HTMLOptionElement& option) {
- active_selection_state_ = !active_selection_state_;
- UpdateSelectedState(&option, true /*multi*/, false /*shift*/);
- ListBoxOnChange();
+void HTMLSelectElement::StopAutoscroll() {
+ if (!IsDisabledFormControl())
+ select_type_->HandleMouseRelease();
}
void HTMLSelectElement::DefaultEventHandler(Event& event) {
@@ -1788,19 +1130,17 @@ void HTMLSelectElement::DefaultEventHandler(Event& event) {
return;
}
- if (UsesMenuList())
- MenuListDefaultEventHandler(event);
- else
- ListBoxDefaultEventHandler(event);
- if (event.DefaultHandled())
+ if (select_type_->DefaultEventHandler(event)) {
+ event.SetDefaultHandled();
return;
+ }
- if (event.type() == event_type_names::kKeypress && event.IsKeyboardEvent()) {
- auto& keyboard_event = ToKeyboardEvent(event);
- if (!keyboard_event.ctrlKey() && !keyboard_event.altKey() &&
- !keyboard_event.metaKey() &&
- WTF::unicode::IsPrintableChar(keyboard_event.charCode())) {
- TypeAheadFind(keyboard_event);
+ auto* keyboard_event = DynamicTo<KeyboardEvent>(event);
+ if (event.type() == event_type_names::kKeypress && keyboard_event) {
+ if (!keyboard_event->ctrlKey() && !keyboard_event->altKey() &&
+ !keyboard_event->metaKey() &&
+ WTF::unicode::IsPrintableChar(keyboard_event->charCode())) {
+ TypeAheadFind(*keyboard_event);
event.SetDefaultHandled();
return;
}
@@ -1843,8 +1183,7 @@ void HTMLSelectElement::TypeAheadFind(const KeyboardEvent& event) {
SelectOption(OptionAtListIndex(index), kDeselectOtherOptionsFlag |
kMakeOptionDirtyFlag |
kDispatchInputAndChangeEventFlag);
- if (!UsesMenuList())
- ListBoxOnChange();
+ select_type_->ListBoxOnChange();
}
void HTMLSelectElement::SelectOptionByAccessKey(HTMLOptionElement* option) {
@@ -1870,7 +1209,7 @@ void HTMLSelectElement::SelectOptionByAccessKey(HTMLOptionElement* option) {
option->SetDirty(true);
if (UsesMenuList())
return;
- ListBoxOnChange();
+ select_type_->ListBoxOnChange();
ScrollToSelection();
}
@@ -1892,16 +1231,16 @@ void HTMLSelectElement::FinishParsingChildren() {
cache->ListboxActiveIndexChanged(this);
}
-bool HTMLSelectElement::AnonymousIndexedSetter(
+IndexedPropertySetterResult HTMLSelectElement::AnonymousIndexedSetter(
unsigned index,
HTMLOptionElement* value,
ExceptionState& exception_state) {
if (!value) { // undefined or null
remove(index);
- return true;
+ return IndexedPropertySetterResult::kIntercepted;
}
SetOption(index, value, exception_state);
- return true;
+ return IndexedPropertySetterResult::kIntercepted;
}
bool HTMLSelectElement::IsInteractiveContent() const {
@@ -1915,14 +1254,47 @@ void HTMLSelectElement::Trace(Visitor* visitor) {
visitor->Trace(active_selection_end_);
visitor->Trace(option_to_scroll_to_);
visitor->Trace(suggested_option_);
- visitor->Trace(popup_);
- visitor->Trace(popup_updater_);
+ visitor->Trace(select_type_);
HTMLFormControlElementWithState::Trace(visitor);
}
void HTMLSelectElement::DidAddUserAgentShadowRoot(ShadowRoot& root) {
+ // Even if UsesMenuList(), the <slot> is necessary to have ComputedStyles
+ // for <option>s. LayoutFlexibleBox::IsChildAllowed() rejects all of
+ // LayoutObject children except for MenuListInnerElement's.
root.AppendChild(
HTMLSlotElement::CreateUserAgentCustomAssignSlot(GetDocument()));
+ UpdateUserAgentShadowTree(root);
+ select_type_->UpdateTextStyleAndContent();
+}
+
+void HTMLSelectElement::UpdateUserAgentShadowTree(ShadowRoot& root) {
+ // Remove all children of the ShadowRoot except for <slot>.
+ Node* node = root.firstChild();
+ while (node) {
+ if (IsA<HTMLSlotElement>(node)) {
+ node = node->nextSibling();
+ } else {
+ auto* will_be_removed = node;
+ node = node->nextSibling();
+ will_be_removed->remove();
+ }
+ }
+ if (UsesMenuList()) {
+ Element* inner_element =
+ MakeGarbageCollected<MenuListInnerElement>(GetDocument());
+ inner_element->setAttribute(html_names::kAriaHiddenAttr, "true");
+ // Make sure InnerElement() always has a Text node.
+ inner_element->appendChild(Text::Create(GetDocument(), g_empty_string));
+ root.insertBefore(inner_element, root.firstChild());
+ }
+}
+
+Element& HTMLSelectElement::InnerElement() const {
+ DCHECK(UsesMenuList());
+ auto* inner_element = DynamicTo<Element>(UserAgentShadowRoot()->firstChild());
+ DCHECK(inner_element);
+ return *inner_element;
}
HTMLOptionElement* HTMLSelectElement::SpatialNavigationFocusedOption() {
@@ -1930,7 +1302,7 @@ HTMLOptionElement* HTMLSelectElement::SpatialNavigationFocusedOption() {
return nullptr;
HTMLOptionElement* focused_option = ActiveSelectionEnd();
if (!focused_option)
- focused_option = FirstSelectableOption();
+ focused_option = select_type_->FirstSelectableOption();
return focused_option;
}
@@ -1960,42 +1332,44 @@ const ComputedStyle* HTMLSelectElement::ItemComputedStyle(
}
LayoutUnit HTMLSelectElement::ClientPaddingLeft() const {
- if (GetLayoutObject() && GetLayoutObject()->IsMenuList())
- return ToLayoutMenuList(GetLayoutObject())->ClientPaddingLeft();
- return LayoutUnit();
+ DCHECK(UsesMenuList());
+ auto* this_box = GetLayoutBox();
+ if (!this_box || !InnerElement().GetLayoutBox())
+ return LayoutUnit();
+ LayoutTheme& theme = LayoutTheme::GetTheme();
+ const ComputedStyle& style = this_box->StyleRef();
+ int inner_padding =
+ style.IsLeftToRightDirection()
+ ? theme.PopupInternalPaddingStart(style)
+ : theme.PopupInternalPaddingEnd(GetDocument().GetFrame(), style);
+ return this_box->PaddingLeft() + inner_padding;
}
LayoutUnit HTMLSelectElement::ClientPaddingRight() const {
- if (GetLayoutObject() && GetLayoutObject()->IsMenuList())
- return ToLayoutMenuList(GetLayoutObject())->ClientPaddingRight();
- return LayoutUnit();
+ DCHECK(UsesMenuList());
+ auto* this_box = GetLayoutBox();
+ if (!this_box || !InnerElement().GetLayoutBox())
+ return LayoutUnit();
+ LayoutTheme& theme = LayoutTheme::GetTheme();
+ const ComputedStyle& style = this_box->StyleRef();
+ int inner_padding =
+ style.IsLeftToRightDirection()
+ ? theme.PopupInternalPaddingEnd(GetDocument().GetFrame(), style)
+ : theme.PopupInternalPaddingStart(style);
+ return this_box->PaddingRight() + inner_padding;
}
void HTMLSelectElement::PopupDidHide() {
- popup_is_visible_ = false;
- UnobserveTreeMutation();
- if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache()) {
- if (GetLayoutObject() && GetLayoutObject()->IsMenuList())
- cache->DidHideMenuListPopup(ToLayoutMenuList(GetLayoutObject()));
- }
+ select_type_->PopupDidHide();
}
void HTMLSelectElement::SetIndexToSelectOnCancel(int list_index) {
index_to_select_on_cancel_ = list_index;
- if (GetLayoutObject())
- GetLayoutObject()->UpdateFromElement();
+ select_type_->UpdateTextStyleAndContent();
}
-HTMLOptionElement* HTMLSelectElement::OptionToBeShown() const {
- if (HTMLOptionElement* option = OptionAtListIndex(index_to_select_on_cancel_))
- return option;
- if (suggested_option_)
- return suggested_option_;
- // TODO(tkent): We should not call optionToBeShown() in isMultiple() case.
- if (IsMultiple())
- return SelectedOption();
- DCHECK_EQ(SelectedOption(), last_on_change_option_);
- return last_on_change_option_;
+HTMLOptionElement* HTMLSelectElement::OptionToBeShownForTesting() const {
+ return select_type_->OptionToBeShown();
}
void HTMLSelectElement::SelectOptionByPopup(int list_index) {
@@ -2031,44 +1405,28 @@ void HTMLSelectElement::ProvisionalSelectionChanged(unsigned list_index) {
}
void HTMLSelectElement::ShowPopup() {
- if (PopupIsVisible())
- return;
- if (GetDocument().GetPage()->GetChromeClient().HasOpenedPopup())
- return;
- if (!GetLayoutObject() || !GetLayoutObject()->IsMenuList())
- return;
- if (VisibleBoundsInVisualViewport().IsEmpty())
- return;
-
- if (!popup_) {
- popup_ = GetDocument().GetPage()->GetChromeClient().OpenPopupMenu(
- *GetDocument().GetFrame(), *this);
- }
- if (!popup_)
- return;
-
- popup_is_visible_ = true;
- ObserveTreeMutation();
-
- LayoutMenuList* menu_list = ToLayoutMenuList(GetLayoutObject());
- popup_->Show();
- if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache())
- cache->DidShowMenuListPopup(menu_list);
+ select_type_->ShowPopup();
}
void HTMLSelectElement::HidePopup() {
- if (popup_)
- popup_->Hide();
+ select_type_->HidePopup();
+}
+
+PopupMenu* HTMLSelectElement::PopupForTesting() const {
+ return select_type_->PopupForTesting();
}
void HTMLSelectElement::DidRecalcStyle(const StyleRecalcChange change) {
HTMLFormControlElementWithState::DidRecalcStyle(change);
- if (!change.ReattachLayoutTree() && PopupIsVisible())
- popup_->UpdateFromElement(PopupMenu::kByStyleChange);
+ select_type_->DidRecalcStyle(change);
}
void HTMLSelectElement::AttachLayoutTree(AttachContext& context) {
HTMLFormControlElementWithState::AttachLayoutTree(context);
+ // The call to UpdateTextStyle() needs to go after the call through
+ // to the base class's AttachLayoutTree() because that can sometimes do a
+ // close on the LayoutObject.
+ select_type_->UpdateTextStyle();
if (const ComputedStyle* style = GetComputedStyle()) {
if (style->Visibility() != EVisibility::kHidden) {
@@ -2082,90 +1440,13 @@ void HTMLSelectElement::AttachLayoutTree(AttachContext& context) {
void HTMLSelectElement::DetachLayoutTree(bool performing_reattach) {
HTMLFormControlElementWithState::DetachLayoutTree(performing_reattach);
- if (popup_)
- popup_->DisconnectClient();
- popup_is_visible_ = false;
- popup_ = nullptr;
- UnobserveTreeMutation();
+ select_type_->DidDetachLayoutTree();
}
void HTMLSelectElement::ResetTypeAheadSessionForTesting() {
type_ahead_.ResetSession();
}
-// PopupUpdater notifies updates of the specified SELECT element subtree to
-// a PopupMenu object.
-class HTMLSelectElement::PopupUpdater : public MutationObserver::Delegate {
- public:
- explicit PopupUpdater(HTMLSelectElement& select)
- : select_(select), observer_(MutationObserver::Create(this)) {
- MutationObserverInit* init = MutationObserverInit::Create();
- init->setAttributeOldValue(true);
- init->setAttributes(true);
- // Observe only attributes which affect popup content.
- init->setAttributeFilter({"disabled", "label", "selected", "value"});
- init->setCharacterData(true);
- init->setCharacterDataOldValue(true);
- init->setChildList(true);
- init->setSubtree(true);
- observer_->observe(select_, init, ASSERT_NO_EXCEPTION);
- }
-
- ExecutionContext* GetExecutionContext() const override {
- return &select_->GetDocument();
- }
-
- void Deliver(const MutationRecordVector& records,
- MutationObserver&) override {
- // We disconnect the MutationObserver when a popup is closed. However
- // MutationObserver can call back after disconnection.
- if (!select_->PopupIsVisible())
- return;
- for (const auto& record : records) {
- if (record->type() == "attributes") {
- const auto& element = *To<Element>(record->target());
- if (record->oldValue() == element.getAttribute(record->attributeName()))
- continue;
- } else if (record->type() == "characterData") {
- if (record->oldValue() == record->target()->nodeValue())
- continue;
- }
- select_->DidMutateSubtree();
- return;
- }
- }
-
- void Dispose() { observer_->disconnect(); }
-
- void Trace(Visitor* visitor) override {
- visitor->Trace(select_);
- visitor->Trace(observer_);
- MutationObserver::Delegate::Trace(visitor);
- }
-
- private:
- Member<HTMLSelectElement> select_;
- Member<MutationObserver> observer_;
-};
-
-void HTMLSelectElement::ObserveTreeMutation() {
- DCHECK(!popup_updater_);
- popup_updater_ = MakeGarbageCollected<PopupUpdater>(*this);
-}
-
-void HTMLSelectElement::UnobserveTreeMutation() {
- if (!popup_updater_)
- return;
- popup_updater_->Dispose();
- popup_updater_ = nullptr;
-}
-
-void HTMLSelectElement::DidMutateSubtree() {
- DCHECK(PopupIsVisible());
- DCHECK(popup_);
- popup_->UpdateFromElement(PopupMenu::kByDOMChange);
-}
-
void HTMLSelectElement::CloneNonAttributePropertiesFrom(
const Element& source,
CloneChildrenFlag flag) {
@@ -2175,6 +1456,10 @@ void HTMLSelectElement::CloneNonAttributePropertiesFrom(
}
void HTMLSelectElement::ChangeRendering() {
+ select_type_->DidDetachLayoutTree();
+ UpdateUsesMenuList();
+ select_type_->WillBeDestroyed();
+ select_type_ = SelectType::Create(*this);
if (!InActiveDocument())
return;
// TODO(futhark): SetForceReattachLayoutTree() should be the correct way to
@@ -2185,4 +1470,8 @@ void HTMLSelectElement::ChangeRendering() {
style_change_reason::kControl));
}
+const ComputedStyle* HTMLSelectElement::OptionStyle() const {
+ return select_type_->OptionStyle();
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_select_element.h b/chromium/third_party/blink/renderer/core/html/forms/html_select_element.h
index 5bfbc9ccde4..314840a3f45 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_select_element.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_select_element.h
@@ -28,6 +28,7 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_HTML_SELECT_ELEMENT_H_
#include "base/gtest_prod_util.h"
+#include "third_party/blink/public/mojom/input/focus_type.mojom-blink-forward.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.h"
#include "third_party/blink/renderer/core/html/forms/html_options_collection.h"
@@ -46,6 +47,7 @@ class HTMLOptionElementOrHTMLOptGroupElement;
class HTMLElementOrLong;
class LayoutUnit;
class PopupMenu;
+class SelectType;
class CORE_EXPORT HTMLSelectElement final
: public HTMLFormControlElementWithState,
@@ -74,9 +76,12 @@ class CORE_EXPORT HTMLSelectElement final
// TODO(tkent): Rename |size| to |Size|. This is not an implementation of
// |size| IDL attribute.
unsigned size() const { return size_; }
+ // The number of items to be shown in the LisBox mode.
+ // Do not call this in the MenuList mode.
+ unsigned ListBoxSize() const;
bool IsMultiple() const { return is_multiple_; }
- bool UsesMenuList() const;
+ bool UsesMenuList() const { return uses_menu_list_; }
void add(const HTMLOptionElementOrHTMLOptGroupElement&,
const HTMLElementOrLong&,
@@ -122,7 +127,6 @@ class CORE_EXPORT HTMLSelectElement final
bool CanSelectAll() const;
void SelectAll();
- void ListBoxOnChange();
int ActiveSelectionEndListIndex() const;
HTMLOptionElement* ActiveSelectionEnd() const;
void SetActiveSelectionAnchor(HTMLOptionElement*);
@@ -132,13 +136,14 @@ class CORE_EXPORT HTMLSelectElement final
void OptionSelectionStateChanged(HTMLOptionElement*, bool option_is_selected);
void OptionInserted(HTMLOptionElement&, bool option_is_selected);
void OptionRemoved(HTMLOptionElement&);
- bool AnonymousIndexedSetter(unsigned, HTMLOptionElement*, ExceptionState&);
+ IndexedPropertySetterResult AnonymousIndexedSetter(unsigned,
+ HTMLOptionElement*,
+ ExceptionState&);
void OptGroupInsertedOrRemoved(HTMLOptGroupElement&);
void HrInsertedOrRemoved(HTMLHRElement&);
HTMLOptionElement* SpatialNavigationFocusedOption();
- void HandleMouseRelease();
int ListIndexForOption(const HTMLOptionElement&);
@@ -159,12 +164,14 @@ class CORE_EXPORT HTMLSelectElement final
// Provisional selection is a selection made using arrow keys or type ahead.
void ProvisionalSelectionChanged(unsigned);
void PopupDidHide();
- bool PopupIsVisible() const { return popup_is_visible_; }
- HTMLOptionElement* OptionToBeShown() const;
+ bool PopupIsVisible() const;
+ HTMLOptionElement* OptionToBeShownForTesting() const;
+ // Style of the selected OPTION. This is nullable, and only for
+ // the menulist mode.
+ const ComputedStyle* OptionStyle() const;
void ShowPopup();
void HidePopup();
- PopupMenu* Popup() const { return popup_.Get(); }
- void DidMutateSubtree();
+ PopupMenu* PopupForTesting() const;
void ResetTypeAheadSessionForTesting();
@@ -177,17 +184,22 @@ class CORE_EXPORT HTMLSelectElement final
void CloneNonAttributePropertiesFrom(const Element&,
CloneChildrenFlag) override;
+ // This should be called only if UsesMenuList().
+ Element& InnerElement() const;
+
private:
const AtomicString& FormControlType() const override;
bool MayTriggerVirtualKeyboard() const override;
+ bool ShouldHaveFocusAppearance() const final;
+
void DispatchFocusEvent(
Element* old_focused_element,
- WebFocusType,
+ mojom::blink::FocusType,
InputDeviceCapabilities* source_capabilities) override;
void DispatchBlurEvent(Element* new_focused_element,
- WebFocusType,
+ mojom::blink::FocusType,
InputDeviceCapabilities* source_capabilities) override;
bool CanStartSelection() const override { return false; }
@@ -199,9 +211,12 @@ class CORE_EXPORT HTMLSelectElement final
FormControlState SaveFormControlState() const override;
void RestoreFormControlState(const FormControlState&) override;
+ void ChildrenChanged(const ChildrenChange& change) override;
+ bool ChildrenChangedAllChildrenRemovedNeedsList() const override;
void ParseAttribute(const AttributeModificationParams&) override;
bool IsPresentationAttribute(const QualifiedName&) const override;
+ bool TypeShouldForceLegacyLayout() const override;
LayoutObject* CreateLayoutObject(const ComputedStyle&, LegacyLayout) override;
void DidRecalcStyle(const StyleRecalcChange) override;
void AttachLayoutTree(AttachContext&) override;
@@ -211,15 +226,11 @@ class CORE_EXPORT HTMLSelectElement final
void DefaultEventHandler(Event&) override;
- void DispatchInputAndChangeEventForMenuList();
-
void SetRecalcListItems();
void RecalcListItems() const;
enum ResetReason { kResetReasonSelectedOptionRemoved, kResetReasonOthers };
void ResetToDefaultSelection(ResetReason = kResetReasonOthers);
void TypeAheadFind(const KeyboardEvent&);
- void SaveLastSelection();
- void SaveListboxActiveSelection();
// Returns the first selected OPTION, or nullptr.
HTMLOptionElement* SelectedOption() const;
@@ -241,37 +252,19 @@ class CORE_EXPORT HTMLSelectElement final
HTMLOptionElement* element_to_exclude = nullptr);
void ParseMultipleAttribute(const AtomicString&);
HTMLOptionElement* LastSelectedOption() const;
- void UpdateSelectedState(HTMLOptionElement*, bool multi, bool shift);
- void MenuListDefaultEventHandler(Event&);
- void HandlePopupOpenKeyboardEvent(Event&);
- bool ShouldOpenPopupForKeyDownEvent(const KeyboardEvent&);
- bool ShouldOpenPopupForKeyPressEvent(const KeyboardEvent&);
- void ListBoxDefaultEventHandler(Event&);
- void SetOptionsChangedOnLayoutObject();
wtf_size_t SearchOptionsForValue(const String&,
wtf_size_t list_index_start,
wtf_size_t list_index_end) const;
- void UpdateListBoxSelection(bool deselect_other_options, bool scroll = true);
- void UpdateMultiSelectListBoxFocus();
void SetIndexToSelectOnCancel(int list_index);
void SetSuggestedOption(HTMLOptionElement*);
- void ToggleSelection(HTMLOptionElement&);
// Returns nullptr if listIndex is out of bounds, or it doesn't point an
// HTMLOptionElement.
HTMLOptionElement* OptionAtListIndex(int list_index) const;
- enum SkipDirection { kSkipBackwards = -1, kSkipForwards = 1 };
- HTMLOptionElement* NextValidOption(int list_index,
- SkipDirection,
- int skip) const;
- HTMLOptionElement* NextSelectableOption(HTMLOptionElement*) const;
- HTMLOptionElement* PreviousSelectableOption(HTMLOptionElement*) const;
- HTMLOptionElement* FirstSelectableOption() const;
- HTMLOptionElement* LastSelectableOption() const;
- HTMLOptionElement* NextSelectableOptionPageAway(HTMLOptionElement*,
- SkipDirection) const;
- HTMLOptionElement* EventTargetOption(const Event&);
+
AutoscrollController* GetAutoscrollController() const;
+ LayoutBox* AutoscrollBox() override;
+ void StopAutoscroll() override;
void ScrollToOptionTask();
bool AreAuthorShadowsAllowed() const override { return false; }
@@ -282,18 +275,15 @@ class CORE_EXPORT HTMLSelectElement final
int OptionCount() const override;
String OptionAtIndex(int index) const override;
- void ObserveTreeMutation();
- void UnobserveTreeMutation();
-
+ void UpdateUsesMenuList();
// Apply changes to rendering as a result of attribute changes (multiple,
// size).
void ChangeRendering();
+ void UpdateUserAgentShadowTree(ShadowRoot& root);
// list_items_ contains HTMLOptionElement, HTMLOptGroupElement, and
// HTMLHRElement objects.
mutable ListItems list_items_;
- Vector<bool> last_on_change_selection_;
- Vector<bool> cached_state_for_active_selection_;
TypeAhead type_ahead_;
unsigned size_;
Member<HTMLOptionElement> last_on_change_option_;
@@ -301,22 +291,18 @@ class CORE_EXPORT HTMLSelectElement final
Member<HTMLOptionElement> active_selection_end_;
Member<HTMLOptionElement> option_to_scroll_to_;
Member<HTMLOptionElement> suggested_option_;
+ bool uses_menu_list_ = true;
bool is_multiple_;
- bool is_in_non_contiguous_selection_;
- bool active_selection_state_;
mutable bool should_recalc_list_items_;
bool is_autofilled_by_preview_;
- class PopupUpdater;
- Member<PopupUpdater> popup_updater_;
- Member<PopupMenu> popup_;
+ Member<SelectType> select_type_;
int index_to_select_on_cancel_;
- bool popup_is_visible_;
- FRIEND_TEST_ALL_PREFIXES(HTMLSelectElementTest, FirstSelectableOption);
- FRIEND_TEST_ALL_PREFIXES(HTMLSelectElementTest, LastSelectableOption);
- FRIEND_TEST_ALL_PREFIXES(HTMLSelectElementTest, NextSelectableOption);
- FRIEND_TEST_ALL_PREFIXES(HTMLSelectElementTest, PreviousSelectableOption);
+ friend class ListBoxSelectType;
+ friend class MenuListSelectType;
+ friend class SelectType;
+ friend class HTMLSelectElementTest;
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_select_element_test.cc b/chromium/third_party/blink/renderer/core/html/forms/html_select_element_test.cc
index 8da70996dff..f92a9abb963 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_select_element_test.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_select_element_test.cc
@@ -9,8 +9,11 @@
#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"
@@ -19,11 +22,49 @@ 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();
+ }
+
+ 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) {
@@ -55,6 +96,8 @@ TEST_F(HTMLSelectElementTest, SaveRestoreSelectSingleFormControlState) {
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) {
@@ -121,7 +164,8 @@ TEST_F(HTMLSelectElementTest, RestoreUnmatchedFormControlState) {
// Restore
select->RestoreFormControlState(select_state);
EXPECT_EQ(-1, To<HTMLSelectElement>(element)->selectedIndex());
- EXPECT_EQ(nullptr, To<HTMLSelectElement>(element)->OptionToBeShown());
+ EXPECT_EQ(nullptr,
+ To<HTMLSelectElement>(element)->OptionToBeShownForTesting());
}
TEST_F(HTMLSelectElementTest, VisibleBoundsInVisualViewport) {
@@ -149,13 +193,13 @@ TEST_F(HTMLSelectElementTest, FirstSelectableOption) {
{
SetHtmlInnerHTML("<select></select>");
auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild());
- EXPECT_EQ(nullptr, select->FirstSelectableOption());
+ 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", select->FirstSelectableOption()->FastGetAttribute(
+ EXPECT_EQ("o1", FirstSelectableOption(*select)->FastGetAttribute(
html_names::kIdAttr));
}
{
@@ -163,7 +207,7 @@ TEST_F(HTMLSelectElementTest, FirstSelectableOption) {
"<select><option id=o1 disabled></option><option "
"id=o2></option></select>");
auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild());
- EXPECT_EQ("o2", select->FirstSelectableOption()->FastGetAttribute(
+ EXPECT_EQ("o2", FirstSelectableOption(*select)->FastGetAttribute(
html_names::kIdAttr));
}
{
@@ -171,7 +215,7 @@ TEST_F(HTMLSelectElementTest, FirstSelectableOption) {
"<select><option id=o1 style='display:none'></option><option "
"id=o2></option></select>");
auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild());
- EXPECT_EQ("o2", select->FirstSelectableOption()->FastGetAttribute(
+ EXPECT_EQ("o2", FirstSelectableOption(*select)->FastGetAttribute(
html_names::kIdAttr));
}
{
@@ -179,7 +223,7 @@ TEST_F(HTMLSelectElementTest, FirstSelectableOption) {
"<select><optgroup><option id=o1></option><option "
"id=o2></option></optgroup></select>");
auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild());
- EXPECT_EQ("o1", select->FirstSelectableOption()->FastGetAttribute(
+ EXPECT_EQ("o1", FirstSelectableOption(*select)->FastGetAttribute(
html_names::kIdAttr));
}
}
@@ -188,13 +232,13 @@ TEST_F(HTMLSelectElementTest, LastSelectableOption) {
{
SetHtmlInnerHTML("<select></select>");
auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild());
- EXPECT_EQ(nullptr, select->LastSelectableOption());
+ 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", select->LastSelectableOption()->FastGetAttribute(
+ EXPECT_EQ("o2", LastSelectableOption(*select)->FastGetAttribute(
html_names::kIdAttr));
}
{
@@ -202,7 +246,7 @@ TEST_F(HTMLSelectElementTest, LastSelectableOption) {
"<select><option id=o1></option><option id=o2 "
"disabled></option></select>");
auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild());
- EXPECT_EQ("o1", select->LastSelectableOption()->FastGetAttribute(
+ EXPECT_EQ("o1", LastSelectableOption(*select)->FastGetAttribute(
html_names::kIdAttr));
}
{
@@ -210,7 +254,7 @@ TEST_F(HTMLSelectElementTest, LastSelectableOption) {
"<select><option id=o1></option><option id=o2 "
"style='display:none'></option></select>");
auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild());
- EXPECT_EQ("o1", select->LastSelectableOption()->FastGetAttribute(
+ EXPECT_EQ("o1", LastSelectableOption(*select)->FastGetAttribute(
html_names::kIdAttr));
}
{
@@ -218,7 +262,7 @@ TEST_F(HTMLSelectElementTest, LastSelectableOption) {
"<select><optgroup><option id=o1></option><option "
"id=o2></option></optgroup></select>");
auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild());
- EXPECT_EQ("o2", select->LastSelectableOption()->FastGetAttribute(
+ EXPECT_EQ("o2", LastSelectableOption(*select)->FastGetAttribute(
html_names::kIdAttr));
}
}
@@ -227,49 +271,50 @@ TEST_F(HTMLSelectElementTest, NextSelectableOption) {
{
SetHtmlInnerHTML("<select></select>");
auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild());
- EXPECT_EQ(nullptr, select->NextSelectableOption(nullptr));
+ 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", select->NextSelectableOption(nullptr)->FastGetAttribute(
- html_names::kIdAttr));
+ 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", select->NextSelectableOption(nullptr)->FastGetAttribute(
- html_names::kIdAttr));
+ 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", select->NextSelectableOption(nullptr)->FastGetAttribute(
- html_names::kIdAttr));
+ 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", select->NextSelectableOption(nullptr)->FastGetAttribute(
- html_names::kIdAttr));
+ 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", select->NextSelectableOption(option)->FastGetAttribute(
- html_names::kIdAttr));
+ EXPECT_EQ("o2", NextSelectableOption(*select, option)
+ ->FastGetAttribute(html_names::kIdAttr));
- EXPECT_EQ(nullptr, select->NextSelectableOption(
- To<HTMLOptionElement>(GetElementById("o2"))));
+ EXPECT_EQ(nullptr,
+ NextSelectableOption(
+ *select, To<HTMLOptionElement>(GetElementById("o2"))));
}
{
SetHtmlInnerHTML(
@@ -277,8 +322,8 @@ TEST_F(HTMLSelectElementTest, NextSelectableOption) {
"id=o2></option></optgroup></select>");
auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild());
auto* option = To<HTMLOptionElement>(GetElementById("o1"));
- EXPECT_EQ("o2", select->NextSelectableOption(option)->FastGetAttribute(
- html_names::kIdAttr));
+ EXPECT_EQ("o2", NextSelectableOption(*select, option)
+ ->FastGetAttribute(html_names::kIdAttr));
}
}
@@ -286,49 +331,50 @@ TEST_F(HTMLSelectElementTest, PreviousSelectableOption) {
{
SetHtmlInnerHTML("<select></select>");
auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild());
- EXPECT_EQ(nullptr, select->PreviousSelectableOption(nullptr));
+ 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", select->PreviousSelectableOption(nullptr)->FastGetAttribute(
- html_names::kIdAttr));
+ 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", select->PreviousSelectableOption(nullptr)->FastGetAttribute(
- html_names::kIdAttr));
+ 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", select->PreviousSelectableOption(nullptr)->FastGetAttribute(
- html_names::kIdAttr));
+ 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", select->PreviousSelectableOption(nullptr)->FastGetAttribute(
- html_names::kIdAttr));
+ 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", select->PreviousSelectableOption(option)->FastGetAttribute(
- html_names::kIdAttr));
+ EXPECT_EQ("o1", PreviousSelectableOption(*select, option)
+ ->FastGetAttribute(html_names::kIdAttr));
- EXPECT_EQ(nullptr, select->PreviousSelectableOption(
- To<HTMLOptionElement>(GetElementById("o1"))));
+ EXPECT_EQ(nullptr,
+ PreviousSelectableOption(
+ *select, To<HTMLOptionElement>(GetElementById("o1"))));
}
{
SetHtmlInnerHTML(
@@ -336,8 +382,8 @@ TEST_F(HTMLSelectElementTest, PreviousSelectableOption) {
"id=o2></option></optgroup></select>");
auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild());
auto* option = To<HTMLOptionElement>(GetElementById("o2"));
- EXPECT_EQ("o1", select->PreviousSelectableOption(option)->FastGetAttribute(
- html_names::kIdAttr));
+ EXPECT_EQ("o1", PreviousSelectableOption(*select, option)
+ ->FastGetAttribute(html_names::kIdAttr));
}
}
@@ -401,7 +447,7 @@ TEST_F(HTMLSelectElementTest, SetRecalcListItemsByOptgroupRemoval) {
"<select><optgroup><option>sub1</option><option>sub2</option></"
"optgroup></select>");
auto* select = To<HTMLSelectElement>(GetDocument().body()->firstChild());
- select->SetInnerHTMLFromString("");
+ select->setInnerHTML("");
// PASS if setInnerHTML didn't have a check failure.
}
@@ -415,4 +461,109 @@ TEST_F(HTMLSelectElementTest, ScrollToOptionAfterLayoutCrash) {
)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.";
+ }
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_text_area_element.cc b/chromium/third_party/blink/renderer/core/html/forms/html_text_area_element.cc
index 4924dfa0d84..d10fd35c9af 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_text_area_element.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_text_area_element.cc
@@ -37,6 +37,8 @@
#include "third_party/blink/renderer/core/editing/iterators/text_iterator.h"
#include "third_party/blink/renderer/core/event_interface_names.h"
#include "third_party/blink/renderer/core/events/before_text_inserted_event.h"
+#include "third_party/blink/renderer/core/events/drag_event.h"
+#include "third_party/blink/renderer/core/events/mouse_event.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/html/forms/form_controller.h"
#include "third_party/blink/renderer/core/html/forms/form_data.h"
@@ -69,6 +71,11 @@ static inline unsigned ComputeLengthForAPIValue(const String& text) {
return text.length() - crlf_count;
}
+static inline void ReplaceCRWithNewLine(String& text) {
+ text.Replace("\r\n", "\n");
+ text.Replace('\r', '\n');
+}
+
HTMLTextAreaElement::HTMLTextAreaElement(Document& document)
: TextControlElement(html_names::kTextareaTag, document),
rows_(kDefaultRows),
@@ -154,7 +161,7 @@ void HTMLTextAreaElement::ParseAttribute(
rows_ = rows;
if (GetLayoutObject()) {
GetLayoutObject()
- ->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(
+ ->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
layout_invalidation_reason::kAttributeChanged);
}
}
@@ -167,7 +174,7 @@ void HTMLTextAreaElement::ParseAttribute(
cols_ = cols;
if (LayoutObject* layout_object = GetLayoutObject()) {
layout_object
- ->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(
+ ->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
layout_invalidation_reason::kAttributeChanged);
}
}
@@ -176,11 +183,11 @@ void HTMLTextAreaElement::ParseAttribute(
// deprecated. The soft/hard /off values are a recommendation for HTML 4
// extension by IE and NS 4.
WrapMethod wrap;
- if (DeprecatedEqualIgnoringCase(value, "physical") ||
- DeprecatedEqualIgnoringCase(value, "hard") ||
- DeprecatedEqualIgnoringCase(value, "on"))
+ if (EqualIgnoringASCIICase(value, "physical") ||
+ EqualIgnoringASCIICase(value, "hard") ||
+ EqualIgnoringASCIICase(value, "on"))
wrap = kHardWrap;
- else if (DeprecatedEqualIgnoringCase(value, "off"))
+ else if (EqualIgnoringASCIICase(value, "off"))
wrap = kNoWrap;
else
wrap = kSoftWrap;
@@ -188,7 +195,7 @@ void HTMLTextAreaElement::ParseAttribute(
wrap_ = wrap;
if (LayoutObject* layout_object = GetLayoutObject()) {
layout_object
- ->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(
+ ->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
layout_invalidation_reason::kAttributeChanged);
}
}
@@ -214,7 +221,7 @@ void HTMLTextAreaElement::AppendToFormData(FormData& form_data) {
if (GetName().IsEmpty())
return;
- GetDocument().UpdateStyleAndLayout();
+ GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kForm);
const String& text =
(wrap_ == kHardWrap) ? ValueWithHardLineBreaks() : value();
@@ -264,7 +271,7 @@ void HTMLTextAreaElement::UpdateFocusAppearanceWithOptions(
void HTMLTextAreaElement::DefaultEventHandler(Event& event) {
if (GetLayoutObject() &&
- (event.IsMouseEvent() || event.IsDragEvent() ||
+ (IsA<MouseEvent>(event) || IsA<DragEvent>(event) ||
event.HasInterface(event_interface_names::kWheelEvent) ||
event.type() == event_type_names::kBlur)) {
ForwardEvent(event);
@@ -332,7 +339,7 @@ void HTMLTextAreaElement::HandleBeforeTextInsertedEvent(
if (IsFocused()) {
// TODO(editing-dev): Use of UpdateStyleAndLayout
// needs to be audited. See http://crbug.com/590369 for more details.
- GetDocument().UpdateStyleAndLayout();
+ GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kForm);
selection_length = ComputeLengthForAPIValue(
GetDocument().GetFrame()->Selection().SelectedText());
@@ -397,8 +404,7 @@ void HTMLTextAreaElement::SetValueCommon(
// Code elsewhere normalizes line endings added by the user via the keyboard
// or pasting. We normalize line endings coming from JavaScript here.
String normalized_value = new_value;
- normalized_value.Replace("\r\n", "\n");
- normalized_value.Replace('\r', '\n');
+ ReplaceCRWithNewLine(normalized_value);
// Clear the suggested value. Use the base class version to not trigger a view
// update.
@@ -445,6 +451,10 @@ void HTMLTextAreaElement::SetValueCommon(
DispatchFormControlChangeEvent();
break;
+ case TextFieldEventBehavior::kDispatchInputEvent:
+ DispatchInputEvent();
+ break;
+
case TextFieldEventBehavior::kDispatchInputAndChangeEvent:
DispatchInputEvent();
DispatchFormControlChangeEvent();
@@ -503,10 +513,12 @@ String HTMLTextAreaElement::validationMessage() const {
bool HTMLTextAreaElement::ValueMissing() const {
// We should not call value() for performance.
- return willValidate() && ValueMissing(nullptr);
+ return ValueMissing(nullptr);
}
bool HTMLTextAreaElement::ValueMissing(const String* value) const {
+ // For textarea elements, the value is missing only if it is mutable.
+ // https://html.spec.whatwg.org/multipage/form-elements.html#attr-textarea-required
return IsRequiredFormControl() && !IsDisabledOrReadOnly() &&
(value ? *value : this->value()).IsEmpty();
}
@@ -602,7 +614,10 @@ void HTMLTextAreaElement::UpdatePlaceholderText() {
IsPlaceholderVisible() ? CSSValueID::kBlock : CSSValueID::kNone, true);
UserAgentShadowRoot()->InsertBefore(placeholder, InnerEditorElement());
}
- placeholder->setTextContent(placeholder_text);
+ String normalized_value = placeholder_text;
+ // https://html.spec.whatwg.org/multipage/form-elements.html#attr-textarea-placeholder
+ ReplaceCRWithNewLine(normalized_value);
+ placeholder->setTextContent(normalized_value);
}
String HTMLTextAreaElement::GetPlaceholderValue() const {
diff --git a/chromium/third_party/blink/renderer/core/html/forms/html_text_area_element.h b/chromium/third_party/blink/renderer/core/html/forms/html_text_area_element.h
index 4980524e917..8700e494c15 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/html_text_area_element.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/html_text_area_element.h
@@ -117,6 +117,7 @@ class CORE_EXPORT HTMLTextAreaElement final : public TextControlElement {
const QualifiedName&,
const AtomicString&,
MutableCSSPropertyValueSet*) override;
+ bool TypeShouldForceLegacyLayout() const override { return true; }
LayoutObject* CreateLayoutObject(const ComputedStyle&, LegacyLayout) override;
void AppendToFormData(FormData&) override;
void ResetImpl() override;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/image_input_type.cc b/chromium/third_party/blink/renderer/core/html/forms/image_input_type.cc
index 53857f6329a..53abd4c58ba 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/image_input_type.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/image_input_type.cc
@@ -87,12 +87,12 @@ bool ImageInputType::SupportsValidation() const {
}
static IntPoint ExtractClickLocation(const Event& event) {
- if (!event.UnderlyingEvent() || !event.UnderlyingEvent()->IsMouseEvent())
+ const auto* mouse_event = DynamicTo<MouseEvent>(event.UnderlyingEvent());
+ if (!event.UnderlyingEvent() || !mouse_event)
return IntPoint();
- auto& mouse_event = *ToMouseEvent(event.UnderlyingEvent());
- if (!mouse_event.HasPosition())
+ if (!mouse_event->HasPosition())
return IntPoint();
- return IntPoint(mouse_event.offsetX(), mouse_event.offsetY());
+ return IntPoint(mouse_event->offsetX(), mouse_event->offsetY());
}
void ImageInputType::HandleDOMActivateEvent(Event& event) {
@@ -104,6 +104,10 @@ void ImageInputType::HandleDOMActivateEvent(Event& event) {
event.SetDefaultHandled();
}
+bool ImageInputType::TypeShouldForceLegacyLayout() const {
+ return true;
+}
+
LayoutObject* ImageInputType::CreateLayoutObject(const ComputedStyle& style,
LegacyLayout legacy) const {
if (use_fallback_content_)
@@ -188,7 +192,8 @@ unsigned ImageInputType::Height() const {
}
}
- GetElement().GetDocument().UpdateStyleAndLayout();
+ GetElement().GetDocument().UpdateStyleAndLayout(
+ DocumentUpdateReason::kJavaScript);
LayoutBox* box = GetElement().GetLayoutBox();
return box ? AdjustForAbsoluteZoom::AdjustInt(box->ContentHeight().ToInt(),
@@ -213,7 +218,8 @@ unsigned ImageInputType::Width() const {
}
}
- GetElement().GetDocument().UpdateStyleAndLayout();
+ GetElement().GetDocument().UpdateStyleAndLayout(
+ DocumentUpdateReason::kJavaScript);
LayoutBox* box = GetElement().GetLayoutBox();
return box ? AdjustForAbsoluteZoom::AdjustInt(box->ContentWidth().ToInt(),
@@ -277,13 +283,9 @@ void ImageInputType::CreateShadowSubtree() {
HTMLImageFallbackHelper::CreateAltTextShadowTree(GetElement());
}
-scoped_refptr<ComputedStyle> ImageInputType::CustomStyleForLayoutObject(
- scoped_refptr<ComputedStyle> new_style) {
- if (!use_fallback_content_)
- return new_style;
-
- return HTMLImageFallbackHelper::CustomStyleForAltText(GetElement(),
- std::move(new_style));
+void ImageInputType::CustomStyleForLayoutObject(ComputedStyle& style) {
+ if (use_fallback_content_)
+ HTMLImageFallbackHelper::CustomStyleForAltText(GetElement(), style);
}
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/image_input_type.h b/chromium/third_party/blink/renderer/core/html/forms/image_input_type.h
index 01d26bee19d..647923dc7d3 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/image_input_type.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/image_input_type.h
@@ -41,8 +41,7 @@ namespace blink {
class ImageInputType final : public BaseButtonInputType {
public:
explicit ImageInputType(HTMLInputElement&);
- scoped_refptr<ComputedStyle> CustomStyleForLayoutObject(
- scoped_refptr<ComputedStyle>) override;
+ void CustomStyleForLayoutObject(ComputedStyle& style) override;
private:
void CountUsage() override;
@@ -51,6 +50,7 @@ class ImageInputType final : public BaseButtonInputType {
void AppendToFormData(FormData&) const override;
String ResultForDialogSubmit() const override;
bool SupportsValidation() const override;
+ bool TypeShouldForceLegacyLayout() const override;
LayoutObject* CreateLayoutObject(const ComputedStyle&,
LegacyLayout) const override;
void HandleDOMActivateEvent(Event&) override;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/input_type.cc b/chromium/third_party/blink/renderer/core/html/forms/input_type.cc
index d0b43d21802..dbe99ae5e5b 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/input_type.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/input_type.cc
@@ -70,6 +70,7 @@
#include "third_party/blink/renderer/core/layout/layout_theme.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/json/json_values.h"
#include "third_party/blink/renderer/platform/text/platform_locale.h"
#include "third_party/blink/renderer/platform/text/text_break_iterator.h"
@@ -253,6 +254,8 @@ void InputType::SetValueAsDecimal(const Decimal& new_value,
void InputType::ReadingChecked() const {}
+void InputType::WillUpdateCheckedness(bool) {}
+
bool InputType::SupportsValidation() const {
return true;
}
@@ -296,7 +299,15 @@ bool InputType::RangeUnderflow(const String& value) const {
if (!numeric_value.IsFinite())
return false;
- return numeric_value < CreateStepRange(kRejectAny).Minimum();
+ StepRange step_range = CreateStepRange(kRejectAny);
+ if (step_range.HasReversedRange()) {
+ // With a reversed range, any value outside of the midnight-crossing valid
+ // range is considered underflow and overflow.
+ return numeric_value > step_range.Maximum() &&
+ numeric_value < step_range.Minimum();
+ } else {
+ return numeric_value < step_range.Minimum();
+ }
}
bool InputType::RangeOverflow(const String& value) const {
@@ -307,7 +318,15 @@ bool InputType::RangeOverflow(const String& value) const {
if (!numeric_value.IsFinite())
return false;
- return numeric_value > CreateStepRange(kRejectAny).Maximum();
+ StepRange step_range = CreateStepRange(kRejectAny);
+ if (step_range.HasReversedRange()) {
+ // With a reversed range, any value outside of the midnight-crossing valid
+ // range is considered underflow and overflow.
+ return numeric_value > step_range.Maximum() &&
+ numeric_value < step_range.Minimum();
+ } else {
+ return numeric_value > step_range.Maximum();
+ }
}
Decimal InputType::DefaultValueForStepUp() const {
@@ -389,6 +408,17 @@ String InputType::RangeUnderflowText(const Decimal&) const {
return String();
}
+String InputType::ReversedRangeOutOfRangeText(const Decimal&,
+ const Decimal&) const {
+ NOTREACHED();
+ return String();
+}
+
+String InputType::RangeInvalidText(const Decimal&, const Decimal&) const {
+ NOTREACHED();
+ return String();
+}
+
String InputType::TypeMismatchText() const {
return GetLocale().QueryString(IDS_FORM_VALIDATION_TYPE_MISMATCH);
}
@@ -444,6 +474,20 @@ std::pair<String, String> InputType::ValidationMessage(
StepRange step_range(CreateStepRange(kRejectAny));
+ if (step_range.Minimum() > step_range.Maximum() &&
+ !step_range.HasReversedRange()) {
+ return std::make_pair(
+ RangeInvalidText(step_range.Minimum(), step_range.Maximum()),
+ g_empty_string);
+ }
+
+ if (step_range.HasReversedRange() && numeric_value < step_range.Minimum() &&
+ numeric_value > step_range.Maximum()) {
+ return std::make_pair(
+ ReversedRangeOutOfRangeText(step_range.Minimum(), step_range.Maximum()),
+ g_empty_string);
+ }
+
if (numeric_value < step_range.Minimum())
return std::make_pair(RangeUnderflowText(step_range.Minimum()),
g_empty_string);
@@ -589,6 +633,9 @@ void InputType::SetValue(const String& sanitized_value,
case TextFieldEventBehavior::kDispatchChangeEvent:
GetElement().DispatchFormControlChangeEvent();
break;
+ case TextFieldEventBehavior::kDispatchInputEvent:
+ GetElement().DispatchInputEvent();
+ break;
case TextFieldEventBehavior::kDispatchInputAndChangeEvent:
GetElement().DispatchInputEvent();
GetElement().DispatchFormControlChangeEvent();
@@ -765,7 +812,7 @@ void InputType::ApplyStep(const Decimal& current,
Decimal new_value = current;
const AtomicString& step_string =
GetElement().FastGetAttribute(html_names::kStepAttr);
- if (!DeprecatedEqualIgnoringCase(step_string, "any") &&
+ if (!EqualIgnoringASCIICase(step_string, "any") &&
step_range.StepMismatch(current)) {
// Snap-to-step / clamping steps
// If the current value is not matched to step value:
@@ -786,10 +833,10 @@ void InputType::ApplyStep(const Decimal& current,
}
new_value = new_value + step_range.Step() * Decimal::FromDouble(count);
- if (!DeprecatedEqualIgnoringCase(step_string, "any"))
+ if (!EqualIgnoringASCIICase(step_string, "any"))
new_value = step_range.AlignValueForStep(current, new_value);
- // 7. If the element has a minimum, and value is less than that minimum,
+ // 8. If the element has a minimum, and value is less than that minimum,
// then set value to the smallest value that, when subtracted from the step
// base, is an integral multiple of the allowed value step, and that is more
// than or equal to minimum.
@@ -800,17 +847,23 @@ void InputType::ApplyStep(const Decimal& current,
new_value = aligned_minimum;
}
- // 8. If the element has a maximum, and value is greater than that maximum,
+ // 9. If the element has a maximum, and value is greater than that maximum,
// then set value to the largest value that, when subtracted from the step
// base, is an integral multiple of the allowed value step, and that is less
// than or equal to maximum.
if (new_value > step_range.Maximum())
new_value = aligned_maximum;
- // 9. Let value as string be the result of running the algorithm to convert
+ // 10. If either the method invoked was the stepDown() method and value is
+ // greater than valueBeforeStepping, or the method invoked was the stepUp()
+ // method and value is less than valueBeforeStepping, then return.
+ if ((count < 0 && current < new_value) || (count > 0 && current > new_value))
+ return;
+
+ // 11. Let value as string be the result of running the algorithm to convert
// a number to a string, as defined for the input element's type attribute's
// current state, on value.
- // 10. Set the value of the element to value as string.
+ // 12. Set the value of the element to value as string.
SetValueAsDecimal(new_value, event_behavior, exception_state);
if (AXObjectCache* cache = GetElement().GetDocument().ExistingAXObjectCache())
@@ -856,7 +909,7 @@ void InputType::StepUpFromLayoutObject(int n) {
// * If 0 is in-range, but not matched to step value
// - The value should be the larger matched value nearest to 0 if n > 0
// e.g. <input type=number min=-100 step=3> -> 2
- // - The value should be the smaler matched value nearest to 0 if n < 0
+ // - The value should be the smaller matched value nearest to 0 if n < 0
// e.g. <input type=number min=-100 step=3> -> -1
// As for date/datetime-local/month/time/week types, the current value is
// assumed as "the current local date/time".
@@ -941,12 +994,35 @@ Decimal InputType::FindStepBase(const Decimal& default_value) const {
return step_base;
}
+StepRange InputType::CreateReversibleStepRange(
+ AnyStepHandling any_step_handling,
+ const Decimal& step_base_default,
+ const Decimal& minimum_default,
+ const Decimal& maximum_default,
+ const StepRange::StepDescription& step_description) const {
+ return CreateStepRange(any_step_handling, step_base_default, minimum_default,
+ maximum_default, step_description,
+ /*supports_reversed_range=*/true);
+}
+
StepRange InputType::CreateStepRange(
AnyStepHandling any_step_handling,
const Decimal& step_base_default,
const Decimal& minimum_default,
const Decimal& maximum_default,
const StepRange::StepDescription& step_description) const {
+ return CreateStepRange(any_step_handling, step_base_default, minimum_default,
+ maximum_default, step_description,
+ /*supports_reversed_range=*/false);
+}
+
+StepRange InputType::CreateStepRange(
+ AnyStepHandling any_step_handling,
+ const Decimal& step_base_default,
+ const Decimal& minimum_default,
+ const Decimal& maximum_default,
+ const StepRange::StepDescription& step_description,
+ bool supports_reversed_range) const {
bool has_range_limitations = false;
const Decimal step_base = FindStepBase(step_base_default);
Decimal minimum =
@@ -964,17 +1040,20 @@ StepRange InputType::CreateStepRange(
const Decimal step = StepRange::ParseStep(
any_step_handling, step_description,
GetElement().FastGetAttribute(html_names::kStepAttr));
- return StepRange(step_base, minimum, maximum, has_range_limitations, step,
- step_description);
+ bool has_reversed_range =
+ has_range_limitations && supports_reversed_range && maximum < minimum;
+ return StepRange(step_base, minimum, maximum, has_range_limitations,
+ has_reversed_range, step, step_description);
}
void InputType::AddWarningToConsole(const char* message_format,
const String& value) const {
- GetElement().GetDocument().AddConsoleMessage(ConsoleMessage::Create(
- mojom::ConsoleMessageSource::kRendering,
- mojom::ConsoleMessageLevel::kWarning,
- String::Format(message_format,
- JSONValue::QuoteString(value).Utf8().c_str())));
+ GetElement().GetDocument().AddConsoleMessage(
+ MakeGarbageCollected<ConsoleMessage>(
+ mojom::ConsoleMessageSource::kRendering,
+ mojom::ConsoleMessageLevel::kWarning,
+ String::Format(message_format,
+ JSONValue::QuoteString(value).Utf8().c_str())));
}
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/input_type.h b/chromium/third_party/blink/renderer/core/html/forms/input_type.h
index 9f36023abe6..45ec3898704 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/input_type.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/input_type.h
@@ -104,7 +104,12 @@ class CORE_EXPORT InputType : public GarbageCollected<InputType> {
virtual void SetValueAsDecimal(const Decimal&,
TextFieldEventBehavior,
ExceptionState&) const;
+
+ // Functions related to 'checked'
+
virtual void ReadingChecked() const;
+ // The function is called just before updating checkedness.
+ virtual void WillUpdateCheckedness(bool new_checked);
// Validation functions
@@ -140,6 +145,10 @@ class CORE_EXPORT InputType : public GarbageCollected<InputType> {
virtual String BadInputText() const;
virtual String RangeOverflowText(const Decimal& maximum) const;
virtual String RangeUnderflowText(const Decimal& minimum) const;
+ virtual String ReversedRangeOutOfRangeText(const Decimal& minimum,
+ const Decimal& maximum) const;
+ virtual String RangeInvalidText(const Decimal& minimum,
+ const Decimal& maximum) const;
virtual String TypeMismatchText() const;
virtual String ValueMissingText() const;
virtual bool CanSetStringValue() const;
@@ -240,6 +249,11 @@ class CORE_EXPORT InputType : public GarbageCollected<InputType> {
const Decimal& minimum_default,
const Decimal& maximum_default,
const StepRange::StepDescription&) const;
+ StepRange CreateReversibleStepRange(AnyStepHandling,
+ const Decimal& step_base_default,
+ const Decimal& minimum_default,
+ const Decimal& maximum_default,
+ const StepRange::StepDescription&) const;
void AddWarningToConsole(const char* message_format,
const String& value) const;
@@ -252,6 +266,13 @@ class CORE_EXPORT InputType : public GarbageCollected<InputType> {
TextFieldEventBehavior,
ExceptionState&);
+ StepRange CreateStepRange(AnyStepHandling,
+ const Decimal& step_base_default,
+ const Decimal& minimum_default,
+ const Decimal& maximum_default,
+ const StepRange::StepDescription&,
+ bool supports_reversed_range) const;
+
Member<HTMLInputElement> element_;
DISALLOW_COPY_AND_ASSIGN(InputType);
diff --git a/chromium/third_party/blink/renderer/core/html/forms/input_type_view.cc b/chromium/third_party/blink/renderer/core/html/forms/input_type_view.cc
index ed41b3c08be..336cea6892e 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/input_type_view.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/input_type_view.cc
@@ -74,28 +74,29 @@ void InputTypeView::DispatchSimulatedClickIfActive(KeyboardEvent& event) const {
void InputTypeView::AccessKeyAction(bool) {
GetElement().focus(FocusParams(SelectionBehaviorOnFocus::kReset,
- kWebFocusTypeNone, nullptr));
+ mojom::blink::FocusType::kNone, nullptr));
}
bool InputTypeView::ShouldSubmitImplicitly(const Event& event) {
- return event.IsKeyboardEvent() &&
- event.type() == event_type_names::kKeypress &&
- ToKeyboardEvent(event).charCode() == '\r';
+ auto* keyboard_event = DynamicTo<KeyboardEvent>(event);
+ return keyboard_event && event.type() == event_type_names::kKeypress &&
+ keyboard_event->charCode() == '\r';
}
HTMLFormElement* InputTypeView::FormForSubmission() const {
return GetElement().Form();
}
+bool InputTypeView::TypeShouldForceLegacyLayout() const {
+ return false;
+}
+
LayoutObject* InputTypeView::CreateLayoutObject(const ComputedStyle& style,
LegacyLayout legacy) const {
return LayoutObject::CreateObject(&GetElement(), style, legacy);
}
-scoped_refptr<ComputedStyle> InputTypeView::CustomStyleForLayoutObject(
- scoped_refptr<ComputedStyle> original_style) {
- return original_style;
-}
+void InputTypeView::CustomStyleForLayoutObject(ComputedStyle&) {}
TextDirection InputTypeView::ComputedTextDirection() {
return GetElement().ComputedStyleRef().Direction();
@@ -111,12 +112,16 @@ bool InputTypeView::HasCustomFocusLogic() const {
void InputTypeView::HandleBlurEvent() {}
-void InputTypeView::HandleFocusInEvent(Element*, WebFocusType) {}
+void InputTypeView::HandleFocusInEvent(Element*, mojom::blink::FocusType) {}
void InputTypeView::StartResourceLoading() {}
void InputTypeView::ClosePopupView() {}
+bool InputTypeView::HasOpenedPopup() const {
+ return false;
+}
+
bool InputTypeView::NeedsShadowSubtree() const {
return true;
}
@@ -128,6 +133,14 @@ void InputTypeView::DestroyShadowSubtree() {
root->RemoveChildren();
}
+HTMLInputElement* InputTypeView::UploadButton() const {
+ return nullptr;
+}
+
+String InputTypeView::FileStatusText() const {
+ return String();
+}
+
void InputTypeView::AltAttributeChanged() {}
void InputTypeView::SrcAttributeChanged() {}
@@ -181,6 +194,10 @@ void InputTypeView::RestoreFormControlState(const FormControlState& state) {
GetElement().setValue(state[0]);
}
+bool InputTypeView::IsDraggedSlider() const {
+ return false;
+}
+
bool InputTypeView::HasBadInput() const {
return false;
}
@@ -190,4 +207,8 @@ void ClickHandlingState::Trace(Visitor* visitor) {
EventDispatchHandlingState::Trace(visitor);
}
+String InputTypeView::RawValue() const {
+ return g_empty_string;
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/input_type_view.h b/chromium/third_party/blink/renderer/core/html/forms/input_type_view.h
index ef47deadfda..cba7ed4d453 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/input_type_view.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/input_type_view.h
@@ -34,7 +34,7 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_INPUT_TYPE_VIEW_H_
#include "base/macros.h"
-#include "third_party/blink/public/platform/web_focus_type.h"
+#include "third_party/blink/public/mojom/input/focus_type.mojom-blink-forward.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/dom/events/event_dispatcher.h"
#include "third_party/blink/renderer/platform/heap/handle.h"
@@ -91,7 +91,8 @@ class CORE_EXPORT InputTypeView : public GarbageCollectedMixin {
virtual bool ShouldSubmitImplicitly(const Event&);
virtual HTMLFormElement* FormForSubmission() const;
virtual bool HasCustomFocusLogic() const;
- virtual void HandleFocusInEvent(Element* old_focused_element, WebFocusType);
+ virtual void HandleFocusInEvent(Element* old_focused_element,
+ mojom::blink::FocusType);
virtual void HandleBlurEvent();
virtual void HandleDOMActivateEvent(Event&);
virtual void AccessKeyAction(bool send_mouse_events);
@@ -99,16 +100,23 @@ class CORE_EXPORT InputTypeView : public GarbageCollectedMixin {
void DispatchSimulatedClickIfActive(KeyboardEvent&) const;
virtual void SubtreeHasChanged();
+ virtual bool TypeShouldForceLegacyLayout() const;
virtual LayoutObject* CreateLayoutObject(const ComputedStyle&,
LegacyLayout) const;
- virtual scoped_refptr<ComputedStyle> CustomStyleForLayoutObject(
- scoped_refptr<ComputedStyle>);
+ virtual void CustomStyleForLayoutObject(ComputedStyle& style);
virtual TextDirection ComputedTextDirection();
virtual void StartResourceLoading();
virtual void ClosePopupView();
+ virtual bool HasOpenedPopup() const;
+
+ // Functions for shadow trees
+
virtual bool NeedsShadowSubtree() const;
virtual void CreateShadowSubtree();
virtual void DestroyShadowSubtree();
+ virtual HTMLInputElement* UploadButton() const;
+ virtual String FileStatusText() const;
+
virtual void MinOrMaxAttributeChanged();
virtual void StepAttributeChanged();
virtual void AltAttributeChanged();
@@ -129,10 +137,13 @@ class CORE_EXPORT InputTypeView : public GarbageCollectedMixin {
virtual bool HasFallbackContent() const { return false; }
virtual FormControlState SaveFormControlState() const;
virtual void RestoreFormControlState(const FormControlState&);
+ virtual bool IsDraggedSlider() const;
// Validation functions
virtual bool HasBadInput() const;
+ virtual String RawValue() const;
+
protected:
InputTypeView(HTMLInputElement& element) : element_(&element) {}
HTMLInputElement& GetElement() const { return *element_; }
diff --git a/chromium/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc b/chromium/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
index 5b3fa566f25..360545b4afe 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
@@ -5,9 +5,9 @@
#include "third_party/blink/renderer/core/html/forms/internal_popup_menu.h"
#include "build/build_config.h"
+#include "third_party/blink/public/common/input/web_mouse_event.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/task_type.h"
-#include "third_party/blink/public/platform/web_mouse_event.h"
#include "third_party/blink/renderer/core/css/css_font_selector.h"
#include "third_party/blink/renderer/core/css/style_engine.h"
#include "third_party/blink/renderer/core/dom/element_traversal.h"
@@ -31,6 +31,7 @@
#include "third_party/blink/renderer/platform/fonts/font_selector_client.h"
#include "third_party/blink/renderer/platform/geometry/int_rect.h"
#include "third_party/blink/renderer/platform/text/platform_locale.h"
+#include "ui/base/ui_base_features.h"
namespace blink {
@@ -286,6 +287,8 @@ void InternalPopupMenu::WriteDocument(SharedBuffer* data) {
AddProperty("scaleFactor", scale_factor, data);
bool is_rtl = !owner_style->IsLeftToRightDirection();
AddProperty("isRTL", is_rtl, data);
+ AddProperty("isFormControlsRefreshEnabled",
+ features::IsFormControlsRefreshEnabled(), data);
AddProperty("paddingStart",
is_rtl ? owner_element.ClientPaddingRight().ToDouble()
: owner_element.ClientPaddingLeft().ToDouble(),
@@ -411,11 +414,11 @@ void InternalPopupMenu::AddSeparator(ItemIterationContext& context,
PagePopupClient::AddString("},\n", data);
}
-void InternalPopupMenu::SelectFontsFromOwnerDocument(Document& document) {
+CSSFontSelector* InternalPopupMenu::CreateCSSFontSelector(
+ Document& popup_document) {
Document& owner_document = OwnerElement().GetDocument();
- document.GetStyleEngine().SetFontSelector(
- MakeGarbageCollected<PopupMenuCSSFontSelector>(
- &document, owner_document.GetStyleEngine().GetFontSelector()));
+ return MakeGarbageCollected<PopupMenuCSSFontSelector>(
+ &popup_document, owner_document.GetStyleEngine().GetFontSelector());
}
void InternalPopupMenu::SetValueAndClosePopup(int num_value,
diff --git a/chromium/third_party/blink/renderer/core/html/forms/internal_popup_menu.h b/chromium/third_party/blink/renderer/core/html/forms/internal_popup_menu.h
index b513ae14683..b6af0908fa0 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/internal_popup_menu.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/internal_popup_menu.h
@@ -12,6 +12,7 @@
namespace blink {
class ChromeClient;
+class CSSFontSelector;
class PagePopup;
class HTMLElement;
class HTMLHRElement;
@@ -49,7 +50,7 @@ class CORE_EXPORT InternalPopupMenu final : public PopupMenu,
// PagePopupClient functions:
void WriteDocument(SharedBuffer*) override;
- void SelectFontsFromOwnerDocument(Document&) override;
+ CSSFontSelector* CreateCSSFontSelector(Document& popup_document) override;
void SetValueAndClosePopup(int, const String&) override;
void SetValue(const String&) override;
void CancelPopup() override;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/internal_popup_menu_test.cc b/chromium/third_party/blink/renderer/core/html/forms/internal_popup_menu_test.cc
index 72c34bf2c8f..e74aa17fb04 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/internal_popup_menu_test.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/internal_popup_menu_test.cc
@@ -25,14 +25,13 @@ TEST(InternalPopupMenuTest, WriteDocumentInStyleDirtyTree) {
auto dummy_page_holder_ =
std::make_unique<DummyPageHolder>(IntSize(800, 600));
Document& document = dummy_page_holder_->GetDocument();
- document.body()->SetInnerHTMLFromString(R"HTML(
+ document.body()->setInnerHTML(R"HTML(
<select id="select">
<option value="foo">Foo</option>
<option value="bar" style="display:none">Bar</option>
</select>
)HTML");
- document.View()->UpdateAllLifecyclePhases(
- DocumentLifecycle::LifecycleUpdateReason::kTest);
+ document.View()->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest);
auto* select = To<HTMLSelectElement>(document.getElementById("select"));
ASSERT_TRUE(select);
auto* menu = MakeGarbageCollected<InternalPopupMenu>(
@@ -50,7 +49,7 @@ TEST(InternalPopupMenuTest, ShowSelectDisplayNone) {
auto dummy_page_holder_ =
std::make_unique<DummyPageHolder>(IntSize(800, 600));
Document& document = dummy_page_holder_->GetDocument();
- document.body()->SetInnerHTMLFromString(R"HTML(
+ document.body()->setInnerHTML(R"HTML(
<div id="container">
<select id="select">
<option>1</option>
@@ -58,8 +57,8 @@ TEST(InternalPopupMenuTest, ShowSelectDisplayNone) {
</select>
</div>
)HTML");
- document.View()->UpdateAllLifecyclePhases(
- DocumentLifecycle::LifecycleUpdateReason::kTest);
+ document.View()->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest);
+
auto* div = document.getElementById("container");
auto* select = To<HTMLSelectElement>(document.getElementById("select"));
ASSERT_TRUE(select);
diff --git a/chromium/third_party/blink/renderer/core/html/forms/labels_node_list.h b/chromium/third_party/blink/renderer/core/html/forms/labels_node_list.h
index ba26f27e7a7..106dae21b52 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/labels_node_list.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/labels_node_list.h
@@ -25,7 +25,6 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_LABELS_NODE_LIST_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_LABELS_NODE_LIST_H_
-#include "base/memory/scoped_refptr.h"
#include "third_party/blink/renderer/core/dom/live_node_list.h"
namespace blink {
diff --git a/chromium/third_party/blink/renderer/core/html/forms/listed_element.cc b/chromium/third_party/blink/renderer/core/html/forms/listed_element.cc
index 53ef3614d4b..5bfc398d3bb 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/listed_element.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/listed_element.cc
@@ -45,6 +45,7 @@
#include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/page/validation_message_client.h"
+#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/text/bidi_text_run.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
@@ -314,8 +315,7 @@ void ListedElement::UpdateWillValidateCache() {
}
bool ListedElement::CustomError() const {
- return ToHTMLElement().willValidate() &&
- !custom_validation_message_.IsEmpty();
+ return !custom_validation_message_.IsEmpty();
}
bool ListedElement::HasBadInput() const {
@@ -371,7 +371,9 @@ void ListedElement::SetCustomValidationMessage(const String& message) {
}
String ListedElement::validationMessage() const {
- return CustomError() ? custom_validation_message_ : String();
+ return ToHTMLElement().willValidate() && CustomError()
+ ? custom_validation_message_
+ : String();
}
String ListedElement::ValidationSubMessage() const {
@@ -493,7 +495,7 @@ bool ListedElement::reportValidity() {
// Update layout now before calling IsFocusable(), which has
// !LayoutObject()->NeedsLayout() assertion.
HTMLElement& element = ToHTMLElement();
- element.GetDocument().UpdateStyleAndLayout();
+ element.GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kForm);
if (element.IsFocusable()) {
ShowValidationMessage();
return false;
@@ -503,8 +505,9 @@ bool ListedElement::reportValidity() {
"An invalid form control with name='%name' is not focusable.");
message.Replace("%name", GetName());
element.GetDocument().AddConsoleMessage(
- ConsoleMessage::Create(mojom::ConsoleMessageSource::kRendering,
- mojom::ConsoleMessageLevel::kError, message));
+ MakeGarbageCollected<ConsoleMessage>(
+ mojom::ConsoleMessageSource::kRendering,
+ mojom::ConsoleMessageLevel::kError, message));
}
return false;
}
diff --git a/chromium/third_party/blink/renderer/core/html/forms/menu_list_inner_element.cc b/chromium/third_party/blink/renderer/core/html/forms/menu_list_inner_element.cc
new file mode 100644
index 00000000000..abd197d33c8
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/html/forms/menu_list_inner_element.cc
@@ -0,0 +1,76 @@
+// Copyright 2020 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/menu_list_inner_element.h"
+
+#include "third_party/blink/renderer/core/dom/node_computed_style.h"
+#include "third_party/blink/renderer/core/html/forms/html_select_element.h"
+#include "third_party/blink/renderer/core/layout/layout_theme.h"
+#include "third_party/blink/renderer/core/style/computed_style.h"
+
+namespace blink {
+
+MenuListInnerElement::MenuListInnerElement(Document& document)
+ : HTMLDivElement(document) {
+ SetHasCustomStyleCallbacks();
+}
+
+scoped_refptr<ComputedStyle>
+MenuListInnerElement::CustomStyleForLayoutObject() {
+ const ComputedStyle& parent_style = OwnerShadowHost()->ComputedStyleRef();
+ scoped_refptr<ComputedStyle> style =
+ ComputedStyle::CreateAnonymousStyleWithDisplay(parent_style,
+ EDisplay::kBlock);
+ style->SetFlexGrow(1);
+ style->SetFlexShrink(1);
+ // min-width: 0; is needed for correct shrinking.
+ style->SetMinWidth(Length::Fixed(0));
+ style->SetHasLineIfEmpty(true);
+ style->SetOverflowX(EOverflow::kHidden);
+ style->SetOverflowY(EOverflow::kHidden);
+ style->SetShouldIgnoreOverflowPropertyForInlineBlockBaseline();
+ style->SetTextOverflow(parent_style.TextOverflow());
+ style->SetUserModify(EUserModify::kReadOnly);
+
+ // Use margin:auto instead of align-items:center to get safe centering, i.e.
+ // when the content overflows, treat it the same as align-items: flex-start.
+ // But we only do that for the cases where html.css would otherwise use
+ // center.
+ if (parent_style.AlignItemsPosition() == ItemPosition::kCenter) {
+ style->SetMarginTop(Length());
+ style->SetMarginBottom(Length());
+ style->SetAlignSelfPosition(ItemPosition::kFlexStart);
+ }
+
+ // We set margin-left/right instead of padding-left/right to clip text by
+ // 'overflow: hidden'.
+ LayoutTheme& theme = LayoutTheme::GetTheme();
+ Length margin_start =
+ Length::Fixed(theme.PopupInternalPaddingStart(parent_style));
+ Length margin_end = Length::Fixed(
+ theme.PopupInternalPaddingEnd(GetDocument().GetFrame(), parent_style));
+ if (parent_style.IsLeftToRightDirection()) {
+ style->SetTextAlign(ETextAlign::kLeft);
+ style->SetMarginLeft(margin_start);
+ style->SetMarginRight(margin_end);
+ } else {
+ style->SetTextAlign(ETextAlign::kRight);
+ style->SetMarginLeft(margin_end);
+ style->SetMarginRight(margin_start);
+ }
+ style->SetPaddingTop(
+ Length::Fixed(theme.PopupInternalPaddingTop(parent_style)));
+ style->SetPaddingBottom(
+ Length::Fixed(theme.PopupInternalPaddingBottom(parent_style)));
+
+ if (const ComputedStyle* option_style =
+ To<HTMLSelectElement>(OwnerShadowHost())->OptionStyle()) {
+ style->SetDirection(option_style->Direction());
+ style->SetUnicodeBidi(option_style->GetUnicodeBidi());
+ }
+
+ return style;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/menu_list_inner_element.h b/chromium/third_party/blink/renderer/core/html/forms/menu_list_inner_element.h
new file mode 100644
index 00000000000..b3f7085cb59
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/html/forms/menu_list_inner_element.h
@@ -0,0 +1,21 @@
+// Copyright 2020 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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_MENU_LIST_INNER_ELEMENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_MENU_LIST_INNER_ELEMENT_H_
+
+#include "third_party/blink/renderer/core/html/html_div_element.h"
+
+namespace blink {
+
+class MenuListInnerElement : public HTMLDivElement {
+ public:
+ explicit MenuListInnerElement(Document& document);
+
+ private:
+ scoped_refptr<ComputedStyle> CustomStyleForLayoutObject() override;
+};
+
+} // namespace blink
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_MENU_LIST_INNER_ELEMENT_H_
diff --git a/chromium/third_party/blink/renderer/core/html/forms/month_input_type.cc b/chromium/third_party/blink/renderer/core/html/forms/month_input_type.cc
index 807f7e5cafb..e72af81f289 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/month_input_type.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/month_input_type.cc
@@ -30,6 +30,7 @@
#include "third_party/blink/renderer/core/html/forms/month_input_type.h"
+#include "third_party/blink/public/strings/grit/blink_strings.h"
#include "third_party/blink/renderer/core/frame/web_feature.h"
#include "third_party/blink/renderer/core/html/forms/date_time_fields_state.h"
#include "third_party/blink/renderer/core/html/forms/html_input_element.h"
@@ -168,4 +169,8 @@ bool MonthInputType::IsValidFormat(bool has_year,
return has_year && has_month;
}
+String MonthInputType::AriaRoleForPickerIndicator() const {
+ return GetLocale().QueryString(IDS_AX_CALENDAR_SHOW_MONTH_PICKER);
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/month_input_type.h b/chromium/third_party/blink/renderer/core/html/forms/month_input_type.h
index 27feb7cf4ae..0bbd850b3be 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/month_input_type.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/month_input_type.h
@@ -66,6 +66,7 @@ class MonthInputType final : public BaseTemporalInputType {
bool has_hour,
bool has_minute,
bool has_second) const override;
+ String AriaRoleForPickerIndicator() const override;
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.cc b/chromium/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.cc
index 665de490bc9..76092674346 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.cc
@@ -30,6 +30,7 @@
#include "third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.h"
+#include "third_party/blink/public/mojom/input/focus_type.mojom-blink.h"
#include "third_party/blink/renderer/core/css/style_change_reason.h"
#include "third_party/blink/renderer/core/css_value_keywords.h"
#include "third_party/blink/renderer/core/dom/document.h"
@@ -56,6 +57,7 @@
#include "third_party/blink/renderer/platform/text/date_time_format.h"
#include "third_party/blink/renderer/platform/text/platform_locale.h"
#include "third_party/blink/renderer/platform/wtf/date_math.h"
+#include "ui/base/ui_base_features.h"
namespace blink {
@@ -162,9 +164,10 @@ ClearButtonElement* MultipleFieldsTemporalInputTypeView::GetClearButtonElement()
PickerIndicatorElement*
MultipleFieldsTemporalInputTypeView::GetPickerIndicatorElement() const {
- return ToPickerIndicatorElementOrDie(
- GetElement().UserAgentShadowRoot()->getElementById(
- shadow_element_names::PickerIndicator()));
+ auto* element = GetElement().UserAgentShadowRoot()->getElementById(
+ shadow_element_names::PickerIndicator());
+ CHECK(!element || IsA<PickerIndicatorElement>(element));
+ return To<PickerIndicatorElement>(element);
}
inline bool MultipleFieldsTemporalInputTypeView::ContainsFocusedShadowElement()
@@ -174,7 +177,7 @@ inline bool MultipleFieldsTemporalInputTypeView::ContainsFocusedShadowElement()
}
void MultipleFieldsTemporalInputTypeView::DidBlurFromControl(
- WebFocusType focus_type) {
+ mojom::blink::FocusType focus_type) {
// We don't need to call blur(). This function is called when control
// lost focus.
@@ -188,7 +191,7 @@ void MultipleFieldsTemporalInputTypeView::DidBlurFromControl(
}
void MultipleFieldsTemporalInputTypeView::DidFocusOnControl(
- WebFocusType focus_type) {
+ mojom::blink::FocusType focus_type) {
// We don't need to call focus(). This function is called when control
// got focus.
@@ -278,8 +281,7 @@ bool MultipleFieldsTemporalInputTypeView::
void MultipleFieldsTemporalInputTypeView::PickerIndicatorChooseValue(
const String& value) {
if (GetElement().IsValidValue(value)) {
- GetElement().setValue(value,
- TextFieldEventBehavior::kDispatchInputAndChangeEvent);
+ GetElement().setValue(value, TextFieldEventBehavior::kDispatchInputEvent);
return;
}
@@ -296,7 +298,6 @@ void MultipleFieldsTemporalInputTypeView::PickerIndicatorChooseValue(
if (date.ParseDate(value, 0, end) && end == value.length())
edit->SetOnlyYearMonthDay(date);
}
- GetElement().DispatchFormControlChangeEvent();
}
void MultipleFieldsTemporalInputTypeView::PickerIndicatorChooseValue(
@@ -304,11 +305,10 @@ void MultipleFieldsTemporalInputTypeView::PickerIndicatorChooseValue(
DCHECK(std::isfinite(value) || std::isnan(value));
if (std::isnan(value)) {
GetElement().setValue(g_empty_string,
- TextFieldEventBehavior::kDispatchInputAndChangeEvent);
+ TextFieldEventBehavior::kDispatchInputEvent);
} else {
- GetElement().setValueAsNumber(
- value, ASSERT_NO_EXCEPTION,
- TextFieldEventBehavior::kDispatchInputAndChangeEvent);
+ GetElement().setValueAsNumber(value, ASSERT_NO_EXCEPTION,
+ TextFieldEventBehavior::kDispatchInputEvent);
}
}
@@ -334,6 +334,14 @@ bool MultipleFieldsTemporalInputTypeView::SetupDateTimeChooserParameters(
return GetElement().SetupDateTimeChooserParameters(parameters);
}
+void MultipleFieldsTemporalInputTypeView::DidEndChooser() {
+ GetElement().EnqueueChangeEvent();
+}
+
+String MultipleFieldsTemporalInputTypeView::AriaRoleForPickerIndicator() const {
+ return input_type_->AriaRoleForPickerIndicator();
+}
+
MultipleFieldsTemporalInputTypeView::MultipleFieldsTemporalInputTypeView(
HTMLInputElement& element,
BaseTemporalInputType& input_type)
@@ -357,25 +365,17 @@ void MultipleFieldsTemporalInputTypeView::Blur() {
ClosePopupView();
}
-scoped_refptr<ComputedStyle>
-MultipleFieldsTemporalInputTypeView::CustomStyleForLayoutObject(
- scoped_refptr<ComputedStyle> original_style) {
- EDisplay original_display = original_style->Display();
+void MultipleFieldsTemporalInputTypeView::CustomStyleForLayoutObject(
+ ComputedStyle& style) {
+ EDisplay original_display = style.Display();
EDisplay new_display = original_display;
if (original_display == EDisplay::kInline ||
original_display == EDisplay::kInlineBlock)
new_display = EDisplay::kInlineFlex;
else if (original_display == EDisplay::kBlock)
new_display = EDisplay::kFlex;
- TextDirection content_direction = ComputedTextDirection();
- if (original_style->Direction() == content_direction &&
- original_display == new_display)
- return original_style;
-
- scoped_refptr<ComputedStyle> style = ComputedStyle::Clone(*original_style);
- style->SetDirection(content_direction);
- style->SetDisplay(new_display);
- return style;
+ style.SetDisplay(new_display);
+ style.SetDirection(ComputedTextDirection());
}
void MultipleFieldsTemporalInputTypeView::CreateShadowSubtree() {
@@ -388,7 +388,7 @@ void MultipleFieldsTemporalInputTypeView::CreateShadowSubtree() {
MakeGarbageCollected<DateTimeEditElement, Document&,
DateTimeEditElement::EditControlOwner&>(document,
*this));
- if (!RuntimeEnabledFeatures::FormControlsRefreshEnabled()) {
+ if (!features::IsFormControlsRefreshEnabled()) {
GetElement().UpdateView();
container->AppendChild(
MakeGarbageCollected<ClearButtonElement, Document&,
@@ -441,16 +441,18 @@ void MultipleFieldsTemporalInputTypeView::HandleClickEvent(MouseEvent& event) {
void MultipleFieldsTemporalInputTypeView::HandleFocusInEvent(
Element* old_focused_element,
- WebFocusType type) {
+ mojom::blink::FocusType type) {
DateTimeEditElement* edit = GetDateTimeEditElement();
if (!edit || is_destroying_shadow_subtree_)
return;
- if (type == kWebFocusTypeBackward) {
+ if (type == mojom::blink::FocusType::kBackward) {
if (GetElement().GetDocument().GetPage())
GetElement().GetDocument().GetPage()->GetFocusController().AdvanceFocus(
type);
- } else if (type == kWebFocusTypeNone || type == kWebFocusTypeMouse ||
- type == kWebFocusTypePage || type == kWebFocusTypeAccessKey) {
+ } else if (type == mojom::blink::FocusType::kNone ||
+ type == mojom::blink::FocusType::kMouse ||
+ type == mojom::blink::FocusType::kPage ||
+ type == mojom::blink::FocusType::kAccessKey) {
edit->FocusByOwner(old_focused_element);
} else {
edit->FocusByOwner();
@@ -488,7 +490,7 @@ void MultipleFieldsTemporalInputTypeView::HandleKeydownEvent(
((event.key() == "ArrowDown" && event.getModifierState("Alt")) ||
(LayoutTheme::GetTheme().ShouldOpenPickerWithF4Key() &&
event.key() == "F4") ||
- (RuntimeEnabledFeatures::FormControlsRefreshEnabled() &&
+ (features::IsFormControlsRefreshEnabled() &&
(event.key() == "Enter" || event.key() == " ")))) {
if (PickerIndicatorElement* element = GetPickerIndicatorElement())
element->OpenPopup();
@@ -606,6 +608,13 @@ void MultipleFieldsTemporalInputTypeView::ClosePopupView() {
picker->ClosePopup();
}
+bool MultipleFieldsTemporalInputTypeView::HasOpenedPopup() const {
+ if (PickerIndicatorElement* picker = GetPickerIndicatorElement())
+ return picker->HasOpenedPopup();
+
+ return false;
+}
+
void MultipleFieldsTemporalInputTypeView::ValueAttributeChanged() {
if (!GetElement().HasDirtyValue())
UpdateView();
@@ -687,4 +696,8 @@ AXObject* MultipleFieldsTemporalInputTypeView::PopupRootAXObject() {
return nullptr;
}
+String MultipleFieldsTemporalInputTypeView::RawValue() const {
+ return GetDateTimeEditElement()->innerText();
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.h b/chromium/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.h
index 7ec1c405cce..2f4522ade10 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/multiple_fields_temporal_input_type_view.h
@@ -31,7 +31,7 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_MULTIPLE_FIELDS_TEMPORAL_INPUT_TYPE_VIEW_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_MULTIPLE_FIELDS_TEMPORAL_INPUT_TYPE_VIEW_H_
-#include "third_party/blink/public/platform/web_focus_type.h"
+#include "third_party/blink/public/mojom/input/focus_type.mojom-blink-forward.h"
#include "third_party/blink/renderer/core/html/forms/clear_button_element.h"
#include "third_party/blink/renderer/core/html/forms/date_time_edit_element.h"
#include "third_party/blink/renderer/core/html/forms/input_type_view.h"
@@ -58,10 +58,12 @@ class MultipleFieldsTemporalInputTypeView final
~MultipleFieldsTemporalInputTypeView() override;
void Trace(Visitor*) override;
+ String RawValue() const override;
+
private:
// DateTimeEditElement::EditControlOwner functions
- void DidBlurFromControl(WebFocusType) final;
- void DidFocusOnControl(WebFocusType) final;
+ void DidBlurFromControl(mojom::blink::FocusType) final;
+ void DidFocusOnControl(mojom::blink::FocusType) final;
void EditControlValueChanged() final;
String FormatDateTimeFieldsState(const DateTimeFieldsState&) const override;
bool IsEditControlOwnerDisabled() const final;
@@ -84,6 +86,8 @@ class MultipleFieldsTemporalInputTypeView final
void PickerIndicatorChooseValue(double) final;
Element& PickerOwnerElement() const final;
bool SetupDateTimeChooserParameters(DateTimeChooserParameters&) final;
+ void DidEndChooser() final;
+ String AriaRoleForPickerIndicator() const final;
// ClearButtonElement::ClearButtonOwner functions.
void FocusAndSelectClearButtonOwner() override;
@@ -93,14 +97,15 @@ class MultipleFieldsTemporalInputTypeView final
// InputTypeView functions
void Blur() final;
void ClosePopupView() override;
- scoped_refptr<ComputedStyle> CustomStyleForLayoutObject(
- scoped_refptr<ComputedStyle>) override;
+ bool HasOpenedPopup() const override;
+ void CustomStyleForLayoutObject(ComputedStyle& style) override;
void CreateShadowSubtree() final;
void DestroyShadowSubtree() final;
void DisabledAttributeChanged() final;
void ForwardEvent(Event&) final;
void HandleClickEvent(MouseEvent&) final;
- void HandleFocusInEvent(Element* old_focused_element, WebFocusType) final;
+ void HandleFocusInEvent(Element* old_focused_element,
+ mojom::blink::FocusType) final;
void HandleKeydownEvent(KeyboardEvent&) final;
bool HasBadInput() const override;
bool HasCustomFocusLogic() const final;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/number_input_type.cc b/chromium/third_party/blink/renderer/core/html/forms/number_input_type.cc
index 996addc47ea..d4548d0aba5 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/number_input_type.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/number_input_type.cc
@@ -153,7 +153,7 @@ bool NumberInputType::SizeShouldIncludeDecoration(int default_size,
const String step_string =
GetElement().FastGetAttribute(html_names::kStepAttr);
- if (DeprecatedEqualIgnoringCase(step_string, "any"))
+ if (EqualIgnoringASCIICase(step_string, "any"))
return false;
const Decimal minimum = ParseToDecimalForNumberType(
@@ -246,10 +246,7 @@ void NumberInputType::WarnIfValueIsInvalid(const String& value) const {
if (value.IsEmpty() || !GetElement().SanitizeValue(value).IsEmpty())
return;
AddWarningToConsole(
- "The specified value %s is not a valid number. The value must match to "
- "the following regular expression: "
- "-?(\\d+|\\d+\\.\\d+|\\.\\d+)([eE][-+]?\\d+)?",
- value);
+ "The specified value %s cannot be parsed, or is out of range.", value);
}
bool NumberInputType::HasBadInput() const {
@@ -283,7 +280,7 @@ void NumberInputType::MinOrMaxAttributeChanged() {
if (GetElement().GetLayoutObject()) {
GetElement()
.GetLayoutObject()
- ->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(
+ ->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
layout_invalidation_reason::kAttributeChanged);
}
}
@@ -294,7 +291,7 @@ void NumberInputType::StepAttributeChanged() {
if (GetElement().GetLayoutObject()) {
GetElement()
.GetLayoutObject()
- ->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(
+ ->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
layout_invalidation_reason::kAttributeChanged);
}
}
diff --git a/chromium/third_party/blink/renderer/core/html/forms/option_list.cc b/chromium/third_party/blink/renderer/core/html/forms/option_list.cc
index 1b990eebaed..3fb7638b0ff 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/option_list.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/option_list.cc
@@ -27,8 +27,7 @@ void OptionListIterator::Advance(HTMLOptionElement* previous) {
current_ = option;
return;
}
- if (IsA<HTMLOptGroupElement>(current) &&
- current->parentNode() == select_.Get()) {
+ if (IsA<HTMLOptGroupElement>(current) && current->parentNode() == select_) {
if ((current_ = Traversal<HTMLOptionElement>::FirstChild(*current)))
return;
}
diff --git a/chromium/third_party/blink/renderer/core/html/forms/option_list.h b/chromium/third_party/blink/renderer/core/html/forms/option_list.h
index cd3ea21d4e5..51f8a5960a5 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/option_list.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/option_list.h
@@ -18,7 +18,7 @@ class CORE_EXPORT OptionListIterator final {
public:
explicit OptionListIterator(const HTMLSelectElement* select)
- : select_(select) {
+ : select_(select), current_(nullptr) {
if (select_)
Advance(nullptr);
}
@@ -37,8 +37,8 @@ class CORE_EXPORT OptionListIterator final {
private:
void Advance(HTMLOptionElement* current);
- Member<const HTMLSelectElement> select_;
- Member<HTMLOptionElement> current_; // nullptr means we reached to the end.
+ const HTMLSelectElement* select_;
+ HTMLOptionElement* current_; // nullptr means we reached to the end.
};
// OptionList class is a lightweight version of HTMLOptionsCollection.
@@ -46,13 +46,13 @@ class OptionList final {
STACK_ALLOCATED();
public:
- explicit OptionList(const HTMLSelectElement& select) : select_(select) {}
+ explicit OptionList(const HTMLSelectElement& select) : select_(&select) {}
using Iterator = OptionListIterator;
Iterator begin() { return Iterator(select_); }
Iterator end() { return Iterator(nullptr); }
private:
- Member<const HTMLSelectElement> select_;
+ const HTMLSelectElement* select_;
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/option_list_test.cc b/chromium/third_party/blink/renderer/core/html/forms/option_list_test.cc
index 863b8eb67c4..71941a49f9e 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/option_list_test.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/option_list_test.cc
@@ -41,12 +41,12 @@ TEST_F(OptionListTest, Empty) {
}
TEST_F(OptionListTest, OptionOnly) {
- Select().SetInnerHTMLFromString(
+ Select().setInnerHTML(
"text<input><option id=o1></option><input><option "
"id=o2></option><input>");
auto* div = To<HTMLElement>(
Select().GetDocument().CreateRawElement(html_names::kDivTag));
- div->SetInnerHTMLFromString("<option id=o3></option>");
+ div->setInnerHTML("<option id=o3></option>");
Select().AppendChild(div);
OptionList list = Select().GetOptionList();
OptionList::Iterator iter = list.begin();
@@ -59,7 +59,7 @@ TEST_F(OptionListTest, OptionOnly) {
}
TEST_F(OptionListTest, Optgroup) {
- Select().SetInnerHTMLFromString(
+ Select().setInnerHTML(
"<optgroup><option id=g11></option><option id=g12></option></optgroup>"
"<optgroup><option id=g21></option></optgroup>"
"<optgroup></optgroup>"
@@ -80,7 +80,7 @@ TEST_F(OptionListTest, Optgroup) {
EXPECT_EQ(list.end(), iter);
To<HTMLElement>(Select().firstChild())
- ->SetInnerHTMLFromString(
+ ->setInnerHTML(
"<optgroup><option id=gg11></option></optgroup>"
"<option id=g11></option>");
list = Select().GetOptionList();
diff --git a/chromium/third_party/blink/renderer/core/html/forms/password_input_type_test.cc b/chromium/third_party/blink/renderer/core/html/forms/password_input_type_test.cc
index 0f8b2d17211..9dc42a72620 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/password_input_type_test.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/password_input_type_test.cc
@@ -51,10 +51,9 @@ class MockInsecureInputService : public mojom::blink::InsecureInputService {
TEST(PasswordInputTypeTest, DidEditFieldEvent) {
auto page_holder = std::make_unique<DummyPageHolder>(IntSize(2000, 2000));
MockInsecureInputService mock_service(page_holder->GetFrame());
- page_holder->GetDocument().body()->SetInnerHTMLFromString(
- "<input type='password'>");
+ page_holder->GetDocument().body()->setInnerHTML("<input type='password'>");
page_holder->GetDocument().View()->UpdateAllLifecyclePhases(
- DocumentLifecycle::LifecycleUpdateReason::kTest);
+ DocumentUpdateReason::kTest);
blink::test::RunPendingTasks();
EXPECT_EQ(0u, mock_service.DidEditFieldCalls());
// Simulate a text field edit.
@@ -77,12 +76,11 @@ TEST(PasswordInputTypeTest, DidEditFieldEventNotSentFromSecureContext) {
nullptr /* extra_data */);
blink::test::RunPendingTasks();
MockInsecureInputService mock_service(page_holder->GetFrame());
- page_holder->GetDocument().SetSecureContextStateForTesting(
- SecureContextState::kSecure);
- page_holder->GetDocument().body()->SetInnerHTMLFromString(
- "<input type='password'>");
+ page_holder->GetDocument().SetSecureContextModeForTesting(
+ SecureContextMode::kSecureContext);
+ page_holder->GetDocument().body()->setInnerHTML("<input type='password'>");
page_holder->GetDocument().View()->UpdateAllLifecyclePhases(
- DocumentLifecycle::LifecycleUpdateReason::kTest);
+ DocumentUpdateReason::kTest);
// Simulate a text field edit.
page_holder->GetDocument().MaybeQueueSendDidEditFieldInInsecureContext();
// No message should have been sent from a secure context.
diff --git a/chromium/third_party/blink/renderer/core/html/forms/picker_indicator_element.cc b/chromium/third_party/blink/renderer/core/html/forms/picker_indicator_element.cc
index 544b8a6b204..50036ddb04b 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/picker_indicator_element.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/picker_indicator_element.cc
@@ -40,6 +40,7 @@
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/platform/text/platform_locale.h"
#include "third_party/blink/renderer/platform/web_test_support.h"
+#include "ui/base/ui_base_features.h"
namespace blink {
@@ -59,7 +60,7 @@ PickerIndicatorElement::~PickerIndicatorElement() {
LayoutObject* PickerIndicatorElement::CreateLayoutObject(
const ComputedStyle& style,
LegacyLayout legacy) {
- if (RuntimeEnabledFeatures::FormControlsRefreshEnabled())
+ if (features::IsFormControlsRefreshEnabled())
return HTMLDivElement::CreateLayoutObject(style, legacy);
return new LayoutDetailsMarker(this);
@@ -72,12 +73,12 @@ void PickerIndicatorElement::DefaultEventHandler(Event& event) {
picker_indicator_owner_->IsPickerIndicatorOwnerDisabledOrReadOnly())
return;
+ auto* keyboard_event = DynamicTo<KeyboardEvent>(event);
if (event.type() == event_type_names::kClick) {
OpenPopup();
event.SetDefaultHandled();
- } else if (event.type() == event_type_names::kKeypress &&
- event.IsKeyboardEvent()) {
- int char_code = ToKeyboardEvent(event).charCode();
+ } else if (event.type() == event_type_names::kKeypress && keyboard_event) {
+ int char_code = keyboard_event->charCode();
if (char_code == ' ' || char_code == '\r') {
OpenPopup();
event.SetDefaultHandled();
@@ -109,10 +110,16 @@ void PickerIndicatorElement::DidChooseValue(double value) {
void PickerIndicatorElement::DidEndChooser() {
chooser_.Clear();
+ picker_indicator_owner_->DidEndChooser();
+ if (::features::IsFormControlsRefreshEnabled() &&
+ OwnerElement().GetLayoutObject()) {
+ // Invalidate paint to ensure that the focus ring is shown.
+ OwnerElement().GetLayoutObject()->SetShouldDoFullPaintInvalidation();
+ }
}
void PickerIndicatorElement::OpenPopup() {
- if (chooser_)
+ if (HasOpenedPopup())
return;
if (!GetDocument().GetPage())
return;
@@ -123,6 +130,11 @@ void PickerIndicatorElement::OpenPopup() {
return;
chooser_ = GetDocument().GetPage()->GetChromeClient().OpenDateTimeChooser(
GetDocument().GetFrame(), this, parameters);
+ if (::features::IsFormControlsRefreshEnabled() &&
+ OwnerElement().GetLayoutObject()) {
+ // Invalidate paint to ensure that the focus ring is removed.
+ OwnerElement().GetLayoutObject()->SetShouldDoFullPaintInvalidation();
+ }
}
Element& PickerIndicatorElement::OwnerElement() const {
@@ -136,6 +148,10 @@ void PickerIndicatorElement::ClosePopup() {
chooser_->EndChooser();
}
+bool PickerIndicatorElement::HasOpenedPopup() const {
+ return chooser_;
+}
+
void PickerIndicatorElement::DetachLayoutTree(bool performing_reattach) {
ClosePopup();
HTMLDivElement::DetachLayoutTree(performing_reattach);
@@ -160,7 +176,8 @@ void PickerIndicatorElement::DidNotifySubtreeInsertionsToDocument() {
return;
// Don't make this focusable if we are in web tests in order to avoid
// breaking existing tests.
- // FIXME: We should have a way to disable accessibility in web tests.
+ // TODO(crbug.com/1054048): We should have a way to disable accessibility in
+ // web tests. Once we do have it, this early return should be removed.
if (WebTestSupport::IsRunningWebTest())
return;
setAttribute(html_names::kTabindexAttr, "0");
@@ -168,7 +185,8 @@ void PickerIndicatorElement::DidNotifySubtreeInsertionsToDocument() {
setAttribute(html_names::kRoleAttr, "button");
setAttribute(
html_names::kAriaLabelAttr,
- AtomicString(GetLocale().QueryString(IDS_AX_CALENDAR_SHOW_DATE_PICKER)));
+ AtomicString(
+ this->picker_indicator_owner_->AriaRoleForPickerIndicator()));
}
void PickerIndicatorElement::Trace(Visitor* visitor) {
diff --git a/chromium/third_party/blink/renderer/core/html/forms/picker_indicator_element.h b/chromium/third_party/blink/renderer/core/html/forms/picker_indicator_element.h
index df33806c889..4aa8b3237f8 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/picker_indicator_element.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/picker_indicator_element.h
@@ -37,8 +37,6 @@
namespace blink {
-class HTMLInputElement;
-
class PickerIndicatorElement final : public HTMLDivElement,
public DateTimeChooserClient {
USING_GARBAGE_COLLECTED_MIXIN(PickerIndicatorElement);
@@ -55,6 +53,8 @@ class PickerIndicatorElement final : public HTMLDivElement,
virtual void PickerIndicatorChooseValue(double) = 0;
virtual Element& PickerOwnerElement() const = 0;
virtual bool SetupDateTimeChooserParameters(DateTimeChooserParameters&) = 0;
+ virtual void DidEndChooser() = 0;
+ virtual String AriaRoleForPickerIndicator() const = 0;
};
PickerIndicatorElement(Document&, PickerIndicatorOwner&);
@@ -63,6 +63,7 @@ class PickerIndicatorElement final : public HTMLDivElement,
void OpenPopup();
void ClosePopup();
+ bool HasOpenedPopup() const;
bool WillRespondToMouseClickEvents() override;
void RemovePickerIndicatorOwner() { picker_indicator_owner_ = nullptr; }
AXObject* PopupRootAXObject() const;
@@ -81,17 +82,16 @@ class PickerIndicatorElement final : public HTMLDivElement,
InsertionNotificationRequest InsertedInto(ContainerNode&) override;
void DidNotifySubtreeInsertionsToDocument() override;
- HTMLInputElement* HostInput();
-
Member<PickerIndicatorOwner> picker_indicator_owner_;
Member<DateTimeChooser> chooser_;
};
-DEFINE_TYPE_CASTS(PickerIndicatorElement,
- Element,
- element,
- element->IsPickerIndicatorElement(),
- element.IsPickerIndicatorElement());
+template <>
+struct DowncastTraits<PickerIndicatorElement> {
+ static bool AllowFrom(const Element& element) {
+ return element.IsPickerIndicatorElement();
+ }
+};
} // namespace blink
#endif
diff --git a/chromium/third_party/blink/renderer/core/html/forms/radio_button_group_scope.cc b/chromium/third_party/blink/renderer/core/html/forms/radio_button_group_scope.cc
index 08db5400d8b..9191f0d96a3 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/radio_button_group_scope.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/radio_button_group_scope.cc
@@ -201,8 +201,6 @@ void RadioButtonGroup::Trace(Visitor* visitor) {
// RadioButtonGroup in the header.
RadioButtonGroupScope::RadioButtonGroupScope() = default;
-RadioButtonGroupScope::~RadioButtonGroupScope() = default;
-
void RadioButtonGroupScope::AddButton(HTMLInputElement* element) {
DCHECK_EQ(element->type(), input_type_names::kRadio);
if (element->GetName().IsEmpty())
diff --git a/chromium/third_party/blink/renderer/core/html/forms/radio_button_group_scope.h b/chromium/third_party/blink/renderer/core/html/forms/radio_button_group_scope.h
index 11399c9d004..a6ba8d9f217 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/radio_button_group_scope.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/radio_button_group_scope.h
@@ -36,7 +36,6 @@ class RadioButtonGroupScope {
public:
RadioButtonGroupScope();
- ~RadioButtonGroupScope();
void Trace(Visitor*);
void AddButton(HTMLInputElement*);
void UpdateCheckedState(HTMLInputElement*);
diff --git a/chromium/third_party/blink/renderer/core/html/forms/radio_input_type.cc b/chromium/third_party/blink/renderer/core/html/forms/radio_input_type.cc
index 5b69b334e22..c3846016db6 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/radio_input_type.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/radio_input_type.cc
@@ -21,6 +21,7 @@
#include "third_party/blink/renderer/core/html/forms/radio_input_type.h"
+#include "third_party/blink/public/mojom/input/focus_type.mojom-blink.h"
#include "third_party/blink/public/strings/grit/blink_strings.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/element_traversal.h"
@@ -56,8 +57,33 @@ const AtomicString& RadioInputType::FormControlType() const {
}
bool RadioInputType::ValueMissing(const String&) const {
- return GetElement().IsInRequiredRadioButtonGroup() &&
- !GetElement().CheckedRadioButtonForGroup();
+ HTMLInputElement& input = GetElement();
+ if (auto* scope = input.GetRadioButtonGroupScope())
+ return scope->IsInRequiredGroup(&input) && !CheckedRadioButtonForGroup();
+
+ // This element is not managed by a RadioButtonGroupScope. We need to traverse
+ // the tree from TreeRoot.
+ DCHECK(!input.isConnected());
+ DCHECK(!input.formOwner());
+ const AtomicString& name = input.GetName();
+ if (name.IsEmpty())
+ return false;
+ bool is_required = false;
+ bool is_checked = false;
+ Node& root = input.TreeRoot();
+ for (auto* another = Traversal<HTMLInputElement>::InclusiveFirstWithin(root);
+ another; another = Traversal<HTMLInputElement>::Next(*another, &root)) {
+ if (another->type() != input_type_names::kRadio ||
+ another->GetName() != name || another->formOwner())
+ continue;
+ if (another->checked())
+ is_checked = true;
+ if (another->FastHasAttribute(html_names::kRequiredAttr))
+ is_required = true;
+ if (is_checked && is_required)
+ return false;
+ }
+ return is_required && !is_checked;
}
String RadioInputType::ValueMissingText() const {
@@ -110,7 +136,7 @@ void RadioInputType::HandleKeydownEvent(KeyboardEvent& event) {
: (key == "ArrowDown" || key == "ArrowRight");
// Force layout for isFocusable() in findNextFocusableRadioButtonInGroup().
- document.UpdateStyleAndLayout();
+ document.UpdateStyleAndLayout(DocumentUpdateReason::kInput);
// We can only stay within the form's children if the form hasn't been demoted
// to a leaf because of malformed HTML.
@@ -128,9 +154,9 @@ void RadioInputType::HandleKeydownEvent(KeyboardEvent& event) {
}
}
if (input_element) {
- document.SetFocusedElement(input_element,
- FocusParams(SelectionBehaviorOnFocus::kRestore,
- kWebFocusTypeNone, nullptr));
+ document.SetFocusedElement(
+ input_element, FocusParams(SelectionBehaviorOnFocus::kRestore,
+ mojom::blink::FocusType::kNone, nullptr));
input_element->DispatchSimulatedClick(&event, kSendNoEvents);
event.SetDefaultHandled();
return;
@@ -175,7 +201,7 @@ bool RadioInputType::IsKeyboardFocusable() const {
// Allow keyboard focus if we're checked or if nothing in the group is
// checked.
- return GetElement().checked() || !GetElement().CheckedRadioButtonForGroup();
+ return GetElement().checked() || !CheckedRadioButtonForGroup();
}
bool RadioInputType::ShouldSendChangeEventAfterCheckedChanged() {
@@ -197,7 +223,7 @@ ClickHandlingState* RadioInputType::WillDispatchClick() {
ClickHandlingState* state = MakeGarbageCollected<ClickHandlingState>();
state->checked = GetElement().checked();
- state->checked_radio_button = GetElement().CheckedRadioButtonForGroup();
+ state->checked_radio_button = CheckedRadioButtonForGroup();
GetElement().setChecked(true, TextFieldEventBehavior::kDispatchChangeEvent);
is_in_click_handler_ = true;
return state;
@@ -225,7 +251,7 @@ void RadioInputType::DidDispatchClick(Event& event,
}
bool RadioInputType::ShouldAppearIndeterminate() const {
- return !GetElement().CheckedRadioButtonForGroup();
+ return !CheckedRadioButtonForGroup();
}
HTMLInputElement* RadioInputType::NextRadioButtonInGroup(
@@ -247,4 +273,42 @@ HTMLInputElement* RadioInputType::NextRadioButtonInGroup(
return nullptr;
}
+HTMLInputElement* RadioInputType::CheckedRadioButtonForGroup() const {
+ HTMLInputElement& input = GetElement();
+ if (input.checked())
+ return &input;
+ if (auto* scope = input.GetRadioButtonGroupScope())
+ return scope->CheckedButtonForGroup(input.GetName());
+
+ // This element is not managed by a RadioButtonGroupScope. We need to traverse
+ // the tree from TreeRoot.
+ DCHECK(!input.isConnected());
+ DCHECK(!input.formOwner());
+ const AtomicString& name = input.GetName();
+ if (name.IsEmpty())
+ return nullptr;
+ Node& root = input.TreeRoot();
+ for (auto* another = Traversal<HTMLInputElement>::InclusiveFirstWithin(root);
+ another; another = Traversal<HTMLInputElement>::Next(*another, &root)) {
+ if (another->type() != input_type_names::kRadio ||
+ another->GetName() != name || another->formOwner())
+ continue;
+ if (another->checked())
+ return another;
+ }
+ return nullptr;
+}
+
+void RadioInputType::WillUpdateCheckedness(bool new_checked) {
+ if (!new_checked)
+ return;
+ if (GetElement().GetRadioButtonGroupScope()) {
+ // Buttons in RadioButtonGroupScope are handled in
+ // HTMLInputElement::setChecked().
+ return;
+ }
+ if (auto* input = CheckedRadioButtonForGroup())
+ input->setChecked(false);
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/radio_input_type.h b/chromium/third_party/blink/renderer/core/html/forms/radio_input_type.h
index 5c0b43d43cb..f3050550000 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/radio_input_type.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/radio_input_type.h
@@ -46,6 +46,7 @@ class RadioInputType final : public BaseCheckableInputType {
private:
void CountUsage() override;
const AtomicString& FormControlType() const override;
+ void WillUpdateCheckedness(bool new_checked) override;
bool ValueMissing(const String&) const override;
String ValueMissingText() const override;
void HandleClickEvent(MouseEvent&) override;
@@ -59,6 +60,7 @@ class RadioInputType final : public BaseCheckableInputType {
HTMLInputElement* FindNextFocusableRadioButtonInGroup(HTMLInputElement*,
bool);
+ HTMLInputElement* CheckedRadioButtonForGroup() const;
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/range_input_type.cc b/chromium/third_party/blink/renderer/core/html/forms/range_input_type.cc
index fe1eb05d751..0eed4825627 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/range_input_type.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/range_input_type.cc
@@ -144,8 +144,8 @@ StepRange RangeInputType::CreateStepRange(
// minimum/maximum.
// https://html.spec.whatwg.org/C/#range-state-(type=range):concept-input-min-default
const bool kHasRangeLimitations = true;
- return StepRange(step_base, minimum, maximum, kHasRangeLimitations, step,
- step_description);
+ return StepRange(step_base, minimum, maximum, kHasRangeLimitations,
+ /*has_reversed_range=*/false, step, step_description);
}
bool RangeInputType::IsSteppable() const {
@@ -185,7 +185,7 @@ void RangeInputType::HandleKeydownEvent(KeyboardEvent& event) {
// FIXME: We can't use stepUp() for the step value "any". So, we increase
// or decrease the value by 1/100 of the value range. Is it reasonable?
const Decimal step =
- DeprecatedEqualIgnoringCase(
+ EqualIgnoringASCIICase(
GetElement().FastGetAttribute(html_names::kStepAttr), "any")
? (step_range.Maximum() - step_range.Minimum()) / 100
: step_range.Step();
@@ -253,6 +253,10 @@ void RangeInputType::CreateShadowSubtree() {
GetElement().UserAgentShadowRoot()->AppendChild(container);
}
+bool RangeInputType::TypeShouldForceLegacyLayout() const {
+ return true;
+}
+
LayoutObject* RangeInputType::CreateLayoutObject(const ComputedStyle&,
LegacyLayout) const {
return new LayoutSlider(&GetElement());
@@ -314,10 +318,7 @@ void RangeInputType::WarnIfValueIsInvalid(const String& value) const {
if (value.IsEmpty() || !GetElement().SanitizeValue(value).IsEmpty())
return;
AddWarningToConsole(
- "The specified value %s is not a valid number. The value must match to "
- "the following regular expression: "
- "-?(\\d+|\\d+\\.\\d+|\\.\\d+)([eE][-+]?\\d+)?",
- value);
+ "The specified value %s cannot be parsed, or is out of range.", value);
}
void RangeInputType::DisabledAttributeChanged() {
@@ -419,4 +420,8 @@ void RangeInputType::ValueAttributeChanged() {
UpdateView();
}
+bool RangeInputType::IsDraggedSlider() const {
+ return GetSliderThumbElement()->IsActive();
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/range_input_type.h b/chromium/third_party/blink/renderer/core/html/forms/range_input_type.h
index b4da9bce5a0..013379890dc 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/range_input_type.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/range_input_type.h
@@ -63,6 +63,7 @@ class RangeInputType final : public InputType, public InputTypeView {
bool IsSteppable() const override;
void HandleMouseDownEvent(MouseEvent&) override;
void HandleKeydownEvent(KeyboardEvent&) override;
+ bool TypeShouldForceLegacyLayout() const override;
LayoutObject* CreateLayoutObject(const ComputedStyle&,
LegacyLayout) const override;
void CreateShadowSubtree() override;
@@ -86,6 +87,7 @@ class RangeInputType final : public InputType, public InputTypeView {
// InputTypeView function:
void UpdateView() override;
void ValueAttributeChanged() override;
+ bool IsDraggedSlider() const override;
bool tick_mark_values_dirty_;
Vector<Decimal> tick_mark_values_;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/resources/PRESUBMIT.py b/chromium/third_party/blink/renderer/core/html/forms/resources/PRESUBMIT.py
index f28465828fa..26b84f5fb5d 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/resources/PRESUBMIT.py
+++ b/chromium/third_party/blink/renderer/core/html/forms/resources/PRESUBMIT.py
@@ -2,13 +2,15 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+
def _CheckChangeOnUploadOrCommit(input_api, output_api):
- return input_api.canned_checks.CheckPatchFormatted(input_api, output_api,
- check_js=True)
+ return input_api.canned_checks.CheckPatchFormatted(
+ input_api, output_api, check_js=True)
+
def CheckChangeOnUpload(input_api, output_api):
- return _CheckChangeOnUploadOrCommit(input_api, output_api)
+ return _CheckChangeOnUploadOrCommit(input_api, output_api)
def CheckChangeOnCommit(input_api, output_api):
- return _CheckChangeOnUploadOrCommit(input_api, output_api)
+ return _CheckChangeOnUploadOrCommit(input_api, output_api)
diff --git a/chromium/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js b/chromium/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js
index d89a990d557..6380d4631f4 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js
+++ b/chromium/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js
@@ -59,6 +59,7 @@ var global = {
],
isLocaleRTL: false,
isFormControlsRefreshEnabled: false,
+ isBorderTransparent: false,
mode: 'date',
isAMPMFirst: false,
hasAMPM: false,
@@ -304,6 +305,40 @@ Day.prototype.next = function(offset) {
};
/**
+ * Given that 'this' is the Nth day of the month, returns the Nth
+ * day of the month that is specified by the parameter.
+ * Clips the date if necessary, e.g. if 'this' Day is October 31st and
+ * the parameter is a November, returns November 30th.
+ * @param {!Month} month
+ * @return {!Day}
+ */
+Day.prototype.thisRangeInMonth = function(month) {
+ var newDate = month.startDate();
+ var originalMonthInt = newDate.getUTCMonth();
+ newDate.setUTCDate(this.date);
+ if (newDate.getUTCMonth() != originalMonthInt) {
+ newDate.setUTCDate(0);
+ }
+ return Day.createFromDate(newDate);
+};
+
+/**
+ * @param {!Month} month
+ * @return {!boolean}
+ */
+Day.prototype.overlapsMonth = function(month) {
+ return (month.firstDay() <= this && month.lastDay() >= this);
+};
+
+/**
+ * @param {!Month} month
+ * @return {!boolean}
+ */
+Day.prototype.isFullyContainedInMonth = function(month) {
+ return (month.firstDay() <= this && month.lastDay() >= this);
+};
+
+/**
* @return {!Date}
*/
Day.prototype.startDate = function() {
@@ -569,6 +604,65 @@ Week.prototype.next = function(offset) {
};
/**
+ * Given that 'this' is the Nth week of the month, returns
+ * the Week that is the Nth week in the month specified
+ * by the parameter.
+ * Clips the date if necessary, e.g. if 'this' is the 5th week
+ * of a month that has 5 weeks and the parameter month only has
+ * 4 weeks, returns the 4th week of that month.
+ * @param {!Month} month
+ * @return {!Week}
+ */
+Week.prototype.thisRangeInMonth = function(month) {
+ var firstDateInCurrentMonth = this.startDate();
+ firstDateInCurrentMonth.setUTCDate(1);
+
+ var offsetInOriginalMonth =
+ Week._numberOfWeeksSinceDate(firstDateInCurrentMonth, this.startDate());
+
+ // Determine the first Monday in the new month (the week control shows weeks
+ // starting on Monday).
+ var firstWeekStartInNewMonth = month.startDate();
+ firstWeekStartInNewMonth.setUTCDate(
+ 1 +
+ ((DaysPerWeek + 1 - firstWeekStartInNewMonth.getUTCDay()) % DaysPerWeek));
+
+
+ // Find the Nth Monday in the month where N == offsetInOriginalMonth.
+ firstWeekStartInNewMonth.setUTCDate(
+ firstWeekStartInNewMonth.getUTCDate() +
+ (DaysPerWeek * offsetInOriginalMonth));
+
+ if (firstWeekStartInNewMonth.getUTCMonth() != month.month) {
+ // If we overshot into the next month (can happen if we were
+ // on the 5th week of the old month), go back to the last week
+ // of the target month.
+ firstWeekStartInNewMonth.setUTCDate(
+ firstWeekStartInNewMonth.getUTCDate() - DaysPerWeek);
+ }
+
+ return Week.createFromDate(firstWeekStartInNewMonth);
+};
+
+/**
+ * @param {!Month} month
+ * @return {!boolean}
+ */
+Week.prototype.overlapsMonth = function(month) {
+ return (
+ month.firstDay() <= this.lastDay() && month.lastDay() >= this.firstDay());
+};
+
+/**
+ * @param {!Month} month
+ * @return {!boolean}
+ */
+Week.prototype.isFullyContainedInMonth = function(month) {
+ return (
+ month.firstDay() <= this.firstDay() && month.lastDay() >= this.lastDay());
+};
+
+/**
* @return {!Date}
*/
Week.prototype.startDate = function() {
@@ -706,13 +800,6 @@ Month.createFromToday = function() {
};
/**
- * @return {!boolean}
- */
-Month.prototype.containsDay = function(day) {
- return this.year === day.year && this.month === day.month;
-};
-
-/**
* @param {!Month} other
* @return {!boolean}
*/
@@ -768,7 +855,7 @@ Month.prototype.firstDay = function() {
* @return {!Day}
*/
Month.prototype.middleDay = function() {
- return new Day(this.year, this.month, this.month === 2 ? 14 : 15);
+ return new Day(this.year, this.month, this.month === 1 ? 14 : 15);
};
/**
@@ -1666,10 +1753,17 @@ ListCell.prototype.setSelected = function(selected) {
if (this._selected === selected)
return;
this._selected = selected;
- if (this._selected)
+ if (this._selected) {
this.element.classList.add('selected');
- else
+ if (global.params.isFormControlsRefreshEnabled) {
+ this.element.setAttribute('aria-selected', true);
+ }
+ } else {
this.element.classList.remove('selected');
+ if (global.params.isFormControlsRefreshEnabled) {
+ this.element.setAttribute('aria-selected', false);
+ }
+ }
};
/**
@@ -1782,7 +1876,18 @@ ListView.prototype.addCellIfNecessary = function(row) {
if (cell)
return cell;
cell = this.prepareNewCell(row);
- cell.attachTo(this.scrollView.contentElement);
+
+ // Ensure that the DOM tree positions of the rows are in increasing
+ // chronological order. This is needed for correct application of
+ // the :hover selector for the week control, which spans across multiple
+ // calendar rows.
+ var rowIndices = Object.keys(this._cells);
+ var shouldPrepend = (rowIndices.length) > 0 && (row < rowIndices[0]);
+ cell.attachTo(
+ this.scrollView.contentElement,
+ shouldPrepend ? this.scrollView.contentElement.firstElementChild :
+ undefined);
+
cell.setWidth(this._width);
cell.setPosition(this.scrollView.contentPositionForContentOffset(
this.scrollOffsetForRow(row)));
@@ -2160,6 +2265,133 @@ ScrubbyScrollBar.prototype.onScrollTimer = function() {
this.scrollView.scrollBy(scrollAmount, false);
};
+// Mixin containing utilities for identifying and navigating between
+// valid day/week/month ranges.
+var DateRangeManager = {
+ _setValidDateConfig(config) {
+ this.config = {};
+
+ this.config.minimum = (typeof config.min !== 'undefined' && config.min) ?
+ parseDateString(config.min) :
+ this._dateTypeConstructor.Minimum;
+ this.config.maximum = (typeof config.max !== 'undefined' && config.max) ?
+ parseDateString(config.max) :
+ this._dateTypeConstructor.Maximum;
+ this.config.minimumValue = this.config.minimum.valueOf();
+ this.config.maximumValue = this.config.maximum.valueOf();
+ this.config.step = (typeof config.step !== 'undefined') ?
+ Number(config.step) :
+ this._dateTypeConstructor.DefaultStep;
+ this.config.stepBase = (typeof config.stepBase !== 'undefined') ?
+ Number(config.stepBase) :
+ this._dateTypeConstructor.DefaultStepBase;
+ },
+
+ _isValidForStep(value) {
+ // nextAllowedValue is the time closest (looking forward) to value that is
+ // within the interval specified by the step and the stepBase. This may
+ // be equal to value.
+ var nextAllowedValue =
+ (Math.ceil((value - this.config.stepBase) / this.config.step) *
+ this.config.step) +
+ this.config.stepBase;
+ // If the nextAllowedValue is between value and the next nearest possible time
+ // for this control type (determined by adding the smallest time interval, given
+ // by DefaultStep, to value) then we consider it to be valid.
+ return nextAllowedValue < (value + this._dateTypeConstructor.DefaultStep);
+ },
+
+ /**
+ * @param {!number} value
+ * @return {!boolean}
+ */
+ _outOfRange(value) {
+ return value < this.config.minimumValue || value > this.config.maximumValue;
+ },
+
+ /**
+ * @param {!DateType} dayOrWeekOrMonth
+ * @return {!boolean}
+ */
+ isValid(dayOrWeekOrMonth) {
+ var value = dayOrWeekOrMonth.valueOf();
+ return dayOrWeekOrMonth instanceof this._dateTypeConstructor &&
+ !this._outOfRange(value) && this._isValidForStep(value);
+ },
+
+ /**
+ * @param {!DayOrWeekOrMonth} dayOrWeekOrMonth
+ * @return {?DayOrWeekOrMonth}
+ */
+ getNearestValidRangeLookingForward(dayOrWeekOrMonth) {
+ if (dayOrWeekOrMonth < this.config.minimumValue) {
+ // Performance optimization: avoid wasting lots of time in the below
+ // loop if dayOrWeekOrMonth is significantly less than the min.
+ dayOrWeekOrMonth =
+ this._dateTypeConstructor.createFromValue(this.config.minimumValue);
+ }
+
+ while (!this.isValid(dayOrWeekOrMonth) &&
+ dayOrWeekOrMonth < this.config.maximumValue) {
+ dayOrWeekOrMonth = dayOrWeekOrMonth.next();
+ }
+
+ return this.isValid(dayOrWeekOrMonth) ? dayOrWeekOrMonth : null;
+ },
+
+ /**
+ * @param {!DayOrWeekOrMonth} dayOrWeekOrMonth
+ * @return {?DayOrWeekOrMonth}
+ */
+ getNearestValidRangeLookingBackward(dayOrWeekOrMonth) {
+ if (dayOrWeekOrMonth > this.config.maximumValue) {
+ // Performance optimization: avoid wasting lots of time in the below
+ // loop if dayOrWeekOrMonth is significantly greater than the max.
+ dayOrWeekOrMonth =
+ this._dateTypeConstructor.createFromValue(this.config.maximumValue);
+ }
+
+ while (!this.isValid(dayOrWeekOrMonth) &&
+ dayOrWeekOrMonth > this.config.minimumValue) {
+ dayOrWeekOrMonth = dayOrWeekOrMonth.previous();
+ }
+
+ return this.isValid(dayOrWeekOrMonth) ? dayOrWeekOrMonth : null;
+ },
+
+ /**
+ * @param {!DayOrWeekOrMonth} dayOrWeekOrMonth
+ * @param {!boolean} lookForwardFirst
+ * @return {?DayOrWeekOrMonth}
+ */
+ getNearestValidRange(dayOrWeekOrMonth, lookForwardFirst) {
+ var result = null;
+ if (lookForwardFirst) {
+ if (!(result =
+ this.getNearestValidRangeLookingForward(dayOrWeekOrMonth))) {
+ result = this.getNearestValidRangeLookingBackward(dayOrWeekOrMonth);
+ }
+ } else {
+ if (!(result =
+ this.getNearestValidRangeLookingBackward(dayOrWeekOrMonth))) {
+ result = this.getNearestValidRangeLookingForward(dayOrWeekOrMonth);
+ }
+ }
+
+ return result;
+ },
+
+ /**
+ * @param {!Day} day
+ * @param {!boolean} lookForwardFirst
+ * @return {?DayOrWeekOrMonth}
+ */
+ getValidRangeNearestToDay(day, lookForwardFirst) {
+ var dayOrWeekOrMonth = this._dateTypeConstructor.createFromDay(day);
+ return this.getNearestValidRange(dayOrWeekOrMonth, lookForwardFirst);
+ }
+};
+
/**
* @constructor
* @extends ListCell
@@ -2294,14 +2526,16 @@ YearListCell.prototype.setHeight = function(height) {
* @param {!Month} minimumMonth
* @param {!Month} maximumMonth
*/
-function YearListView(minimumMonth, maximumMonth) {
+function YearListView(minimumMonth, maximumMonth, config) {
ListView.call(this);
this.element.classList.add('year-list-view');
/**
* @type {?Month}
*/
- this.highlightedMonth = null;
+ if (!global.params.isFormControlsRefreshEnabled) {
+ this.highlightedMonth = null;
+ }
/**
* @type {?Month}
*/
@@ -2350,13 +2584,50 @@ function YearListView(minimumMonth, maximumMonth) {
this.scrubbyScrollBar = new ScrubbyScrollBar(this.scrollView);
this.scrubbyScrollBar.attachTo(this);
- this.element.addEventListener('mouseover', this.onMouseOver, false);
- this.element.addEventListener('mouseout', this.onMouseOut, false);
this.element.addEventListener('keydown', this.onKeyDown, false);
- this.element.addEventListener('touchstart', this.onTouchStart, false);
+ if (!global.params.isFormControlsRefreshEnabled) {
+ this.element.addEventListener('mouseover', this.onMouseOver, false);
+ this.element.addEventListener('mouseout', this.onMouseOut, false);
+ this.element.addEventListener('touchstart', this.onTouchStart, false);
+ }
+
+ if (global.params.isFormControlsRefreshEnabled && config &&
+ config.mode == 'month') {
+ this.type = 'month';
+ this._dateTypeConstructor = Month;
+
+ this._setValidDateConfig(config);
+
+ this._hadValidValueWhenOpened = false;
+ var initialSelection = parseDateString(config.currentValue);
+ if (initialSelection) {
+ this._hadValidValueWhenOpened = this.isValid(initialSelection);
+ this._selectedMonth = this.getNearestValidRange(
+ initialSelection, /*lookForwardFirst*/ true);
+ } else {
+ // Ensure that the next month closest to today is selected to start with so that
+ // the user can simply submit the popup to choose it.
+ this._selectedMonth = this.getValidRangeNearestToDay(
+ this._dateTypeConstructor.createFromToday(),
+ /*lookForwardFirst*/ true);
+ }
+
+ this._initialSelectedMonth = this._selectedMonth;
+ } else if (global.params.isFormControlsRefreshEnabled) {
+ // This is a month switcher menu embedded in another calendar control.
+ // Set up our config so that getNearestValidRangeLookingForward(Backward)
+ // when called on this YearListView will navigate by month.
+ this.config = {};
+ this.config.minimumValue = minimumMonth;
+ this.config.maximumValue = maximumMonth;
+ this.config.step = Month.DefaultStep;
+ this.config.stepBase = Month.DefaultStepBase;
+ this._dateTypeConstructor = Month;
+ }
}
YearListView.prototype = Object.create(ListView.prototype);
+Object.assign(YearListView.prototype, DateRangeManager);
YearListView._Height = YearListCell._SelectedHeight - 1;
YearListView._VisibleYearsRefresh = 4;
@@ -2504,9 +2775,11 @@ YearListView.prototype.onClick = function(event) {
if (this.selectedRow !== oldSelectedRow) {
// Always start with first month when changing the year.
const month = new Month(year, 0);
- this.highlightMonth(month);
- this.dispatchEvent(
- YearListView.EventTypeYearListViewDidSelectMonth, this, month);
+ if (!global.params.isFormControlsRefreshEnabled) {
+ this.highlightMonth(month);
+ this.dispatchEvent(
+ YearListView.EventTypeYearListViewDidSelectMonth, this, month);
+ }
this.scrollView.scrollTo(this.selectedRow * YearListCell.GetHeight(), true);
} else {
var monthButton = enclosingNodeOrSelfWithClass(
@@ -2517,7 +2790,9 @@ YearListView.prototype.onClick = function(event) {
this.dispatchEvent(
YearListView.EventTypeYearListViewDidSelectMonth, this,
new Month(year, month));
- this.hide();
+ if (!global.params.isFormControlsRefreshEnabled) {
+ this.hide();
+ }
}
};
@@ -2588,13 +2863,20 @@ YearListView.prototype.prepareNewCell = function(row) {
for (var i = 0; i < cell.monthButtons.length; ++i) {
var month = new Month(row + 1, i);
cell.monthButtons[i].id = month.toString();
- cell.monthButtons[i].setAttribute(
- 'aria-disabled',
- this._minimumMonth > month || this._maximumMonth < month ? 'true' :
- 'false');
+ if (global.params.isFormControlsRefreshEnabled && this.type === 'month') {
+ cell.monthButtons[i].setAttribute(
+ 'aria-disabled', this.isValid(month) ? 'false' : 'true');
+ } else {
+ cell.monthButtons[i].setAttribute(
+ 'aria-disabled',
+ this._minimumMonth > month || this._maximumMonth < month ? 'true' :
+ 'false');
+ }
cell.monthButtons[i].setAttribute('aria-label', month.toLocaleString());
+ cell.monthButtons[i].setAttribute('aria-selected', false);
}
- if (this.highlightedMonth && row === this.highlightedMonth.year - 1) {
+ if (!global.params.isFormControlsRefreshEnabled && this.highlightedMonth &&
+ row === this.highlightedMonth.year - 1) {
var monthButton = cell.monthButtons[this.highlightedMonth.month];
monthButton.classList.add(YearListCell.ClassNameHighlighted);
// aria-activedescendant assumes both elements have layoutObjects, and
@@ -2607,6 +2889,10 @@ YearListView.prototype.prepareNewCell = function(row) {
if (this._selectedMonth && (this._selectedMonth.year - 1) === row) {
var monthButton = cell.monthButtons[this._selectedMonth.month];
monthButton.classList.add(YearListCell.ClassNameSelected);
+ if (global.params.isFormControlsRefreshEnabled) {
+ this.element.setAttribute('aria-activedescendant', monthButton.id);
+ monthButton.setAttribute('aria-selected', true);
+ }
}
const todayMonth = Month.createFromToday();
if ((todayMonth.year - 1) === row) {
@@ -2691,8 +2977,10 @@ YearListView.prototype.select = function(row) {
this.selectedRow, YearListView.RowAnimationDirection.Opening);
if (selectedCell)
selectedCell.setSelected(true);
- var month = this.highlightedMonth ? this.highlightedMonth.month : 0;
- this.highlightMonth(new Month(this.selectedRow + 1, month));
+ if (!global.params.isFormControlsRefreshEnabled) {
+ var month = this.highlightedMonth ? this.highlightedMonth.month : 0;
+ this.highlightMonth(new Month(this.selectedRow + 1, month));
+ }
}
this.setNeedsUpdateCells(true);
};
@@ -2713,8 +3001,6 @@ YearListView.prototype.selectWithoutAnimating = function(row) {
selectedCell.setSelected(true);
selectedCell.setHeight(YearListCell.GetSelectedHeight());
}
- var month = this.highlightedMonth ? this.highlightedMonth.month : 0;
- this.highlightMonth(new Month(this.selectedRow + 1, month));
}
this.setNeedsUpdateCells(true);
};
@@ -2762,7 +3048,29 @@ YearListView.prototype.highlightMonth = function(month) {
};
YearListView.prototype.setSelectedMonth = function(month) {
+
+ var oldMonthButton = this.buttonForMonth(this._selectedMonth);
+ if (oldMonthButton) {
+ oldMonthButton.classList.remove(YearListCell.ClassNameSelected);
+ oldMonthButton.setAttribute('aria-selected', false);
+ }
+
this._selectedMonth = month;
+
+ var newMonthButton = this.buttonForMonth(this._selectedMonth);
+ if (newMonthButton) {
+ newMonthButton.classList.add(YearListCell.ClassNameSelected);
+ this.element.setAttribute('aria-activedescendant', newMonthButton.id);
+ newMonthButton.setAttribute('aria-selected', true);
+ }
+};
+
+YearListView.prototype.setSelectedMonthAndUpdateView = function(month) {
+ this.setSelectedMonth(month);
+
+ this.select(this._selectedMonth.year - 1);
+
+ this.scrollView.scrollTo(this.selectedRow * YearListCell.GetHeight(), true);
};
YearListView.prototype.showSelectedMonth = function() {
@@ -2780,7 +3088,9 @@ YearListView.prototype.show = function(month) {
this.scrollToRow(month.year - 1, false);
this.selectWithoutAnimating(month.year - 1);
- this.highlightMonth(month);
+ if (!global.params.isFormControlsRefreshEnabled) {
+ this.highlightMonth(month);
+ }
this.showSelectedMonth();
};
@@ -2795,8 +3105,11 @@ YearListView.prototype._moveHighlightTo = function(month) {
this.highlightMonth(month);
this.select(this.highlightedMonth.year - 1);
- this.dispatchEvent(
- YearListView.EventTypeYearListViewDidSelectMonth, this, month);
+ if (!global.params.isFormControlsRefreshEnabled) {
+ this.dispatchEvent(
+ YearListView.EventTypeYearListViewDidSelectMonth, this, month);
+ }
+
this.scrollView.scrollTo(this.selectedRow * YearListCell.GetHeight(), true);
return true;
};
@@ -2807,9 +3120,66 @@ YearListView.prototype._moveHighlightTo = function(month) {
YearListView.prototype.onKeyDown = function(event) {
var key = event.key;
var eventHandled = false;
- if (key == 't')
- eventHandled = this._moveHighlightTo(Month.createFromToday());
- else if (this.highlightedMonth) {
+ if (key == 't') {
+ if (!global.params.isFormControlsRefreshEnabled) {
+ eventHandled = this._moveHighlightTo(Month.createFromToday());
+ if (global.params.isFormControlsRefreshEnabled) {
+ this.dispatchEvent(
+ YearListView.EventTypeYearListViewDidSelectMonth, this,
+ this.highlightedMonth);
+ }
+ }
+ } else if (
+ global.params.isFormControlsRefreshEnabled && this._selectedMonth) {
+ if (global.params.isLocaleRTL ? key == 'ArrowRight' : key == 'ArrowLeft') {
+ var newSelection = this.getNearestValidRangeLookingBackward(
+ this._selectedMonth.previous());
+ if (newSelection) {
+ this.setSelectedMonthAndUpdateView(newSelection);
+ }
+ } else if (key == 'ArrowUp') {
+ var newSelection = this.getNearestValidRangeLookingBackward(
+ this._selectedMonth.previous(YearListCell.ButtonColumns));
+ if (newSelection) {
+ this.setSelectedMonthAndUpdateView(newSelection);
+ }
+ } else if (
+ global.params.isLocaleRTL ? key == 'ArrowLeft' : key == 'ArrowRight') {
+ var newSelection =
+ this.getNearestValidRangeLookingForward(this._selectedMonth.next());
+ if (newSelection) {
+ this.setSelectedMonthAndUpdateView(newSelection);
+ }
+ } else if (key == 'ArrowDown') {
+ var newSelection = this.getNearestValidRangeLookingForward(
+ this._selectedMonth.next(YearListCell.ButtonColumns));
+ if (newSelection) {
+ this.setSelectedMonthAndUpdateView(newSelection);
+ }
+ } else if (key == 'PageUp') {
+ var newSelection = this.getNearestValidRangeLookingBackward(
+ this._selectedMonth.previous(MonthsPerYear));
+ if (newSelection) {
+ this.setSelectedMonthAndUpdateView(newSelection);
+ }
+ } else if (key == 'PageDown') {
+ var newSelection = this.getNearestValidRangeLookingForward(
+ this._selectedMonth.next(MonthsPerYear));
+ if (newSelection) {
+ this.setSelectedMonthAndUpdateView(newSelection);
+ }
+ } else if (this.type !== 'month') {
+ if (key == 'Enter') {
+ this.dispatchEvent(
+ YearListView.EventTypeYearListViewDidSelectMonth, this,
+ this._selectedMonth);
+ } else if (key == 'Escape') {
+ this.hide();
+ eventHandled = true;
+ }
+ }
+ } else if (
+ !global.params.isFormControlsRefreshEnabled && this.highlightedMonth) {
if (global.params.isLocaleRTL ? key == 'ArrowRight' : key == 'ArrowLeft')
eventHandled = this._moveHighlightTo(this.highlightedMonth.previous());
else if (key == 'ArrowUp')
@@ -2884,7 +3254,15 @@ MonthPopupView.ClassNameMonthPopupView = 'month-popup-view';
MonthPopupView.prototype.show = function(initialMonth, calendarTableRect) {
this.isVisible = true;
- document.body.appendChild(this.element);
+ if (global.params.isFormControlsRefreshEnabled &&
+ global.params.mode == 'datetime-local') {
+ // Place the month popup under the datetimelocal-picker element so that the
+ // datetimelocal-picker element receives its keyboard and click events.
+ // For other calendar control types, these events are handled via the body element.
+ document.querySelector('datetimelocal-picker').appendChild(this.element);
+ } else {
+ document.body.appendChild(this.element);
+ }
this.yearListView.setWidth(calendarTableRect.width - 2);
this.yearListView.setHeight(YearListView.GetHeight());
if (global.params.isLocaleRTL)
@@ -3194,12 +3572,17 @@ CalendarHeaderView.prototype = Object.create(View.prototype);
CalendarHeaderView.Height = 24;
CalendarHeaderView.BottomMargin = 10;
+CalendarHeaderView.ClassNameCalendarNavigationButtonIconRefresh =
+ 'today-button-icon-refresh';
CalendarHeaderView._ForwardTriangle =
'<svg width=\'4\' height=\'7\'><polygon points=\'0,7 0,0, 4,3.5\' style=\'fill:#6e6e6e;\' /></svg>';
-CalendarHeaderView._ForwardTriangleRefresh =
- '<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\
- <path d=\"M15.3516 8.60156L8 15.9531L0.648438 8.60156L1.35156 7.89844L7.5 14.0469V0H8.5V14.0469L14.6484 7.89844L15.3516 8.60156Z\" fill=\"#101010\"/>\
- </svg>'
+CalendarHeaderView._ForwardTriangleRefresh = `<svg class="${
+ CalendarHeaderView
+ .ClassNameCalendarNavigationButtonIconRefresh}" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <path class="${
+ CalendarHeaderView
+ .ClassNameCalendarNavigationButtonIconRefresh}" d="M15.3516 8.60156L8 15.9531L0.648438 8.60156L1.35156 7.89844L7.5 14.0469V0H8.5V14.0469L14.6484 7.89844L15.3516 8.60156Z" fill="#101010"/>
+ </svg>`;
CalendarHeaderView.GetForwardTriangle = function() {
if (global.params.isFormControlsRefreshEnabled) {
return CalendarHeaderView._ForwardTriangleRefresh;
@@ -3208,10 +3591,13 @@ CalendarHeaderView.GetForwardTriangle = function() {
};
CalendarHeaderView._BackwardTriangle =
'<svg width=\'4\' height=\'7\'><polygon points=\'0,3.5 4,7 4,0\' style=\'fill:#6e6e6e;\' /></svg>';
-CalendarHeaderView._BackwardTriangleRefresh =
- '<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\
- <path d=\"M14.6484 8.10156L8.5 1.95312V16H7.5V1.95312L1.35156 8.10156L0.648438 7.39844L8 0.046875L15.3516 7.39844L14.6484 8.10156Z\" fill=\"#101010\"/>\
- </svg>'
+CalendarHeaderView._BackwardTriangleRefresh = `<svg class="${
+ CalendarHeaderView
+ .ClassNameCalendarNavigationButtonIconRefresh}" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <path class="${
+ CalendarHeaderView
+ .ClassNameCalendarNavigationButtonIconRefresh}" d="M14.6484 8.10156L8.5 1.95312V16H7.5V1.95312L1.35156 8.10156L0.648438 7.39844L8 0.046875L15.3516 7.39844L14.6484 8.10156Z" fill="#101010"/>
+ </svg>`;
CalendarHeaderView.GetBackwardTriangle = function() {
if (global.params.isFormControlsRefreshEnabled) {
return CalendarHeaderView._BackwardTriangleRefresh;
@@ -3240,15 +3626,17 @@ CalendarHeaderView.prototype.onCurrentMonthChanged = function() {
};
CalendarHeaderView.prototype.onNavigationButtonClick = function(sender) {
- if (sender === this._previousMonthButton)
+ if (sender === this._previousMonthButton) {
this.calendarPicker.setCurrentMonth(
this.calendarPicker.currentMonth().previous(),
CalendarPicker.NavigationBehavior.WithAnimation);
- else if (sender === this._nextMonthButton)
+ this.calendarPicker.ensureSelectionIsWithinCurrentMonth();
+ } else if (sender === this._nextMonthButton) {
this.calendarPicker.setCurrentMonth(
this.calendarPicker.currentMonth().next(),
CalendarPicker.NavigationBehavior.WithAnimation);
- else
+ this.calendarPicker.ensureSelectionIsWithinCurrentMonth();
+ } else
this.calendarPicker.selectRangeContainingDay(Day.createFromToday());
};
@@ -3536,6 +3924,7 @@ CalendarTableHeaderView.GetHeight = function() {
*/
function CalendarRowCell() {
ListCell.call(this);
+
this.element.classList.add(CalendarRowCell.ClassNameCalendarRowCell);
this.element.style.height = CalendarRowCell.GetHeight() + 'px';
this.element.setAttribute('role', 'row');
@@ -3678,8 +4067,10 @@ function CalendarTableView(calendarPicker) {
this._ignoreMouseOutUntillNextMouseOver = false;
this.element.addEventListener('click', this.onClick, false);
- this.element.addEventListener('mouseover', this.onMouseOver, false);
- this.element.addEventListener('mouseout', this.onMouseOut, false);
+ if (!global.params.isFormControlsRefreshEnabled) {
+ this.element.addEventListener('mouseover', this.onMouseOver, false);
+ this.element.addEventListener('mouseout', this.onMouseOut, false);
+ }
// You shouldn't be able to use the mouse wheel to scroll.
this.scrollView.element.removeEventListener(
@@ -3891,16 +4282,23 @@ CalendarTableView.prototype.updateCells = function() {
var dayCell = this._dayCells[dayString];
var day = dayCell.day;
dayCell.setIsToday(Day.createFromToday().equals(day));
- dayCell.setSelected(
- day >= firstDayInSelection && day <= lastDayInSelection);
- var isHighlighted = day >= firstDayInHighlight && day <= lastDayInHighlight;
- dayCell.setHighlighted(isHighlighted);
- if (isHighlighted) {
- if (firstDayInHighlight == lastDayInHighlight)
- activeCell = dayCell;
- else if (
- this.calendarPicker.type == 'month' && day == firstDayInHighlight)
+ var isSelected = (day >= firstDayInSelection && day <= lastDayInSelection);
+ dayCell.setSelected(isSelected);
+ if (global.params.isFormControlsRefreshEnabled) {
+ if (isSelected && firstDayInSelection == lastDayInSelection) {
activeCell = dayCell;
+ }
+ } else {
+ var isHighlighted =
+ day >= firstDayInHighlight && day <= lastDayInHighlight;
+ dayCell.setHighlighted(isHighlighted);
+ if (isHighlighted) {
+ if (firstDayInHighlight == lastDayInHighlight)
+ activeCell = dayCell;
+ else if (
+ this.calendarPicker.type == 'month' && day == firstDayInHighlight)
+ activeCell = dayCell;
+ }
}
dayCell.setIsInCurrentMonth(
day >= firstDayInCurrentMonth && day <= lastDayInCurrentMonth);
@@ -3910,11 +4308,18 @@ CalendarTableView.prototype.updateCells = function() {
for (var weekString in this._weekNumberCells) {
var weekNumberCell = this._weekNumberCells[weekString];
var week = weekNumberCell.week;
- var isWeekHighlighted = highlight && highlight.equals(week);
- weekNumberCell.setSelected(selection && selection.equals(week));
- weekNumberCell.setHighlighted(isWeekHighlighted);
- if (isWeekHighlighted)
- activeCell = weekNumberCell;
+ var isSelected = (selection && selection.equals(week));
+ weekNumberCell.setSelected(isSelected);
+ if (global.params.isFormControlsRefreshEnabled) {
+ if (isSelected) {
+ activeCell = weekNumberCell;
+ }
+ } else {
+ var isWeekHighlighted = highlight && highlight.equals(week);
+ weekNumberCell.setHighlighted(isWeekHighlighted);
+ if (isWeekHighlighted)
+ activeCell = weekNumberCell;
+ }
weekNumberCell.setDisabled(!this.calendarPicker.isValid(week));
}
}
@@ -3976,7 +4381,9 @@ CalendarTableView.prototype.throwAwayWeekNumberCell = function(weekNumberCell) {
function CalendarPicker(type, config) {
View.call(this, createElement('div', CalendarPicker.ClassNameCalendarPicker));
this.element.classList.add(CalendarPicker.ClassNamePreparing);
-
+ if (global.params.isBorderTransparent) {
+ this.element.style.borderColor = 'transparent';
+ }
/**
* @type {!string}
* @const
@@ -3988,12 +4395,13 @@ function CalendarPicker(type, config) {
this._dateTypeConstructor = Month;
else
this._dateTypeConstructor = Day;
- /**
- * @type {!Object}
- * @const
- */
- this.config = {};
- this._setConfig(config);
+
+ this._setValidDateConfig(config);
+
+ if (global.params.isFormControlsRefreshEnabled && this.type === 'week') {
+ this.element.classList.add(CalendarPicker.ClassNameWeekPicker);
+ }
+
/**
* @type {!Month}
* @const
@@ -4042,15 +4450,26 @@ function CalendarPicker(type, config) {
* @protected
*/
this._selection = null;
+
/**
* @type {?DateType}
* @protected
*/
- this._highlight = null;
+ if (!global.params.isFormControlsRefreshEnabled) {
+ this._highlight = null;
+ }
+
this.calendarTableView.element.addEventListener(
- 'keydown', this.onCalendarTableKeyDown, false);
+ 'keydown',
+ global.params.isFormControlsRefreshEnabled ?
+ this.onCalendarTableKeyDownRefresh :
+ this.onCalendarTableKeyDown,
+ false);
+
+ document.body.addEventListener('click', this.onBodyClick, false);
document.body.addEventListener('keydown', this.onBodyKeyDown, false);
+
window.addEventListener('resize', this.onWindowResize, false);
/**
@@ -4059,22 +4478,48 @@ function CalendarPicker(type, config) {
*/
this._height = -1;
+ this._hadValidValueWhenOpened = false;
+
var initialSelection = parseDateString(config.currentValue);
if (initialSelection) {
this.setCurrentMonth(
Month.createFromDay(initialSelection.middleDay()),
CalendarPicker.NavigationBehavior.None);
- this.setSelection(initialSelection);
- } else
+
+ if (global.params.isFormControlsRefreshEnabled) {
+ this._hadValidValueWhenOpened = this.isValid(initialSelection);
+ this.setSelection(this.getNearestValidRange(
+ initialSelection, /*lookForwardFirst*/ true));
+ } else {
+ this.setSelection(initialSelection);
+ }
+ } else {
this.setCurrentMonth(
Month.createFromToday(), CalendarPicker.NavigationBehavior.None);
+
+ if (global.params.isFormControlsRefreshEnabled) {
+ // Ensure that the next date closest to today is selected to start with so that
+ // the user can simply submit the popup to choose it.
+ this.setSelection(this.getValidRangeNearestToDay(
+ this._dateTypeConstructor.createFromToday(),
+ /*lookForwardFirst*/ true));
+ }
+ }
+
+ /**
+ * @type {?DateType}
+ * @protected
+ */
+ this._initialSelection = this._selection;
}
CalendarPicker.prototype = Object.create(View.prototype);
+Object.assign(CalendarPicker.prototype, DateRangeManager);
CalendarPicker.Padding = 10;
CalendarPicker.BorderWidth = 1;
CalendarPicker.ClassNameCalendarPicker = 'calendar-picker';
+CalendarPicker.ClassNameWeekPicker = 'week-picker';
CalendarPicker.ClassNamePreparing = 'preparing';
CalendarPicker.EventTypeCurrentMonthChanged = 'currentMonthChanged';
CalendarPicker.commitDelayMs = 100;
@@ -4088,6 +4533,10 @@ CalendarPicker.prototype.onWindowResize = function(event) {
window.removeEventListener('resize', this.onWindowResize, false);
};
+CalendarPicker.prototype.resetToInitialValue = function() {
+ this.setSelection(this._initialSelection);
+};
+
/**
* @param {!YearListView} sender
*/
@@ -4096,6 +4545,7 @@ CalendarPicker.prototype.onYearListViewDidHide = function(sender) {
this.calendarHeaderView.setDisabled(false);
if (global.params.isFormControlsRefreshEnabled) {
this.calendarTableView.element.style.visibility = 'visible';
+ this.calendarTableView.element.focus();
} else {
this.adjustHeight();
}
@@ -4108,6 +4558,11 @@ CalendarPicker.prototype.onYearListViewDidHide = function(sender) {
CalendarPicker.prototype.onYearListViewDidSelectMonth = function(
sender, month) {
this.setCurrentMonth(month, CalendarPicker.NavigationBehavior.None);
+
+ if (global.params.isFormControlsRefreshEnabled) {
+ this.ensureSelectionIsWithinCurrentMonth();
+ this.onYearListViewDidHide();
+ }
};
/**
@@ -4146,23 +4601,6 @@ CalendarPicker.prototype.onMonthPopupButtonClick = function(sender) {
}
};
-CalendarPicker.prototype._setConfig = function(config) {
- this.config.minimum = (typeof config.min !== 'undefined' && config.min) ?
- parseDateString(config.min) :
- this._dateTypeConstructor.Minimum;
- this.config.maximum = (typeof config.max !== 'undefined' && config.max) ?
- parseDateString(config.max) :
- this._dateTypeConstructor.Maximum;
- this.config.minimumValue = this.config.minimum.valueOf();
- this.config.maximumValue = this.config.maximum.valueOf();
- this.config.step = (typeof config.step !== undefined) ?
- Number(config.step) :
- this._dateTypeConstructor.DefaultStep;
- this.config.stepBase = (typeof config.stepBase !== 'undefined') ?
- Number(config.stepBase) :
- this._dateTypeConstructor.DefaultStepBase;
-};
-
/**
* @return {!Month}
*/
@@ -4289,6 +4727,13 @@ CalendarPicker.prototype.setSelection = function(dayOrWeekOrMonth) {
return;
if (this._selection && this._selection.equals(dayOrWeekOrMonth))
return;
+ if (this._selection && !dayOrWeekOrMonth) {
+ this._selection = null;
+ if (!global.params.isFormControlsRefreshEnabled) {
+ this._setHighlight(null);
+ }
+ return;
+ }
var firstDayInSelection = dayOrWeekOrMonth.firstDay();
var lastDayInSelection = dayOrWeekOrMonth.lastDay();
var candidateCurrentMonth = Month.createFromDay(firstDayInSelection);
@@ -4320,7 +4765,9 @@ CalendarPicker.prototype.setSelection = function(dayOrWeekOrMonth) {
candidateCurrentMonth,
CalendarPicker.NavigationBehavior.WithAnimation);
}
- this._setHighlight(dayOrWeekOrMonth);
+ if (!global.params.isFormControlsRefreshEnabled) {
+ this._setHighlight(dayOrWeekOrMonth);
+ }
if (!this.isValid(dayOrWeekOrMonth))
return;
this._selection = dayOrWeekOrMonth;
@@ -4377,41 +4824,84 @@ CalendarPicker.prototype._setHighlight = function(dayOrWeekOrMonth) {
};
/**
- * @param {!number} value
+ * @param {!Day} day
* @return {!boolean}
*/
-CalendarPicker.prototype._stepMismatch = function(value) {
- var nextAllowedValue =
- Math.ceil((value - this.config.stepBase) / this.config.step) *
- this.config.step +
- this.config.stepBase;
- return nextAllowedValue >= value + this._dateTypeConstructor.DefaultStep;
+CalendarPicker.prototype.isValidDay = function(day) {
+ return this.isValid(this._dateTypeConstructor.createFromDay(day));
};
/**
- * @param {!number} value
- * @return {!boolean}
+ * If the selection is not inside the month currently shown in the control,
+ * adjust the selection so that it is within the current month.
+ * The new selection value is determined in the following manner:
+ * 1) If the old selection is on the Nth day of the month, try to place it
+ * on the Nth day of the new month.
+ * 2) If the Nth day of the new month is not valid, choose the closest
+ * valid date that is within the new month.
+ * 3) If the next and previous valid date are equidistant and both within
+ * the new month, arbitrarily choose the older date.
*/
-CalendarPicker.prototype._outOfRange = function(value) {
- return value < this.config.minimumValue || value > this.config.maximumValue;
-};
+CalendarPicker.prototype.ensureSelectionIsWithinCurrentMonth = function() {
+ if (!this._selection)
+ return;
+ if (this._selection.isFullyContainedInMonth(this.currentMonth()))
+ return;
-/**
- * @param {!DateType} dayOrWeekOrMonth
- * @return {!boolean}
- */
-CalendarPicker.prototype.isValid = function(dayOrWeekOrMonth) {
- var value = dayOrWeekOrMonth.valueOf();
- return dayOrWeekOrMonth instanceof this._dateTypeConstructor &&
- !this._outOfRange(value) && !this._stepMismatch(value);
-};
+ var newSelection = null;
+ var currentRangeInNewMonth =
+ this._selection.thisRangeInMonth(this.currentMonth());
-/**
- * @param {!Day} day
- * @return {!boolean}
- */
-CalendarPicker.prototype.isValidDay = function(day) {
- return this.isValid(this._dateTypeConstructor.createFromDay(day));
+ if (this.isValid(currentRangeInNewMonth)) {
+ newSelection = currentRangeInNewMonth;
+ } else {
+ var validRangeLookingBackward =
+ this.getNearestValidRangeLookingBackward(currentRangeInNewMonth);
+ var validRangeLookingForward =
+ this.getNearestValidRangeLookingForward(currentRangeInNewMonth);
+ if (validRangeLookingBackward && validRangeLookingForward) {
+ var newMonthIsForwardOfSelection =
+ (currentRangeInNewMonth.firstDay() > this._selection.firstDay());
+ var [validRangeInDirectionOfAdvancement, validRangeAgainstDirectionOfAdvancement] =
+ newMonthIsForwardOfSelection ?
+ [validRangeLookingForward, validRangeLookingBackward] :
+ [validRangeLookingBackward, validRangeLookingForward];
+
+ if (!validRangeAgainstDirectionOfAdvancement.overlapsMonth(
+ this.currentMonth())) {
+ // If the range going against our direction of movement is not
+ // entirely within the new month, go with the range in the
+ // other direction to ensure we that we don't backtrack.
+ newSelection = validRangeInDirectionOfAdvancement;
+ } else if (!validRangeInDirectionOfAdvancement.overlapsMonth(
+ this.currentMonth())) {
+ newSelection = validRangeAgainstDirectionOfAdvancement;
+ } else {
+ // If both of the ranges are in the new month, select the closest one
+ // to the target date in the new month.
+ var diffFromForwardRange = Math.abs(
+ currentRangeInNewMonth.valueOf() -
+ validRangeLookingForward.valueOf());
+ var diffFromBackwardRange = Math.abs(
+ currentRangeInNewMonth.valueOf() -
+ validRangeLookingBackward.valueOf());
+ if (diffFromForwardRange < diffFromBackwardRange) {
+ newSelection = validRangeLookingForward;
+ } else { // In a tie, arbitrarily choose older date
+ newSelection = validRangeLookingBackward;
+ }
+ }
+ } else if (!validRangeLookingForward) {
+ newSelection = validRangeLookingBackward;
+ } else { // !validRangeLookingBackward
+ newSelection = validRangeLookingForward;
+ } // No additional clause because they can't both be null; we have a
+ // selection so there's at least one valid date.
+ }
+
+ if (newSelection) {
+ this.setSelection(newSelection);
+ }
};
/**
@@ -4435,9 +4925,79 @@ CalendarPicker.prototype._moveHighlight = function(dateRange) {
/**
* @param {?Event} event
*/
+CalendarPicker.prototype.onCalendarTableKeyDownRefresh = function(event) {
+ var key = event.key;
+ var offset = 0;
+
+ if (!event.target.matches('.today-button-refresh') && this._selection) {
+ switch (key) {
+ case 'PageUp':
+ var previousMonth = this.currentMonth().previous();
+ if (previousMonth && previousMonth >= this.config.minimumValue) {
+ this.setCurrentMonth(
+ previousMonth, CalendarPicker.NavigationBehavior.WithAnimation);
+ this.ensureSelectionIsWithinCurrentMonth();
+ }
+ break;
+ case 'PageDown':
+ var nextMonth = this.currentMonth().next();
+ if (nextMonth && nextMonth >= this.config.minimumValue) {
+ this.setCurrentMonth(
+ nextMonth, CalendarPicker.NavigationBehavior.WithAnimation);
+ this.ensureSelectionIsWithinCurrentMonth();
+ }
+ break;
+ case 'ArrowUp':
+ case 'ArrowDown':
+ case 'ArrowLeft':
+ case 'ArrowRight':
+ var upOrDownArrowStepSize =
+ this.type === 'date' || this.type === 'datetime-local' ?
+ DaysPerWeek :
+ 1;
+ if (global.params.isLocaleRTL ? key == 'ArrowRight' :
+ key == 'ArrowLeft') {
+ var newSelection = this.getNearestValidRangeLookingBackward(
+ this._selection.previous());
+ if (newSelection) {
+ this.setSelection(newSelection);
+ }
+ } else if (key == 'ArrowUp') {
+ var newSelection = this.getNearestValidRangeLookingBackward(
+ this._selection.previous(upOrDownArrowStepSize));
+ if (newSelection) {
+ this.setSelection(newSelection);
+ }
+ } else if (
+ global.params.isLocaleRTL ? key == 'ArrowLeft' :
+ key == 'ArrowRight') {
+ var newSelection =
+ this.getNearestValidRangeLookingForward(this._selection.next());
+ if (newSelection) {
+ this.setSelection(newSelection);
+ }
+ } else if (key == 'ArrowDown') {
+ var newSelection = this.getNearestValidRangeLookingForward(
+ this._selection.next(upOrDownArrowStepSize));
+ if (newSelection) {
+ this.setSelection(newSelection);
+ }
+ }
+ break;
+ };
+ }
+ // else if there is no selection it must be the case that there are no
+ // valid values (because min >= max). Otherwise we would have set the selection
+ // during initialization. In this case there's nothing to do.
+};
+
+/**
+ * @param {?Event} event
+ */
CalendarPicker.prototype.onCalendarTableKeyDown = function(event) {
var key = event.key;
var eventHandled = false;
+
if (key == 't') {
this.selectRangeContainingDay(Day.createFromToday());
eventHandled = true;
@@ -4456,19 +5016,20 @@ CalendarPicker.prototype.onCalendarTableKeyDown = function(event) {
eventHandled = true;
}
} else if (this._highlight) {
+ var upOrDownArrowStepSize =
+ this.type === 'date' || this.type === 'datetime-local' ? DaysPerWeek :
+ 1;
if (global.params.isLocaleRTL ? key == 'ArrowRight' : key == 'ArrowLeft') {
eventHandled = this._moveHighlight(this._highlight.previous());
} else if (key == 'ArrowUp') {
- eventHandled = this._moveHighlight(this._highlight.previous(
- this.type === 'date' || this.type === 'datetime-local' ? DaysPerWeek :
- 1));
+ eventHandled =
+ this._moveHighlight(this._highlight.previous(upOrDownArrowStepSize));
} else if (
global.params.isLocaleRTL ? key == 'ArrowLeft' : key == 'ArrowRight') {
eventHandled = this._moveHighlight(this._highlight.next());
} else if (key == 'ArrowDown') {
- eventHandled = this._moveHighlight(this._highlight.next(
- this.type === 'date' || this.type === 'datetime-local' ? DaysPerWeek :
- 1));
+ eventHandled =
+ this._moveHighlight(this._highlight.next(upOrDownArrowStepSize));
} else if (key == 'Enter') {
this.setSelectionAndCommit(this._highlight);
}
@@ -4520,42 +5081,115 @@ CalendarPicker.prototype.setHeight = function(height) {
/**
* @param {?Event} event
*/
+CalendarPicker.prototype.onBodyClick = function(event) {
+ if (global.params.isFormControlsRefreshEnabled &&
+ this.type !== 'datetime-local') {
+ if (event.target.matches(
+ '.calendar-navigation-button, .today-button-icon-refresh, .month-button')) {
+ window.pagePopupController.setValue(this.getSelectedValue());
+ }
+ }
+};
+
+/**
+ * @param {?Event} event
+ */
CalendarPicker.prototype.onBodyKeyDown = function(event) {
var key = event.key;
var eventHandled = false;
var offset = 0;
switch (key) {
case 'Escape':
- window.pagePopupController.closePopup();
- eventHandled = true;
+ // The datetime-local control handles submission/cancellation at
+ // the top level, so if we're in a datetime-local let event bubble
+ // up instead of handling it here.
+ if (global.params.isFormControlsRefreshEnabled) {
+ if (this.type !== 'datetime-local') {
+ if (!this._selection ||
+ (this._selection.equals(this._initialSelection))) {
+ window.pagePopupController.closePopup();
+ } else {
+ this.resetToInitialValue();
+ window.pagePopupController.setValue(
+ this._hadValidValueWhenOpened ?
+ this._initialSelection.toString() :
+ '');
+ }
+ }
+ } else {
+ window.pagePopupController.closePopup();
+ eventHandled = true;
+ }
+ break;
+ case 'ArrowUp':
+ case 'ArrowDown':
+ case 'ArrowLeft':
+ case 'ArrowRight':
+ case 'PageUp':
+ case 'PageDown':
+ if (global.params.isFormControlsRefreshEnabled &&
+ this.type !== 'datetime-local' &&
+ event.target.matches('.calendar-table-view') && this._selection) {
+ window.pagePopupController.setValue(this.getSelectedValue());
+ }
+ break;
+ case 'Enter':
+ // Submit the popup for an Enter keypress except when the user is
+ // hitting Enter to activate the month switcher button, Today button,
+ // or previous/next month arrows.
+ if (global.params.isFormControlsRefreshEnabled &&
+ this.type !== 'datetime-local') {
+ if (!event.target.matches(
+ '.calendar-navigation-button, .month-popup-button, .year-list-view')) {
+ if (this._selection) {
+ window.pagePopupController.setValueAndClosePopup(
+ 0, this.getSelectedValue());
+ } else {
+ // If there is no selection it must be the case that there are no
+ // valid values (because min >= max). There's nothing useful to do
+ // with the popup in this case so just close on Enter.
+ window.pagePopupController.closePopup();
+ }
+ } else if (event.target.matches(
+ '.calendar-navigation-button, .year-list-view')) {
+ // Navigating with the previous/next arrows may change selection,
+ // so push this change to the in-page control but don't
+ // close the popup.
+ window.pagePopupController.setValue(this.getSelectedValue());
+ }
+ }
break;
case 'm':
case 'M':
- offset = offset || 1; // Fall-through.
+ offset = offset || 1;
+ // Fall-through.
case 'y':
case 'Y':
- offset = offset || MonthsPerYear; // Fall-through.
+ offset = offset || MonthsPerYear;
+ // Fall-through.
case 'd':
case 'D':
- offset = offset || MonthsPerYear * 10;
- var oldFirstVisibleRow =
- this.calendarTableView
- .columnAndRowForDay(this.currentMonth().firstDay())
- .row;
- this.setCurrentMonth(
- event.shiftKey ? this.currentMonth().previous(offset) :
- this.currentMonth().next(offset),
- CalendarPicker.NavigationBehavior.WithAnimation);
- var newFirstVisibleRow =
- this.calendarTableView
- .columnAndRowForDay(this.currentMonth().firstDay())
- .row;
- if (this._highlight) {
- var highlightMiddleDay = this._highlight.middleDay();
- this.highlightRangeContainingDay(highlightMiddleDay.next(
- (newFirstVisibleRow - oldFirstVisibleRow) * DaysPerWeek));
+ if (!global.params.isFormControlsRefreshEnabled) {
+ offset = offset || MonthsPerYear * 10;
+ var oldFirstVisibleRow =
+ this.calendarTableView
+ .columnAndRowForDay(this.currentMonth().firstDay())
+ .row;
+ this.setCurrentMonth(
+ event.shiftKey ? this.currentMonth().previous(offset) :
+ this.currentMonth().next(offset),
+ CalendarPicker.NavigationBehavior.WithAnimation);
+ var newFirstVisibleRow =
+ this.calendarTableView
+ .columnAndRowForDay(this.currentMonth().firstDay())
+ .row;
+ if (this._highlight) {
+ var highlightMiddleDay = this._highlight.middleDay();
+ this.highlightRangeContainingDay(highlightMiddleDay.next(
+ (newFirstVisibleRow - oldFirstVisibleRow) * DaysPerWeek));
+ }
+ eventHandled = true;
}
- eventHandled = true;
break;
}
if (eventHandled) {
diff --git a/chromium/third_party/blink/renderer/core/html/forms/resources/calendar_picker_refresh.css b/chromium/third_party/blink/renderer/core/html/forms/resources/calendar_picker_refresh.css
index 7ba4b2ba739..69a6fc615e6 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/resources/calendar_picker_refresh.css
+++ b/chromium/third_party/blink/renderer/core/html/forms/resources/calendar_picker_refresh.css
@@ -4,7 +4,7 @@
*/
body {
- font: 12px sans-serif;
+ font-size: 12px;
}
.calendar-picker {
@@ -41,7 +41,7 @@ body {
}
.calendar-navigation-button:hover {
- background-color: #E5E5E5;
+ background-color: rgba(0, 117, 255, 0.3);
}
.calendar-navigation-button:disabled {
@@ -92,7 +92,7 @@ body {
border: 1px solid transparent !important;
border-radius: 2px;
color: #767676;
- padding: 1px;
+ padding: 1px !important;
text-align: center;
}
@@ -100,31 +100,61 @@ body {
color: #101010;
}
-.day-cell.highlighted,
-.month-button.highlighted,
-.week-number-cell.highlighted {
- background-color: #E5E5E5;
+.week-number-cell,
+.day-cell {
+ transition: color 0s;
+}
+
+/*
+* Highlight-when-hovered for cells in the month picker menu and standalone
+* month control
+*/
+.month-button:hover {
+ background-color: rgba(0, 117, 255, 0.3);
+}
+
+/*
+* Highlight-when-hovered for day cells except if this is a week picker
+*/
+:not(.week-picker) > .calendar-table-view > .scroll-view > .scroll-view-content
+ > .calendar-row-cell > .day-cell:not(.selected):hover {
+ background-color: rgba(0, 117, 255, 0.3);
+}
+
+/*
+* Highlight-when-hovered for week picker, in 3 parts:
+* 1. Highlight all cells in the hovered row except for Monday, because it
+* belongs to the previous week.
+* 2. Highlight Monday of the row after the hovered row, because it belongs to
+* this week
+* 3. Highlight the week number cell for the hovered week
+*/
+.week-picker .calendar-row-cell:hover
+ .day-cell:not(.selected):not(.disabled):not(:nth-child(2)),
+.week-picker .calendar-row-cell:hover + .calendar-row-cell
+ .day-cell:not(.selected):not(.disabled):nth-child(2),
+.calendar-row-cell:hover .week-number-cell:not(.selected):not(.disabled) {
+ background-color: rgba(0, 117, 255, 0.3);
}
.day-cell.selected,
.month-button.selected,
.week-number-cell.selected {
- background-color: #CECECE;
+ background-color: #0075FF;
+ color: #FFFFFF;
font-weight: bold;
}
-.day-cell.highlighted.today,
-.day-cell.today,
-.month-button.today {
- background-color: #6E6E6E;
- border: 0;
- color: #FFFFFF;
- font-weight: bold;
+.calendar-table-view:focus .day-cell.selected,
+.year-list-view:focus .month-button.selected,
+.calendar-table-view:focus .week-number-cell.selected {
+ outline: solid 2px -webkit-focus-ring-color;
+ outline-offset: -2px;
}
-.day-cell.selected.today,
-.month-button.selected.today {
- border: 2px solid #CECECE !important;
+.day-cell.today,
+.month-button.today {
+ border-color: #767676 !important;
}
.day-cell.disabled,
@@ -148,7 +178,7 @@ body {
}
.today-button-refresh:hover {
- background-color: #E5E5E5;
+ background-color: rgba(0, 117, 255, 0.3);
}
.today-button-refresh:disabled {
@@ -255,11 +285,39 @@ body {
color: WindowText;
}
- .day-cell.highlighted,
- .month-button.highlighted,
- .week-number-cell.highlighted {
- background-color: Window;
+ /*
+ * Highlight-when-hovered for cells in the month picker menu and standalone
+ * month control
+ */
+ .month-button:not(.selected):hover {
+ background-color: Window !important;
+ border-color: Highlight !important;
+ }
+
+ /*
+ * Highlight-when-hovered for day cells except if this is a week picker
+ */
+ :not(.week-picker) > .calendar-table-view > .scroll-view > .scroll-view-content
+ > .calendar-row-cell > .day-cell:not(.selected):hover {
+ background-color: Window !important;
+ border-color: Highlight !important;
+ }
+
+ /*
+ * Highlight-when-hovered for week picker, in 3 parts:
+ * 1. Highlight all cells in the hovered row except for Monday, because it
+ * belongs to the previous week.
+ * 2. Highlight Monday of the row after the hovered row, because it belongs to
+ * this week
+ * 3. Highlight the week number cell for the hovered week
+ */
+ .week-picker .calendar-row-cell:hover
+ .day-cell:not(.selected):not(.disabled):not(:nth-child(2)),
+ .week-picker .calendar-row-cell:hover + .calendar-row-cell
+ .day-cell:not(.selected):not(.disabled):nth-child(2),
+ .calendar-row-cell:hover .week-number-cell:not(.selected):not(.disabled) {
border-color: Highlight !important;
+ background-color: Window !important;
}
.day-cell.selected,
@@ -269,17 +327,15 @@ body {
color: Window;
}
- .day-cell.highlighted.today,
- .day-cell.today,
- .month-button.today {
- background-color: Highlight;
- border: 2px solid Window !important;
- color: Window;
+ .calendar-table-view:focus .day-cell.selected,
+ .year-list-view:focus .month-button.selected,
+ .calendar-table-view:focus .week-number-cell.selected {
+ outline: none;
}
- .day-cell.selected.today,
- .month-button.selected.today {
- border: 1px solid Window !important;
+ .day-cell.today,
+ .month-button.today {
+ border-color: WindowText !important;
}
.day-cell.disabled,
diff --git a/chromium/third_party/blink/renderer/core/html/forms/resources/colorSuggestionPicker.css b/chromium/third_party/blink/renderer/core/html/forms/resources/colorSuggestionPicker.css
index dfa50b795cb..5c7e7d331d9 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/resources/colorSuggestionPicker.css
+++ b/chromium/third_party/blink/renderer/core/html/forms/resources/colorSuggestionPicker.css
@@ -53,7 +53,7 @@ body.controls-refresh {
}
.controls-refresh .color-suggestion-picker-main {
- border: 0;
+ border: 1px solid #bfbfbf;
box-shadow: none;
padding: 8px 8px 4px 8px;
}
@@ -104,6 +104,11 @@ body.controls-refresh {
width: 4px;
}
+.controls-refresh .color-swatch:focus {
+ outline: solid 2px -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+
.other-color {
width: 100%;
margin: 4px 0 0 0;
@@ -114,7 +119,6 @@ body.controls-refresh {
border-color: transparent;
border-radius: 2px;
color: #000000;
- font-family: sans-serif;
font-size: 12px;
line-height: 16px;
margin: 0;
@@ -136,7 +140,7 @@ body.controls-refresh {
}
.controls-refresh .color-swatch:focus {
- border-color: Highlight;
+ outline-color: Highlight;
}
.controls-refresh .color-swatch-container {
diff --git a/chromium/third_party/blink/renderer/core/html/forms/resources/color_picker.css b/chromium/third_party/blink/renderer/core/html/forms/resources/color_picker.css
index ed9cc009f44..467b28893ee 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/resources/color_picker.css
+++ b/chromium/third_party/blink/renderer/core/html/forms/resources/color_picker.css
@@ -3,10 +3,6 @@
* found in the LICENSE file.
*/
-body {
- font-family: sans-serif;
-}
-
.color-picker-main {
border: 0;
padding: 0;
@@ -14,14 +10,15 @@ body {
color-picker {
background: #FFFFFF;
+ border: 1px solid #bfbfbf;
display: flex;
flex-direction: column;
- height: 304px;
+ height: 250px;
width: 232px;
}
visual-color-picker {
- height: 59%;
+ height: 71.5%;
min-height: 0;
}
@@ -48,12 +45,33 @@ hue-slider {
}
eye-dropper {
+ border-radius: 2px;
height: 32px;
margin-left: 2%;
position: relative;
width: 32px;
}
+eye-dropper.hidden {
+ visibility: hidden;
+}
+
+eye-dropper:not(.selected):hover {
+ background-color: #F7F7F7;
+}
+
+eye-dropper.selected {
+ background-color: #CECECE;
+}
+
+eye-dropper > svg {
+ height: 16px;
+ left: 25%;
+ position: absolute;
+ top: 25%;
+ width: 16px;
+}
+
color-viewer {
border: 1px solid rgba(0, 0, 0, 0.19);
border-radius: 50%;
@@ -70,7 +88,7 @@ color-well > canvas {
hue-slider > canvas {
border-radius: 2px;
- height: 11px;
+ height: 12px;
margin-top: 7%;
width: 100%;
}
@@ -83,6 +101,15 @@ color-selection-ring {
position: absolute;
}
+color-selection-ring:focus {
+ /* Simulate the outline using box-shadow because it follows the border radius
+ * (unlike outline).
+ */
+ box-shadow: 0px 0px 0px 2px;
+ color: -webkit-focus-ring-color;
+ outline: none;
+}
+
color-well > color-selection-ring {
height: 12px;
width: 12px;
@@ -126,7 +153,7 @@ input {
}
color-value-container > input:not(:first-child) {
- margin-left: 1%;
+ margin-left: 4%;
}
format-toggler {
@@ -167,31 +194,6 @@ channel-label {
width: 172px;
}
-submission-controls {
- align-items: center;
- border-top: 1px solid #CECECE;
- display: flex;
- flex-direction: row;
- height: 13%;
- min-height: 0;
-}
-
-#submission-controls-padding {
- height: 100%;
- width: 84%;
-}
-
-submission-button {
- padding: 3%;
- text-align: center;
- width: 8%;
-}
-
-submission-button:hover {
- background-color: #F3F3F3;
- border-radius: 2px;
-}
-
@media (forced-colors: active) {
color-viewer {
forced-color-adjust: none;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/resources/color_picker.js b/chromium/third_party/blink/renderer/core/html/forms/resources/color_picker.js
index a91dcc38d09..f7396a8596b 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/resources/color_picker.js
+++ b/chromium/third_party/blink/renderer/core/html/forms/resources/color_picker.js
@@ -424,15 +424,16 @@ class ColorPicker extends HTMLElement {
constructor(initialColor) {
super();
+ if (global.params.isBorderTransparent) {
+ this.style.borderColor = 'transparent';
+ }
+
this.selectedColor_ = initialColor;
+ this.colorWhenOpened_ = initialColor;
this.visualColorPicker_ = new VisualColorPicker(initialColor);
this.manualColorPicker_ = new ManualColorPicker(initialColor);
- this.submissionControls_ = new SubmissionControls(
- this.onSubmitButtonClick_, this.onCancelButtonClick_);
- this.append(
- this.visualColorPicker_, this.manualColorPicker_,
- this.submissionControls_);
+ this.append(this.visualColorPicker_, this.manualColorPicker_);
this.visualColorPicker_.addEventListener(
'visual-color-picker-initialized', this.initializeListeners_);
@@ -447,6 +448,8 @@ class ColorPicker extends HTMLElement {
this.addEventListener('format-change', this.updateFocusableElements_);
document.documentElement.addEventListener('keydown', this.onKeyDown_);
+
+ window.addEventListener('resize', this.onWindowResize_, {once: true});
}
get selectedColor() {
@@ -480,8 +483,11 @@ class ColorPicker extends HTMLElement {
this.processingManualColorChange_ = true;
this.visualColorPicker_.color = newColor;
this.processingManualColorChange_ = false;
+
+ const selectedValue = newColor.asHex();
+ window.pagePopupController.setValue(selectedValue);
}
- }
+ };
/**
* @param {!Event} event
@@ -492,24 +498,33 @@ class ColorPicker extends HTMLElement {
if (!this.processingManualColorChange_) {
this.selectedColor = newColor;
this.manualColorPicker_.color = newColor;
+
+ const selectedValue = newColor.asHex();
+ window.pagePopupController.setValue(selectedValue);
} else {
// We are making a visual color change in response to a manual color
// change. So we do not overwrite the manually specified values and do
// not change the selected color.
}
}
- }
+ };
/**
* @param {!Event} event
*/
onKeyDown_ = (event) => {
- switch(event.key) {
+ switch (event.key) {
case 'Enter':
- this.submissionControls_.submitButton.click();
+ window.pagePopupController.closePopup();
break;
case 'Escape':
- this.submissionControls_.cancelButton.click();
+ if (this.selectedColor.equals(this.colorWhenOpened_)) {
+ window.pagePopupController.closePopup();
+ } else {
+ this.manualColorPicker_.dispatchEvent(new CustomEvent(
+ 'manual-color-change',
+ {bubbles: true, detail: {color: this.colorWhenOpened_}}));
+ }
break;
case 'Tab':
event.preventDefault();
@@ -522,9 +537,8 @@ class ColorPicker extends HTMLElement {
this.focusableElements_.indexOf(document.activeElement);
let nextFocusIndex;
if (event.shiftKey) {
- nextFocusIndex = (currentFocusIndex > 0) ?
- currentFocusIndex - 1 :
- length - 1;
+ nextFocusIndex =
+ (currentFocusIndex > 0) ? currentFocusIndex - 1 : length - 1;
} else {
nextFocusIndex = (currentFocusIndex + 1) % length;
}
@@ -532,27 +546,20 @@ class ColorPicker extends HTMLElement {
}
break;
}
- }
+ };
updateFocusableElements_ = () => {
this.focusableElements_ = Array.from(this.querySelectorAll(
'color-value-container:not(.hidden-color-value-container) > input,' +
'[tabindex]:not([tabindex=\'-1\'])'));
- }
-
- static get COMMIT_DELAY_MS() {
- return 100;
- }
-
- onSubmitButtonClick_ = () => {
- const selectedValue = this.selectedColor_.asHex();
- window.setTimeout(function() {
- window.pagePopupController.setValueAndClosePopup(0, selectedValue);
- }, ColorPicker.COMMIT_DELAY_MS);
};
- onCancelButtonClick_ = () => {
- window.pagePopupController.closePopup();
+ onWindowResize_ = () => {
+ // Set focus on the first focusable element.
+ if (this.focusableElements_ === undefined) {
+ this.updateFocusableElements_();
+ }
+ this.focusableElements_[0].focus({preventScroll: true});
};
}
window.customElements.define('color-picker', ColorPicker);
@@ -602,6 +609,15 @@ class VisualColorPicker extends HTMLElement {
document.documentElement
.addEventListener('mousemove', this.onMouseMove_);
document.documentElement.addEventListener('mouseup', this.onMouseUp_);
+ this.colorWell_
+ .addEventListener('touchstart', this.onColorWellTouchStart_);
+ this.hueSlider_
+ .addEventListener('touchstart', this.onHueSliderTouchStart_);
+ document.documentElement
+ .addEventListener('touchstart', this.onTouchStart_);
+ document.documentElement
+ .addEventListener('touchmove', this.onTouchMove_);
+ document.documentElement.addEventListener('touchend', this.onTouchEnd_);
document.documentElement.addEventListener('keydown', this.onKeyDown_);
this.dispatchEvent(new CustomEvent('visual-color-picker-initialized'));
@@ -626,7 +642,7 @@ class VisualColorPicker extends HTMLElement {
event.preventDefault();
event.stopPropagation();
this.hueSlider_.focused = false;
- this.colorWell_.mouseDown(new Point(event.clientX, event.clientY));
+ this.colorWell_.pointerDown(new Point(event.clientX, event.clientY));
}
/**
@@ -636,7 +652,7 @@ class VisualColorPicker extends HTMLElement {
event.preventDefault();
event.stopPropagation();
this.colorWell_.focused = false;
- this.hueSlider_.mouseDown(new Point(event.clientX, event.clientY));
+ this.hueSlider_.pointerDown(new Point(event.clientX, event.clientY));
}
onMouseDown_ = () => {
@@ -649,13 +665,52 @@ class VisualColorPicker extends HTMLElement {
*/
onMouseMove_ = (event) => {
var point = new Point(event.clientX, event.clientY);
- this.colorWell_.mouseMove(point);
- this.hueSlider_.mouseMove(point);
+ this.colorWell_.pointerMove(point);
+ this.hueSlider_.pointerMove(point);
}
onMouseUp_ = () => {
- this.colorWell_.mouseUp();
- this.hueSlider_.mouseUp();
+ this.colorWell_.pointerUp();
+ this.hueSlider_.pointerUp();
+ }
+
+ /**
+ * @param {!Event} event
+ */
+ onColorWellTouchStart_ = (event) => {
+ event.preventDefault();
+ event.stopPropagation();
+ this.hueSlider_.focused = false;
+ this.colorWell_.pointerDown(new Point(event.touches[0].clientX, event.touches[0].clientY));
+ }
+
+ /**
+ * @param {!Event} event
+ */
+ onHueSliderTouchStart_ = (event) => {
+ event.preventDefault();
+ event.stopPropagation();
+ this.colorWell_.focused = false;
+ this.hueSlider_.pointerDown(new Point(event.touches[0].clientX, event.touches[0].clientY));
+ }
+
+ onTouchStart_ = () => {
+ this.colorWell_.focused = false;
+ this.hueSlider_.focused = false;
+ }
+
+ /**
+ * @param {!Event} event
+ */
+ onTouchMove_ = (event) => {
+ var point = new Point(event.touches[0].clientX, event.touches[0].clientY);
+ this.colorWell_.pointerMove(point);
+ this.hueSlider_.pointerMove(point);
+ }
+
+ onTouchEnd_ = () => {
+ this.colorWell_.pointerUp();
+ this.hueSlider_.pointerUp();
}
/**
@@ -700,7 +755,88 @@ window.customElements.define('visual-color-picker', VisualColorPicker);
* implementation.)
* TODO(http://crbug.com/992297): Implement eye dropper
*/
-class EyeDropper extends HTMLElement {}
+class EyeDropper extends HTMLElement {
+ constructor() {
+ super();
+
+ if (!global.params.isEyeDropperEnabled) {
+ this.classList.add('hidden');
+ return;
+ }
+
+ this.setAttribute('tabIndex', 0);
+ this.innerHTML =
+ '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" ' +
+ 'xmlns="http://www.w3.org/2000/svg"><path d="M13.7344 0C14.0469 0 ' +
+ '14.3411 0.0598958 14.6172 0.179688C14.8932 0.299479 15.1328 ' +
+ '0.460938 15.3359 0.664062C15.5391 0.867188 15.7005 1.10677 15.8203 ' +
+ '1.38281C15.9401 1.65885 16 1.95312 16 2.26562C16 2.56771 15.9427 ' +
+ '2.85938 15.8281 3.14062C15.7135 3.41667 15.5495 3.66146 15.3359 ' +
+ '3.875L13.4609 5.75C13.6328 5.91667 13.7656 6.10677 13.8594 ' +
+ '6.32031C13.9531 6.52865 14 6.75521 14 7C14 7.23958 13.9531 7.46354 ' +
+ '13.8594 7.67188C13.7708 7.88021 13.6432 8.06771 13.4766 ' +
+ '8.23438L12.25 9.46094L11 8.20312L4.71094 14.4922L4.50781 ' +
+ '14.5C4.24219 14.5104 4.01302 14.5547 3.82031 14.6328C3.63281 ' +
+ '14.7109 3.46615 14.8073 3.32031 14.9219C3.17969 15.0312 3.04948 ' +
+ '15.1484 2.92969 15.2734C2.8151 15.3984 2.69271 15.5156 2.5625 ' +
+ '15.625C2.43229 15.7344 2.28906 15.8255 2.13281 15.8984C1.97656 ' +
+ '15.9661 1.78646 16 1.5625 16C1.34896 16 1.14583 15.9583 0.953125 ' +
+ '15.875C0.765625 15.7917 0.601562 15.6797 0.460938 15.5391C0.320312 ' +
+ '15.3984 0.208333 15.2344 0.125 15.0469C0.0416667 14.8542 0 14.651 0 ' +
+ '14.4375C0 14.2135 0.0338542 14.0234 0.101562 13.8672C0.174479 ' +
+ '13.7057 0.265625 13.5625 0.375 13.4375C0.484375 13.3073 0.601562 ' +
+ '13.1849 0.726562 13.0703C0.851562 12.9505 0.96875 12.8203 1.07812 ' +
+ '12.6797C1.19271 12.5339 1.28906 12.3672 1.36719 12.1797C1.44531 ' +
+ '11.9922 1.48958 11.763 1.5 11.4922L1.50781 11.2891L7.79688 ' +
+ '5L6.53906 3.75L7.76562 2.52344C7.93229 2.35677 8.11979 2.22917 ' +
+ '8.32812 2.14062C8.53646 2.04688 8.76042 2 9 2C9.24479 2 9.47135 ' +
+ '2.04688 9.67969 2.14062C9.89323 2.23438 10.0833 2.36719 10.25 ' +
+ '2.53906L12.125 0.664062C12.3385 0.450521 12.5833 0.286458 12.8594 ' +
+ '0.171875C13.1406 0.0572917 13.4323 0 13.7344 0ZM10.2891 7.5L8.5 ' +
+ '5.71094L2.49219 11.7188C2.46615 11.9844 2.41667 12.2214 2.34375 ' +
+ '12.4297C2.27083 12.638 2.17708 12.8333 2.0625 13.0156C1.94792 ' +
+ '13.1927 1.8125 13.3646 1.65625 13.5312C1.50521 13.6927 1.34115 ' +
+ '13.8646 1.16406 14.0469C1.05469 14.1562 1 14.2891 1 14.4453C1 ' +
+ '14.5964 1.05469 14.7266 1.16406 14.8359C1.27344 14.9453 1.40365 15 ' +
+ '1.55469 15C1.71094 15 1.84375 14.9453 1.95312 14.8359C2.13542 ' +
+ '14.6589 2.3099 14.4948 2.47656 14.3438C2.64323 14.1875 2.8151 ' +
+ '14.0521 2.99219 13.9375C3.16927 13.8229 3.36198 13.7292 3.57031 ' +
+ '13.6562C3.77865 13.5833 4.01562 13.5339 4.28125 13.5078L10.2891 ' +
+ '7.5ZM14.625 3.16406C14.875 2.91406 15 2.61719 15 2.27344C15 2.10156 ' +
+ '14.9661 1.9375 14.8984 1.78125C14.8307 1.625 14.7396 1.48958 14.625 ' +
+ '1.375C14.5104 1.26042 14.375 1.16927 14.2188 1.10156C14.0625 ' +
+ '1.03385 13.8984 1 13.7266 1C13.3828 1 13.0859 1.125 12.8359 ' +
+ '1.375L10.25 3.95312L9.51562 3.21875C9.36979 3.07292 9.19792 3 9 ' +
+ '3C8.89062 3 8.78646 3.02604 8.6875 3.07812C8.59375 3.13021 8.5026 ' +
+ '3.19531 8.41406 3.27344C8.33073 3.35156 8.25 3.4349 8.17188 ' +
+ '3.52344C8.09375 3.60677 8.02083 3.68229 7.95312 3.75L12.25 ' +
+ '8.04688L12.7812 7.51562C12.9271 7.36979 13 7.19792 13 7C13 6.89583 ' +
+ '12.9792 6.80208 12.9375 6.71875C12.901 6.63021 12.8464 6.54948 ' +
+ '12.7734 6.47656L12.0469 5.75L14.625 3.16406Z" fill="WindowText"/> ' +
+ '</svg>';
+
+ this.addEventListener('click', this.onClick_);
+ this.addEventListener('keydown', this.onKeyDown_);
+ }
+
+ onClick_ = () => {
+ event.preventDefault();
+ event.stopPropagation();
+ this.classList.add('selected');
+ window.pagePopupController.openEyeDropper();
+ };
+
+ /**
+ * @param {!Event} event
+ */
+ onKeyDown_ = (event) => {
+ switch (event.key) {
+ case 'Enter':
+ this.onClick_();
+ break;
+ }
+ };
+}
window.customElements.define('eye-dropper', EyeDropper);
/**
@@ -766,7 +902,7 @@ class ColorSelectionArea extends HTMLElement {
/**
* @param {!Point} point
*/
- mouseDown(point) {
+ pointerDown(point) {
this.colorSelectionRing_.focus({preventScroll: true});
this.colorSelectionRing_.drag = true;
this.moveColorSelectionRingTo_(point);
@@ -775,13 +911,13 @@ class ColorSelectionArea extends HTMLElement {
/**
* @param {!Point} point
*/
- mouseMove(point) {
+ pointerMove(point) {
if (this.colorSelectionRing_.drag) {
this.moveColorSelectionRingTo_(point);
}
}
- mouseUp() {
+ pointerUp() {
this.colorSelectionRing_.drag = false;
}
@@ -1011,6 +1147,7 @@ class ColorSelectionRing extends HTMLElement {
initialize() {
this.set(this.backingColorPalette_.left, this.backingColorPalette_.top);
+ this.onPositionChange_();
}
/**
@@ -1873,65 +2010,3 @@ class ChannelLabel extends HTMLElement {
}
}
window.customElements.define('channel-label', ChannelLabel);
-
-/**
- * SubmissionControls: Provides functionality to submit or discard a change.
- */
-class SubmissionControls extends HTMLElement {
- /**
- * @param {function} submitCallback executed if the submit button is clicked
- * @param {function} cancelCallback executed if the cancel button is clicked
- */
- constructor(submitCallback, cancelCallback) {
- super();
-
- const padding = document.createElement('span');
- padding.setAttribute('id', 'submission-controls-padding');
- this.append(padding);
-
- this.submitButton_ = new SubmissionButton(
- submitCallback,
- '<svg width="14" height="10" viewBox="0 0 14 10" fill="none" ' +
- 'xmlns="http://www.w3.org/2000/svg"><path d="M13.3516 ' +
- '1.35156L5 9.71094L0.648438 5.35156L1.35156 4.64844L5 ' +
- '8.28906L12.6484 0.648438L13.3516 1.35156Z" fill="WindowText"/></svg>');
- this.cancelButton_ = new SubmissionButton(
- cancelCallback,
- '<svg width="14" height="14" viewBox="0 0 14 14" fill="none" ' +
- 'xmlns="http://www.w3.org/2000/svg"><path d="M7.71094 7L13.1016 ' +
- '12.3984L12.3984 13.1016L7 7.71094L1.60156 13.1016L0.898438 ' +
- '12.3984L6.28906 7L0.898438 1.60156L1.60156 0.898438L7 ' +
- '6.28906L12.3984 0.898438L13.1016 1.60156L7.71094 7Z" ' +
- 'fill="WindowText"/></svg>');
- this.append(this.submitButton_, this.cancelButton_);
- }
-
- get submitButton() {
- return this.submitButton_;
- }
-
- get cancelButton() {
- return this.cancelButton_;
- }
-}
-window.customElements.define('submission-controls', SubmissionControls);
-
-/**
- * SubmissionButton: Button with a custom look that can be clicked for
- * a submission action.
- */
-class SubmissionButton extends HTMLElement {
- /**
- * @param {function} clickCallback executed when the button is clicked
- * @param {string} htmlString custom look for the button
- */
- constructor(clickCallback, htmlString) {
- super();
-
- this.setAttribute('tabIndex', '0');
- this.innerHTML = htmlString;
-
- this.addEventListener('click', clickCallback);
- }
-}
-window.customElements.define('submission-button', SubmissionButton);
diff --git a/chromium/third_party/blink/renderer/core/html/forms/resources/color_picker_common.js b/chromium/third_party/blink/renderer/core/html/forms/resources/color_picker_common.js
index bdd671d2cf0..907aa31debe 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/resources/color_picker_common.js
+++ b/chromium/third_party/blink/renderer/core/html/forms/resources/color_picker_common.js
@@ -43,6 +43,9 @@ function initialize(args) {
document.body.classList.add('controls-refresh');
}
main.classList.add('color-suggestion-picker-main');
+ if (global.params.isBorderTransparent) {
+ main.style.borderColor = 'transparent';
+ }
errorString = validateColorSuggestionPickerArguments(args);
} else {
main.classList.add('color-picker-main');
diff --git a/chromium/third_party/blink/renderer/core/html/forms/resources/datetimelocal_picker.js b/chromium/third_party/blink/renderer/core/html/forms/resources/datetimelocal_picker.js
index 64f219fcaad..414bfde715b 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/resources/datetimelocal_picker.js
+++ b/chromium/third_party/blink/renderer/core/html/forms/resources/datetimelocal_picker.js
@@ -19,10 +19,9 @@ function initializeDateTimeLocalPicker(config) {
/**
* DateTimeLocalPicker: Custom element providing a datetime-local picker implementation.
- * DateTimeLocalPicker contains 3 parts:
+ * DateTimeLocalPicker contains 2 parts:
* - date picker
* - time picker
- * - submission controls
*/
class DateTimeLocalPicker extends HTMLElement {
constructor(config) {
@@ -34,34 +33,72 @@ class DateTimeLocalPicker extends HTMLElement {
this.timePicker_ = new TimePicker(config);
this.append(this.datePicker_.element, this.timePicker_);
- this.submissionControls_ = new SubmissionControls(
- this.onSubmitButtonClick_, this.onCancelButtonClick_);
- this.append(this.submissionControls_);
+ this.hadValidValueWhenOpened_ =
+ (config.currentValue !== '') && (this.datePicker_.selection() != null);
+ this.initialSelectedValue_ = this.selectedValue;
+
this.addEventListener('keydown', this.onKeyDown_);
+ this.addEventListener('click', this.onClick_);
window.addEventListener('resize', this.onWindowResize_, {once: true});
};
- onSubmitButtonClick_ = () => {
- const selectedValue = this.datePicker_.getSelectedValue() + 'T' +
- this.timePicker_.selectedValue;
- window.setTimeout(function() {
- window.pagePopupController.setValueAndClosePopup(0, selectedValue);
- }, 100);
- };
-
- onCancelButtonClick_ = () => {
- window.pagePopupController.closePopup();
- };
-
onKeyDown_ = (event) => {
switch (event.key) {
case 'Enter':
- this.submissionControls_.submitButton.click();
+ // Submit the popup for an Enter keypress except when the user is
+ // hitting Enter to activate the month switcher button, Today button,
+ // or previous/next month arrows.
+ if (!event.target.matches(
+ '.calendar-navigation-button, .month-popup-button, .year-list-view')) {
+ window.pagePopupController.setValueAndClosePopup(
+ 0, this.selectedValue);
+ } else if (event.target.matches(
+ '.calendar-navigation-button, .year-list-view')) {
+ // Navigating with the previous/next arrows may change selection,
+ // so push this change to the in-page control but don't
+ // close the popup.
+ window.pagePopupController.setValue(this.selectedValue);
+ }
break;
case 'Escape':
- this.submissionControls_.cancelButton.click();
+ if (this.selectedValue === this.initialSelectedValue_) {
+ window.pagePopupController.closePopup();
+ } else {
+ this.datePicker_.resetToInitialValue();
+ this.timePicker_.resetToInitialValue();
+ window.pagePopupController.setValue(
+ this.hadValidValueWhenOpened_ ? this.initialSelectedValue_ : '');
+ }
+ break;
+ case 'ArrowUp':
+ case 'ArrowDown':
+ case 'ArrowLeft':
+ case 'ArrowRight':
+ case 'PageUp':
+ case 'PageDown':
+ if (event.target.matches('.calendar-table-view, .time-column') &&
+ this.hasSelectedDate) {
+ window.pagePopupController.setValue(this.selectedValue);
+ }
+ // Stop the native scrolling behavior; the Time picker needs to manage
+ // its own scroll position.
+ event.preventDefault();
break;
+ case 'Home':
+ case 'End':
+ // Prevent an attempt to scroll to the end of
+ // of an infinitely looping time picker column.
+ event.preventDefault();
+ break;
+ }
+ };
+
+ onClick_ = (event) => {
+ if (event.target.matches(
+ '.day-cell, .time-cell, .today-button-refresh, .calendar-navigation-button, .year-list-view, .calendar-navigation-button, .today-button-icon-refresh, .month-button') &&
+ this.hasSelectedDate) {
+ window.pagePopupController.setValue(this.selectedValue);
}
};
@@ -69,6 +106,19 @@ class DateTimeLocalPicker extends HTMLElement {
this.datePicker_.calendarTableView.element.focus();
};
+ // This will be false if neither the initial value of the
+ // control nor today's date are within a valid date range defined
+ // by the 'step', 'min', and 'max' attributes of the control.
+ get hasSelectedDate() {
+ return (this.datePicker_.selection() != null);
+ }
+
+ get selectedValue() {
+ return this.hasSelectedDate ? (this.datePicker_.getSelectedValue() + 'T' +
+ this.timePicker_.selectedValue) :
+ '';
+ }
+
get height() {
return DateTimeLocalPicker.Height;
}
@@ -77,10 +127,6 @@ class DateTimeLocalPicker extends HTMLElement {
return this.datePicker_.width() + this.timePicker_.width;
}
- get submissionControls() {
- return this.submissionControls_;
- }
-
get datePicker() {
return this.datePicker_;
}
@@ -90,5 +136,5 @@ class DateTimeLocalPicker extends HTMLElement {
}
}
DateTimeLocalPicker.ClassName = 'datetimelocal-picker';
-DateTimeLocalPicker.Height = 320;
+DateTimeLocalPicker.Height = 280;
window.customElements.define('datetimelocal-picker', DateTimeLocalPicker);
diff --git a/chromium/third_party/blink/renderer/core/html/forms/resources/listPicker.css b/chromium/third_party/blink/renderer/core/html/forms/resources/listPicker.css
index d190f9dab12..be88651d93a 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/resources/listPicker.css
+++ b/chromium/third_party/blink/renderer/core/html/forms/resources/listPicker.css
@@ -16,3 +16,13 @@ option, optgroup {
.wrap option {
white-space: pre-wrap;
}
+
+.controls-refresh select {
+ border-radius: 0px;
+ outline: none;
+}
+
+.controls-refresh option:checked:enabled {
+ outline: solid 2px -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
diff --git a/chromium/third_party/blink/renderer/core/html/forms/resources/listPicker.js b/chromium/third_party/blink/renderer/core/html/forms/resources/listPicker.js
index 8294f0b6d7e..0709fa0ee21 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/resources/listPicker.js
+++ b/chromium/third_party/blink/renderer/core/html/forms/resources/listPicker.js
@@ -21,6 +21,9 @@ function initialize(args) {
global.params = args;
var main = $('main');
main.innerHTML = '';
+ if (global.params.isFormControlsRefreshEnabled) {
+ document.body.classList.add('controls-refresh');
+ }
global.picker = new ListPicker(main, args);
}
@@ -37,7 +40,6 @@ function handleArgumentsTimeout() {
*/
function ListPicker(element, config) {
Picker.call(this, element, config);
- window.pagePopupController.selectFontsFromOwnerDocument(document);
this._selectElement = createElement('select');
this._selectElement.size = 20;
this._element.appendChild(this._selectElement);
diff --git a/chromium/third_party/blink/renderer/core/html/forms/resources/month_picker.js b/chromium/third_party/blink/renderer/core/html/forms/resources/month_picker.js
index 47cf5ee0c4a..5ebf0d6b6b3 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/resources/month_picker.js
+++ b/chromium/third_party/blink/renderer/core/html/forms/resources/month_picker.js
@@ -10,7 +10,10 @@
function initializeMonthPicker(config) {
global.picker = new MonthPicker(config);
main.append(global.picker);
- main.style.border = '1px solid transparent';
+ main.style.border = '1px solid #bfbfbf';
+ if (global.params.isBorderTransparent) {
+ main.style.borderColor = 'transparent';
+ }
main.style.height = (MonthPicker.Height - 2) + 'px';
main.style.width = (MonthPicker.Width - 2) + 'px';
resizeWindow(MonthPicker.Width, MonthPicker.Height);
@@ -29,7 +32,7 @@ class MonthPicker extends HTMLElement {
this.initializeFromConfig_(config);
this.yearListView_ =
- new YearListView(this.minimumMonth_, this.maximumMonth_);
+ new YearListView(this.minimumMonth_, this.maximumMonth_, config);
this.append(this.yearListView_.element);
this.initializeYearListView_();
@@ -38,6 +41,7 @@ class MonthPicker extends HTMLElement {
this.initializeTodayButton_();
window.addEventListener('resize', this.onWindowResize_);
+ this.addEventListener('keydown', this.onKeyDown_);
}
initializeFromConfig_ = (config) => {
@@ -49,20 +53,6 @@ class MonthPicker extends HTMLElement {
Month.Maximum;
this.minimumMonth_ = Month.createFromDay(minimum.firstDay());
this.maximumMonth_ = Month.createFromDay(maximum.lastDay());
-
- const initialSelection = parseDateString(config.currentValue);
- const initialSelectedMonth = initialSelection ?
- Month.createFromDay(initialSelection.middleDay()) :
- Month.createFromToday();
- this.initialValidSelection_ = false;
- if (initialSelectedMonth < this.minimumMonth_) {
- this.selectedMonth_ = this.minimumMonth_;
- } else if (initialSelectedMonth > this.maximumMonth_) {
- this.selectedMonth_ = this.maximumMonth_;
- } else {
- this.selectedMonth_ = initialSelectedMonth;
- this.initialValidSelection_ = initialSelection != null;
- }
};
initializeYearListView_ = () => {
@@ -78,26 +68,21 @@ class MonthPicker extends HTMLElement {
MonthPicker.YearWidth + 'px';
}
this.yearListView_.element.style.top = MonthPicker.YearPadding + 'px';
- if (this.initialValidSelection_) {
- this.yearListView_.setSelectedMonth(this.selectedMonth_);
- }
- this.yearListView_.show(this.selectedMonth_);
+
+ let yearForInitialScroll = this.selectedMonth ?
+ this.selectedMonth.year - 1 :
+ Month.createFromToday().year - 1;
+ this.yearListView_.scrollToRow(yearForInitialScroll, false);
+ this.yearListView_.selectWithoutAnimating(yearForInitialScroll);
+
this.yearListView_.on(
YearListView.EventTypeYearListViewDidSelectMonth,
this.onYearListViewDidSelectMonth_);
- this.yearListView_.on(
- YearListView.EventTypeYearListViewDidHide, this.onYearListViewDidHide_);
- };
-
- onYearListViewDidHide_ = (sender) => {
- const selectedValue = this.selectedMonth_.toString();
- window.setTimeout(function() {
- window.pagePopupController.setValueAndClosePopup(0, selectedValue);
- }, 100);
};
onYearListViewDidSelectMonth_ = (sender, month) => {
- this.selectedMonth_ = month;
+ const selectedValue = month.toString();
+ window.pagePopupController.setValueAndClosePopup(0, selectedValue);
};
initializeTodayButton_ = () => {
@@ -107,8 +92,7 @@ class MonthPicker extends HTMLElement {
this.todayButton_.element.classList.add(MonthPicker.ClassNameTodayButton);
const monthContainingToday = Month.createFromToday();
this.todayButton_.setDisabled(
- monthContainingToday < this.minimumMonth_ ||
- monthContainingToday > this.maximumMonth_);
+ !this.yearListView_.isValid(monthContainingToday));
this.todayButton_.on(
CalendarNavigationButton.EventTypeButtonClick,
this.onTodayButtonClick_);
@@ -116,15 +100,68 @@ class MonthPicker extends HTMLElement {
onTodayButtonClick_ = (sender) => {
const selectedValue = Month.createFromToday().toString();
- window.setTimeout(function() {
- window.pagePopupController.setValueAndClosePopup(0, selectedValue);
- }, 100);
+ window.pagePopupController.setValueAndClosePopup(0, selectedValue);
+ };
+
+ onKeyDown_ = (event) => {
+ switch (event.key) {
+ case 'Enter':
+ // Don't do anything here if user has hit Enter on 'This month'
+ // button. We'll handle that in this.onTodayButtonClick_.
+ if (!event.target.matches('.calendar-navigation-button')) {
+ if (this.selectedMonth) {
+ window.pagePopupController.setValueAndClosePopup(
+ 0, this.selectedMonth.toString());
+ } else {
+ window.pagePopupController.closePopup();
+ }
+ }
+ break;
+ case 'Escape':
+ if (!this.selectedMonth ||
+ (this.selectedMonth.equals(this.initialSelectedMonth))) {
+ window.pagePopupController.closePopup();
+ } else {
+ this.resetToInitialValue_();
+ window.pagePopupController.setValue(
+ this.hadValidValueWhenOpened ?
+ this.initialSelectedMonth.toString() :
+ '');
+ }
+ break;
+ case 'ArrowUp':
+ case 'ArrowDown':
+ case 'ArrowLeft':
+ case 'ArrowRight':
+ case 'PageUp':
+ case 'PageDown':
+ if (this.selectedMonth) {
+ window.pagePopupController.setValue(this.selectedMonth.toString());
+ }
+ break;
+ }
+ };
+
+ resetToInitialValue_ = () => {
+ this.yearListView_.setSelectedMonthAndUpdateView(this.initialSelectedMonth);
};
onWindowResize_ = (event) => {
window.removeEventListener('resize', this.onWindowResize_);
this.yearListView_.element.focus();
};
+
+ get selectedMonth() {
+ return this.yearListView_._selectedMonth;
+ };
+
+ get initialSelectedMonth() {
+ return this.yearListView_._initialSelectedMonth;
+ };
+
+ get hadValidValueWhenOpened() {
+ return this.yearListView_._hadValidValueWhenOpened;
+ };
}
MonthPicker.Width = 232;
MonthPicker.YearWidth = 194;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/resources/suggestionPicker.css b/chromium/third_party/blink/renderer/core/html/forms/resources/suggestionPicker.css
index 860e2a397f0..011130295f3 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/resources/suggestionPicker.css
+++ b/chromium/third_party/blink/renderer/core/html/forms/resources/suggestionPicker.css
@@ -56,7 +56,7 @@
}
.controls-refresh .suggestion-list {
- border-color: transparent;
+ border-color: #bfbfbf;
padding: 4px;
}
@@ -69,6 +69,8 @@
.controls-refresh .suggestion-list-entry:focus {
background-color: #E5E5E5;
+ outline: solid 2px -webkit-focus-ring-color;
+ outline-offset: -2px;
}
.controls-refresh .suggestion-list-entry .title {
diff --git a/chromium/third_party/blink/renderer/core/html/forms/resources/suggestionPicker.js b/chromium/third_party/blink/renderer/core/html/forms/resources/suggestionPicker.js
index e42d5424a39..760c04e1022 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/resources/suggestionPicker.js
+++ b/chromium/third_party/blink/renderer/core/html/forms/resources/suggestionPicker.js
@@ -200,6 +200,9 @@ SuggestionPicker.prototype._layout = function() {
if (this._config.isLocaleRTL)
this._element.classList.add('locale-rtl');
this._containerElement = createElement('ul', 'suggestion-list');
+ if (global.params.isBorderTransparent) {
+ this._containerElement.style.borderColor = 'transparent';
+ }
this._containerElement.addEventListener(
'click', this._handleEntryClick.bind(this), false);
for (var i = 0; i < this._config.suggestionValues.length; ++i) {
diff --git a/chromium/third_party/blink/renderer/core/html/forms/resources/time_picker.css b/chromium/third_party/blink/renderer/core/html/forms/resources/time_picker.css
index ebd30d88d0c..2a460119fea 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/resources/time_picker.css
+++ b/chromium/third_party/blink/renderer/core/html/forms/resources/time_picker.css
@@ -3,16 +3,12 @@
* found in the LICENSE file.
*/
-body {
- font-family: sans-serif;
-}
-
.time-picker {
background: #FFFFFF;
- border: 1px solid transparent;
+ border: 1px solid #bfbfbf;
display: flex;
flex-direction: column;
- height: 298px;
+ height: 258px;
}
.time-columns {
@@ -31,7 +27,7 @@ body {
outline: none;
overflow: scroll;
padding: 0;
- scroll-snap-type: y mandatory;
+ position: relative;
width: 52px;
}
@@ -46,57 +42,23 @@ body {
font-size: 14px;
height: 32px;
line-height: 32px;
- scroll-snap-align: start;
+ position: relative;
text-align: center;
width: 48px;
}
.time-cell:hover {
- background: #E5E5E5;
+ background-color: rgba(0, 117, 255, 0.3);
}
.time-cell.selected {
- background-color: #CECECE;
+ background-color: #0075FF;
+ color: #FFFFFF;
font-weight: bold;
}
.time-column:focus .time-cell.selected {
- border-color: highlight;
-}
-
-.submission-controls {
- align-items: center;
- border-top: 1px solid #CECECE;
- bottom: 0px;
- display: flex;
- flex-direction: row;
- height: 40px;
- position: absolute;
- width: 100%;
-}
-
-#submission-controls-padding {
- height: 100%;
- width: 84%;
-}
-
-.submission-button {
- background-color: #FFFFFF;
- border: 2px solid transparent;
- border-radius: 2px;
- height: 32px;
- margin: 4px;
- padding: 8px;
- width: 32px;
-}
-
-.submission-button:hover {
- background-color: #E5E5E5;
-}
-
-.submission-button:focus {
- border-color: highlight;
- outline: none;
+ border-color: #101010;
}
@media (forced-colors: active) {
@@ -119,22 +81,4 @@ body {
.time-column:focus .time-cell.selected {
border-color: WindowText;
}
-
- .submission-button {
- background-color: Window;
- forced-color-adjust: none;
- }
-
- .submission-button:hover {
- background-color: Window;
- border-color: Highlight;
- }
-
- .submission-button:focus {
- border-color: WindowText;
- }
-
- .submission-button path {
- fill: WindowText;
- }
}
diff --git a/chromium/third_party/blink/renderer/core/html/forms/resources/time_picker.js b/chromium/third_party/blink/renderer/core/html/forms/resources/time_picker.js
index 8959e6bc4cc..62280830b9d 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/resources/time_picker.js
+++ b/chromium/third_party/blink/renderer/core/html/forms/resources/time_picker.js
@@ -27,7 +27,6 @@ const TimeColumnType = {
AMPM: 5,
};
-
/**
* Supported label types.
* @enum {number}
@@ -63,7 +62,7 @@ class Time {
this.minute_ = minute;
this.second_ = second;
this.millisecond_ = millisecond;
- }
+ };
next = (columnType) => {
switch (columnType) {
@@ -210,15 +209,15 @@ DateTime.ISOStringRegExp = /^(\d+)-(\d+)-(\d+)T(\d+):(\d+):?(\d*).?(\d*)/;
/**
* TimePicker: Custom element providing a time picker implementation.
- * TimePicker contains 2 parts:
- * - column container
- * - submission controls
*/
class TimePicker extends HTMLElement {
constructor(config) {
super();
this.className = TimePicker.ClassName;
+ if (global.params.isBorderTransparent) {
+ this.style.borderColor = 'transparent';
+ }
this.initializeFromConfig_(config);
this.timeColumns_ = new TimeColumns(this);
@@ -226,10 +225,8 @@ class TimePicker extends HTMLElement {
if (config.mode == 'time') {
// TimePicker doesn't handle the submission when used for non-time types.
- this.submissionControls_ = new SubmissionControls(
- this.onSubmitButtonClick_, this.onCancelButtonClick_);
- this.append(this.submissionControls_);
this.addEventListener('keydown', this.onKeyDown_);
+ this.addEventListener('click', this.onClick_);
}
window.addEventListener('resize', this.onWindowResize_, {once: true});
@@ -237,48 +234,70 @@ class TimePicker extends HTMLElement {
initializeFromConfig_ = (config) => {
const initialSelection = parseDateTimeString(config.currentValue, 'time');
- this.selectedTime_ =
+ this.initialSelectedTime_ =
initialSelection ? initialSelection : Time.currentTime();
+ this.hadValidValueWhenOpened_ = (initialSelection != null);
this.hasSecond_ = config.hasSecond;
this.hasMillisecond_ = config.hasMillisecond;
this.hasAMPM_ = config.hasAMPM;
};
- onSubmitButtonClick_ = () => {
- const selectedValue = this.selectedValue;
- window.setTimeout(function() {
- window.pagePopupController.setValueAndClosePopup(0, selectedValue);
- }, 100);
- };
-
- onCancelButtonClick_ = () => {
- window.pagePopupController.closePopup();
- };
-
onWindowResize_ = (event) => {
- // Scroll columns to the second half to allow scrolling up.
- this.timeColumns_.scrollColumnsToMiddle();
+ this.timeColumns_.scrollColumnsToSelectedCells();
this.timeColumns_.firstChild.focus();
};
onKeyDown_ = (event) => {
switch (event.key) {
case 'Enter':
- this.submissionControls_.submitButton.click();
+ window.pagePopupController.setValueAndClosePopup(0, this.selectedValue);
break;
case 'Escape':
- this.submissionControls_.cancelButton.click();
+ if (this.selectedValue ===
+ this.initialSelectedTime.toString(
+ this.hasSecond, this.hasMillisecond)) {
+ window.pagePopupController.closePopup();
+ } else {
+ this.resetToInitialValue();
+ window.pagePopupController.setValue(
+ this.hadValidValueWhenOpened ? this.selectedValue : '');
+ }
+ break;
+ case 'ArrowUp':
+ case 'ArrowDown':
+ window.pagePopupController.setValue(this.selectedValue);
+ event.stopPropagation();
+ event.preventDefault();
+ break;
+ case 'Home':
+ case 'End':
+ // Prevent an attempt to scroll to the end of
+ // of an infinitely looping column.
+ event.preventDefault();
break;
}
};
+ onClick_ = (event) => {
+ window.pagePopupController.setValue(this.selectedValue);
+ };
+
+ resetToInitialValue = () => {
+ this.timeColumns_.resetToInitialValues();
+ this.timeColumns_.scrollColumnsToSelectedCells();
+ }
+
get selectedValue() {
return this.timeColumns_.selectedValue().toString(
this.hasSecond, this.hasMillisecond);
}
- get selectedTime() {
- return this.selectedTime_;
+ get initialSelectedTime() {
+ return this.initialSelectedTime_;
+ }
+
+ get hadValidValueWhenOpened() {
+ return this.hadValidValueWhenOpened_;
}
get hasSecond() {
@@ -304,13 +323,9 @@ class TimePicker extends HTMLElement {
get timeColumns() {
return this.timeColumns_;
}
-
- get submissionControls() {
- return this.submissionControls_;
- }
}
TimePicker.ClassName = 'time-picker';
-TimePicker.Height = 300;
+TimePicker.Height = 260;
TimePicker.ColumnWidth = 56;
TimePicker.BorderWidth = 1;
window.customElements.define('time-picker', TimePicker);
@@ -379,15 +394,21 @@ class TimeColumns extends HTMLElement {
return new Time(hour, minute, second, millisecond);
};
- scrollColumnsToMiddle = () => {
- this.hourColumn_.scrollTop = this.hourColumn_.scrollHeight / 2;
- this.minuteColumn_.scrollTop = this.minuteColumn_.scrollHeight / 2;
+ resetToInitialValues =
+ () => {
+ Array.prototype.forEach.call(this.children, (column) => {
+ column.resetToInitialValue();
+ });
+ }
+
+ scrollColumnsToSelectedCells = () => {
+ this.hourColumn_.scrollToSelectedCell();
+ this.minuteColumn_.scrollToSelectedCell();
if (this.secondColumn_) {
- this.secondColumn_.scrollTop = this.secondColumn_.scrollHeight / 2;
+ this.secondColumn_.scrollToSelectedCell();
}
if (this.millisecondColumn_) {
- this.millisecondColumn_.scrollTop =
- this.millisecondColumn_.scrollHeight / 2;
+ this.millisecondColumn_.scrollToSelectedCell();
}
}
}
@@ -405,10 +426,24 @@ class TimeColumn extends HTMLUListElement {
this.className = TimeColumn.ClassName;
this.tabIndex = 0;
this.columnType_ = columnType;
+ this.setAttribute('role', 'listbox');
+ if (this.columnType_ === TimeColumnType.HOUR) {
+ this.setAttribute('aria-label', global.params.axHourLabel);
+ } else if (this.columnType_ === TimeColumnType.MINUTE) {
+ this.setAttribute('aria-label', global.params.axMinuteLabel);
+ } else if (this.columnType_ === TimeColumnType.SECOND) {
+ this.setAttribute('aria-label', global.params.axSecondLabel);
+ } else if (this.columnType_ === TimeColumnType.MILLISECOND) {
+ this.setAttribute('aria-label', global.params.axMillisecondLabel);
+ } else {
+ this.setAttribute('aria-label', global.params.axAmPmLabel);
+ }
+
if (this.columnType_ == TimeColumnType.AMPM) {
this.createAndInitializeAMPMCells_(timePicker);
} else {
this.createAndInitializeCells_(timePicker);
+ this.setupScrollHandler_();
}
this.addEventListener('click', this.onClick_);
@@ -417,23 +452,156 @@ class TimeColumn extends HTMLUListElement {
createAndInitializeCells_ = (timePicker) => {
const totalCells = Time.numberOfValues(this.columnType_, timePicker.hasAMPM);
- let currentTime = timePicker.selectedTime.clone();
+ let currentTime = timePicker.initialSelectedTime.clone();
+
+ // The granularity of millisecond cells is once cell per 100ms.
+ // But, we want to have a cell with the exact millisecond value of the
+ // in-page control, so we'll replace the millisecond cell closest to that
+ // value with the exact value. We do that by figuring out here which of
+ // the cells will be the closest one here, and then matching against that
+ // one in the subsequent loop.
+ let roundedMillisecondValue = 0;
+ if (this.columnType_ === TimeColumnType.MILLISECOND) {
+ let millisecondValue =
+ currentTime.value(TimeColumnType.MILLISECOND, timePicker.hasAMPM);
+ roundedMillisecondValue =
+ (100 * Math.floor((Number(millisecondValue) + 50.0) / 100.0)) % 1000;
+ }
+
+ let time = new Time(1, 1, 1, 100);
let cells = [];
- let duplicateCells = [];
- // In order to support a continuous looping navigation for up/down arrows,
- // the initial list of cells is doubled and middleTimeCell is kept
- // to inform where the duplicated cells begin.
+ let initialCellIndex = -1;
for (let i = 0; i < totalCells; i++) {
- let value = currentTime.value(this.columnType_, timePicker.hasAMPM);
+ let value = time.value(this.columnType_, timePicker.hasAMPM);
+
+ if (this.columnType_ === TimeColumnType.MILLISECOND &&
+ Number(value) === roundedMillisecondValue) {
+ // Set this cell to the exact ms value of the in-page control
+ value =
+ currentTime.value(TimeColumnType.MILLISECOND, timePicker.hasAMPM);
+ initialCellIndex = i;
+ } else if (
+ time.value(this.columnType_, timePicker.hasAMPM) ===
+ currentTime.value(this.columnType_, timePicker.hasAMPM)) {
+ initialCellIndex = i;
+ }
+
let timeCell = new TimeCell(value, localizeNumber(value));
- let duplicatedTimeCell = new TimeCell(value, localizeNumber(value));
cells.push(timeCell);
- duplicateCells.push(duplicatedTimeCell);
- currentTime.next(this.columnType_);
+
+ timeCell.initialOffsetTop = TimeColumn.CELL_HEIGHT * i;
+ timeCell.style.top = `${TimeColumn.SCROLL_OFFSET}px`;
+
+ time.next(this.columnType_);
+ }
+ this.selectedTimeCell = this.initialTimeCell_ = cells[initialCellIndex];
+ this.cellsInLayoutOrder = cells;
+ this.append(...cells);
+ };
+
+ /*
+ * Create a scroll handler that implements infinite looping scroll by
+ * rotating TimeCells up/down so that there is always at least one cell
+ * offscreen in the direction of the scroll. This activity should be
+ * invisible to the user.
+ */
+ setupScrollHandler_ = () => {
+ let lastScrollPosition = 0;
+ let upcomingSnapToCellEdge = null;
+ this.addEventListener('scroll', (event) => {
+ let isGoingDown = (this.scrollTop > lastScrollPosition);
+ lastScrollPosition = this.scrollTop;
+
+ // Rotate cells down until there is one cell beyond the bottom
+ // of the visible scroller area.
+ while (this.cellsInLayoutOrder[this.cellsInLayoutOrder.length - 1]
+ .offsetTop -
+ this.scrollTop - this.clientHeight <
+ TimeColumn.CELL_HEIGHT) {
+ this.rotateCells_(
+ /*topToBottom*/ true);
+ }
+
+ // Rotate cells up until there is one cell beyond the top
+ // of the visible scroller area.
+ while (this.scrollTop - this.cellsInLayoutOrder[0].offsetTop <
+ TimeColumn.CELL_HEIGHT * 2) {
+ this.rotateCells_(
+ /*topToBottom*/ false);
+ }
+
+ // Snap the scroll amount to the nearest TimeCell top edge 1 second
+ // after the user has stopped scrolling. This would be done with
+ // CSS scroll-snap-align, but it interferes with this scroll handler
+ // and causes jittery scrolling.
+ window.clearTimeout(upcomingSnapToCellEdge);
+ upcomingSnapToCellEdge =
+ window.setTimeout(() => {this.snapToCellEdge_(isGoingDown)}, 1000);
+ });
+ };
+
+ /*
+ * Scroll the column so that the top is aligned with the top edge of the
+ * nearest TimeCell in the given direction.
+ */
+ snapToCellEdge_ = (isGoingDown) => {
+ let offsetFromCellEdge =
+ (this.cellsInLayoutOrder[this.cellsInLayoutOrder.length - 1].offsetTop -
+ this.scrollTop) %
+ TimeColumn.CELL_HEIGHT;
+ if (isGoingDown) {
+ this.scrollTop += offsetFromCellEdge;
+ } else {
+ if (offsetFromCellEdge != 0) {
+ this.scrollTop -= TimeColumn.CELL_HEIGHT - offsetFromCellEdge;
+ }
+ }
+ };
+
+ // Ideally we would have truly infinite scrolling in both directions.
+ // However, the platform does not allow scrolling into negative scroll
+ // offsets. So, we start the column at a large positive scroll so that
+ // the column will be unlikely to hit the top during normal use.
+ static SCROLL_OFFSET = 100000;
+ static CELL_HEIGHT = 36; // Height of one TimeCell, including border
+
+ // Using position:absolute for TimeCells seems like the natural choice,
+ // but absolutely positioned children don't cause the TimeColumn scroll
+ // container to expand to hold the cells, so they fall off the end of
+ // the popup. Instead, we use relative positioning and use these
+ // helpers to convert to an "absolute" position that is easier to reason
+ // about when manipulating the layout position of the TimeCells.
+ static getCellAbsolutePosition = (cell) => {
+ let cellOffset = parseInt(cell.style.top.substring(
+ 0, cell.style.top.length - 2)); // Chop off the 'px'
+ return (cellOffset + cell.initialOffsetTop);
+ };
+ static setCellAbsolutePosition = (cell, absolutePosition) => {
+ cell.style.top = `${absolutePosition - cell.initialOffsetTop}px`;
+ };
+
+ // Take the top/bottom TimeCell in this column and move it to the
+ // bottom/top. This should only be done for offscreen cells so that
+ // it is invisible to the user -- but it ensures that the cells will
+ // always be visible wherever the user scrolls.
+ rotateCells_ = (topToBottom) => {
+ if (topToBottom) {
+ let topCell = this.cellsInLayoutOrder.shift();
+ let bottomCell =
+ this.cellsInLayoutOrder[this.cellsInLayoutOrder.length - 1];
+ let bottomCellAbsoluteOffset =
+ TimeColumn.getCellAbsolutePosition(bottomCell);
+ TimeColumn.setCellAbsolutePosition(
+ topCell, bottomCellAbsoluteOffset + TimeColumn.CELL_HEIGHT);
+ this.cellsInLayoutOrder.push(topCell);
+ } else {
+ let topCell = this.cellsInLayoutOrder[0];
+ let bottomCell = this.cellsInLayoutOrder.pop();
+ let absoluteTopCellOffset = TimeColumn.getCellAbsolutePosition(topCell);
+ TimeColumn.setCellAbsolutePosition(
+ bottomCell, absoluteTopCellOffset - TimeColumn.CELL_HEIGHT);
+ this.cellsInLayoutOrder.unshift(bottomCell);
}
- this.selectedTimeCell = duplicateCells[0];
- this.middleTimeCell_ = duplicateCells[0];
- this.append(...cells, ...duplicateCells);
};
createAndInitializeAMPMCells_ = (timePicker) => {
@@ -444,7 +612,7 @@ class TimeColumn extends HTMLUListElement {
cells.push(timeCell);
}
- if (timePicker.selectedTime.isAM()) {
+ if (timePicker.initialSelectedTime.isAM()) {
this.append(cells[Label.AM], cells[Label.PM]);
this.selectedTimeCell = cells[Label.AM];
} else {
@@ -458,40 +626,53 @@ class TimeColumn extends HTMLUListElement {
};
/**
- * Continuous looping navigation for up/down arrows is supported by:
- * - moving for ArrowUp to previous cell and for topmost cell which
- * has no previous, we are moving to the last cell from the first list
- * - moving for ArrowDown to next cell and for the last duplicated cell
- * which has no next, we are moving to the first cell from the duplicated list
+ * Continuous looping navigation for up/down arrows and scrolling is
+ * supported by rotating the layout positions of the TimeCells. This
+ * is done in a scroll event handler and the following keydown handler.
+ * Cells are rotated in before they are reached by the visible part of
+ * the scroller, so the user just sees an infinitely looping column.
*/
onKeyDown_ = (event) => {
- let eventHandled = false;
switch (event.key) {
case 'ArrowUp':
- const previousTimeCell = this.selectedTimeCell.previousSibling;
- if (previousTimeCell) {
- this.selectedTimeCell = previousTimeCell;
- previousTimeCell.scrollIntoViewIfNeeded(false);
- } else if (this.columnType != TimeColumnType.AMPM) {
- // move from the topmost cell to the last cell (the last cell is
- // the first one before the duplicated list).
- this.selectedTimeCell = this.middleTimeCell.previousSibling;
- this.selectedTimeCell.scrollIntoView();
+ const previousTimeCell = this.selectedTimeCell.previousSibling ?
+ this.selectedTimeCell.previousSibling :
+ this.lastElementChild;
+
+ if (this.scrollTop === 0 && previousTimeCell.offsetTop <= 0) {
+ // If the user somehow made it all the way to the top of the
+ // scroller, stop going up and rotating cells into negative
+ // offsets. This should not be a normal scenario.
+ break;
}
- eventHandled = true;
+
+ // Ensure that we don't run out of cells ahead of the selected cell in
+ // the event that the scroll event handler can't keep up. This can
+ // happen e.g. if the user holds down the arrow key.
+ if (this.columnType_ !== TimeColumnType.AMPM &&
+ this.selectedTimeCell === this.cellsInLayoutOrder[0]) {
+ this.rotateCells_(/*topToBottom*/ false);
+ }
+
+ this.selectedTimeCell = previousTimeCell;
+ this.selectedTimeCell.scrollIntoViewIfNeeded(false);
break;
case 'ArrowDown':
- const nextTimeCell = this.selectedTimeCell.nextSibling;
- if (nextTimeCell) {
- this.selectedTimeCell = nextTimeCell;
- nextTimeCell.scrollIntoViewIfNeeded(false);
- } else if (this.columnType != TimeColumnType.AMPM) {
- // move from the last duplicated cell to the first cell
- // of the duplicated list.
- this.selectedTimeCell = this.middleTimeCell;
- this.selectedTimeCell.scrollIntoView(false);
+ const nextTimeCell = this.selectedTimeCell.nextSibling ?
+ this.selectedTimeCell.nextSibling :
+ this.firstElementChild;
+
+ // Ensure that we don't run out of cells ahead of the selected cell in
+ // the event that the scroll event handler can't keep up. This can
+ // happen e.g. if the user holds down the arrow key.
+ if (this.columnType_ !== TimeColumnType.AMPM &&
+ this.selectedTimeCell ===
+ this.cellsInLayoutOrder[this.cellsInLayoutOrder.length - 1]) {
+ this.rotateCells_(/*topToBottom*/ true);
}
- eventHandled = true;
+
+ this.selectedTimeCell = nextTimeCell;
+ this.selectedTimeCell.scrollIntoViewIfNeeded(false);
break;
case 'ArrowLeft':
const previousTimeColumn = this.previousSibling;
@@ -506,12 +687,14 @@ class TimeColumn extends HTMLUListElement {
}
break;
}
+ };
- if (eventHandled) {
- event.stopPropagation();
- event.preventDefault();
+ scrollToSelectedCell = (cell) => {
+ while(this.cellsInLayoutOrder[1] != this.selectedTimeCell) {
+ this.rotateCells_(/*topToBottom*/true);
}
- };
+ this.scrollTop = this.selectedTimeCell.offsetTop;
+ }
get selectedTimeCell() {
return this.selectedTimeCell_;
@@ -520,14 +703,21 @@ class TimeColumn extends HTMLUListElement {
set selectedTimeCell(timeCell) {
if (this.selectedTimeCell_) {
this.selectedTimeCell_.classList.remove('selected');
+ this.selectedTimeCell_.removeAttribute('aria-selected');
}
this.selectedTimeCell_ = timeCell;
+ this.setAttribute('aria-activedescendant', timeCell.id);
this.selectedTimeCell_.classList.add('selected');
+ this.selectedTimeCell_.setAttribute('aria-selected', 'true');
}
- get middleTimeCell() {
- return this.middleTimeCell_;
- }
+ resetToInitialValue = () => {
+ if (this.columnType_ == TimeColumnType.AMPM) {
+ this.selectedTimeCell = this.firstChild;
+ } else {
+ this.selectedTimeCell = this.initialTimeCell_;
+ }
+ };
get columnType() {
return this.columnType_;
@@ -546,65 +736,16 @@ class TimeCell extends HTMLLIElement {
this.className = TimeCell.ClassName;
this.textContent = localizedValue;
this.value = value;
- };
-}
-TimeCell.ClassName = 'time-cell';
-window.customElements.define('time-cell', TimeCell, {extends: 'li'});
-
-/**
- * SubmissionControls: Provides functionality to submit or discard a change.
- */
-class SubmissionControls extends HTMLElement {
- constructor(submitCallback, cancelCallback) {
- super();
-
- const padding = document.createElement('span');
- padding.setAttribute('id', 'submission-controls-padding');
- this.append(padding);
-
- this.className = SubmissionControls.ClassName;
-
- this.submitButton_ = new SubmissionButton(
- submitCallback,
- '<svg width="14" height="10" viewBox="0 0 14 10" fill="none" ' +
- 'xmlns="http://www.w3.org/2000/svg"><path d="M13.3516 ' +
- '1.35156L5 9.71094L0.648438 5.35156L1.35156 4.64844L5 ' +
- '8.28906L12.6484 0.648438L13.3516 1.35156Z" fill="black"/></svg>');
- this.cancelButton_ = new SubmissionButton(
- cancelCallback,
- '<svg width="14" height="14" viewBox="0 0 14 14" fill="none" ' +
- 'xmlns="http://www.w3.org/2000/svg"><path d="M7.71094 7L13.1016 ' +
- '12.3984L12.3984 13.1016L7 7.71094L1.60156 13.1016L0.898438 ' +
- '12.3984L6.28906 7L0.898438 1.60156L1.60156 0.898438L7 ' +
- '6.28906L12.3984 0.898438L13.1016 1.60156L7.71094 7Z" ' +
- 'fill="black"/></svg>');
- this.append(this.submitButton_, this.cancelButton_);
- }
- get submitButton() {
- return this.submitButton_;
- }
+ this.setAttribute('role', 'option');
+ this.id = TimeCell.getNextUniqueId();
+ };
- get cancelButton() {
- return this.cancelButton_;
+ static getNextUniqueId() {
+ return `timeCell${TimeCell.idCount++}`;
}
-}
-SubmissionControls.ClassName = 'submission-controls';
-window.customElements.define('submission-controls', SubmissionControls);
-
-/**
- * SubmissionButton: Button with a custom look that can be clicked for
- * a submission action.
- */
-class SubmissionButton extends HTMLButtonElement {
- constructor(clickCallback, htmlString) {
- super();
- this.className = SubmissionButton.ClassName;
- this.innerHTML = htmlString;
- this.addEventListener('click', clickCallback);
- }
+ static idCount = 0;
}
-SubmissionButton.ClassName = 'submission-button';
-window.customElements.define(
- 'submission-button', SubmissionButton, {extends: 'button'});
+TimeCell.ClassName = 'time-cell';
+window.customElements.define('time-cell', TimeCell, {extends: 'li'});
diff --git a/chromium/third_party/blink/renderer/core/html/forms/search_input_type.cc b/chromium/third_party/blink/renderer/core/html/forms/search_input_type.cc
index 5b964f18b85..74fff77ea69 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/search_input_type.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/search_input_type.cc
@@ -31,6 +31,9 @@
#include "third_party/blink/renderer/core/html/forms/search_input_type.h"
#include "third_party/blink/public/platform/task_type.h"
+#include "third_party/blink/renderer/core/css/css_property_names.h"
+#include "third_party/blink/renderer/core/css_value_keywords.h"
+#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/shadow_root.h"
#include "third_party/blink/renderer/core/events/keyboard_event.h"
#include "third_party/blink/renderer/core/frame/web_feature.h"
@@ -39,7 +42,6 @@
#include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/input_type_names.h"
-#include "third_party/blink/renderer/core/layout/layout_search_field.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
@@ -56,11 +58,6 @@ void SearchInputType::CountUsage() {
CountUsageIfVisible(WebFeature::kInputTypeSearch);
}
-LayoutObject* SearchInputType::CreateLayoutObject(const ComputedStyle&,
- LegacyLayout) const {
- return new LayoutSearchField(&GetElement());
-}
-
const AtomicString& SearchInputType::FormControlType() const {
return input_type_names::kSearch;
}
diff --git a/chromium/third_party/blink/renderer/core/html/forms/search_input_type.h b/chromium/third_party/blink/renderer/core/html/forms/search_input_type.h
index 34a6f6b89df..dd1ff43512a 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/search_input_type.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/search_input_type.h
@@ -42,8 +42,6 @@ class SearchInputType final : public BaseTextInputType {
private:
void CountUsage() override;
- LayoutObject* CreateLayoutObject(const ComputedStyle&,
- LegacyLayout) const override;
const AtomicString& FormControlType() const override;
bool NeedsContainer() const override;
void CreateShadowSubtree() override;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/select_type.cc b/chromium/third_party/blink/renderer/core/html/forms/select_type.cc
new file mode 100644
index 00000000000..345cbeb2682
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/html/forms/select_type.cc
@@ -0,0 +1,1252 @@
+/*
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2001 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2011 Apple Inc. All rights
+ * reserved.
+ * (C) 2006 Alexey Proskuryakov (ap@nypop.com)
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ * Copyright (C) 2009 Torch Mobile Inc. All rights reserved.
+ * (http://www.torchmobile.com/)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "third_party/blink/renderer/core/html/forms/select_type.h"
+
+#include "build/build_config.h"
+#include "third_party/blink/public/strings/grit/blink_strings.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_mutation_observer_init.h"
+#include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
+#include "third_party/blink/renderer/core/dom/mutation_observer.h"
+#include "third_party/blink/renderer/core/dom/mutation_record.h"
+#include "third_party/blink/renderer/core/dom/node_computed_style.h"
+#include "third_party/blink/renderer/core/events/gesture_event.h"
+#include "third_party/blink/renderer/core/events/keyboard_event.h"
+#include "third_party/blink/renderer/core/events/mouse_event.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/html/forms/html_form_element.h"
+#include "third_party/blink/renderer/core/html/forms/html_select_element.h"
+#include "third_party/blink/renderer/core/html/forms/popup_menu.h"
+#include "third_party/blink/renderer/core/input/event_handler.h"
+#include "third_party/blink/renderer/core/input/input_device_capabilities.h"
+#include "third_party/blink/renderer/core/layout/layout_box.h"
+#include "third_party/blink/renderer/core/page/autoscroll_controller.h"
+#include "third_party/blink/renderer/core/page/chrome_client.h"
+#include "third_party/blink/renderer/core/page/page.h"
+#include "third_party/blink/renderer/core/page/spatial_navigation.h"
+#include "third_party/blink/renderer/core/paint/paint_layer.h"
+#include "third_party/blink/renderer/platform/text/platform_locale.h"
+#include "ui/base/ui_base_features.h"
+
+namespace blink {
+
+class PopupUpdater;
+
+namespace {
+
+HTMLOptionElement* EventTargetOption(const Event& event) {
+ return DynamicTo<HTMLOptionElement>(event.target()->ToNode());
+}
+
+} // anonymous namespace
+
+class MenuListSelectType final : public SelectType {
+ public:
+ explicit MenuListSelectType(HTMLSelectElement& select) : SelectType(select) {}
+ void Trace(Visitor* visitor) override;
+
+ bool DefaultEventHandler(const Event& event) override;
+ void DidSelectOption(HTMLOptionElement* element,
+ HTMLSelectElement::SelectOptionFlags flags,
+ bool should_update_popup) override;
+ void DidBlur() override;
+ void DidDetachLayoutTree() override;
+ void DidRecalcStyle(const StyleRecalcChange change) override;
+ void DidSetSuggestedOption(HTMLOptionElement* option) override;
+ void SaveLastSelection() override;
+
+ void UpdateTextStyle() override { UpdateTextStyleInternal(); }
+ void UpdateTextStyleAndContent() override;
+ HTMLOptionElement* OptionToBeShown() const override;
+ const ComputedStyle* OptionStyle() const override {
+ return option_style_.get();
+ }
+ void MaximumOptionWidthMightBeChanged() const override;
+
+ void ShowPopup() override;
+ void HidePopup() override;
+ void PopupDidHide() override;
+ bool PopupIsVisible() const override;
+ PopupMenu* PopupForTesting() const override;
+
+ void DidMutateSubtree();
+
+ private:
+ bool ShouldOpenPopupForKeyDownEvent(const KeyboardEvent& event);
+ bool ShouldOpenPopupForKeyPressEvent(const KeyboardEvent& event);
+ // Returns true if this function handled the event.
+ bool HandlePopupOpenKeyboardEvent();
+ void SetPopupIsVisible(bool popup_is_visible);
+ void DispatchEventsIfSelectedOptionChanged();
+ String UpdateTextStyleInternal();
+ void DidUpdateActiveOption(HTMLOptionElement* option);
+ void ObserveTreeMutation();
+ void UnobserveTreeMutation();
+
+ Member<PopupMenu> popup_;
+ Member<PopupUpdater> popup_updater_;
+ scoped_refptr<const ComputedStyle> option_style_;
+ int ax_menulist_last_active_index_ = -1;
+ bool has_updated_menulist_active_option_ = false;
+ bool popup_is_visible_ = false;
+ bool snav_arrow_key_selection_ = false;
+};
+
+void MenuListSelectType::Trace(Visitor* visitor) {
+ visitor->Trace(popup_);
+ visitor->Trace(popup_updater_);
+ SelectType::Trace(visitor);
+}
+
+bool MenuListSelectType::DefaultEventHandler(const Event& event) {
+ // We need to make the layout tree up-to-date to have GetLayoutObject() give
+ // the correct result below. An author event handler may have set display to
+ // some element to none which will cause a layout tree detach.
+ select_->GetDocument().UpdateStyleAndLayoutTree();
+
+ const auto* key_event = DynamicTo<KeyboardEvent>(event);
+ if (event.type() == event_type_names::kKeydown) {
+ if (!select_->GetLayoutObject() || !key_event)
+ return false;
+
+ if (ShouldOpenPopupForKeyDownEvent(*key_event))
+ return HandlePopupOpenKeyboardEvent();
+
+ // When using spatial navigation, we want to be able to navigate away
+ // from the select element when the user hits any of the arrow keys,
+ // instead of changing the selection.
+ if (IsSpatialNavigationEnabled(select_->GetDocument().GetFrame())) {
+ if (!snav_arrow_key_selection_)
+ return false;
+ }
+
+ // The key handling below shouldn't be used for non spatial navigation
+ // mode Mac
+ if (LayoutTheme::GetTheme().PopsMenuByArrowKeys() &&
+ !IsSpatialNavigationEnabled(select_->GetDocument().GetFrame()))
+ return false;
+
+ int ignore_modifiers = WebInputEvent::kShiftKey |
+ WebInputEvent::kControlKey | WebInputEvent::kAltKey |
+ WebInputEvent::kMetaKey;
+ if (key_event->GetModifiers() & ignore_modifiers)
+ return false;
+
+ const String& key = key_event->key();
+ bool handled = true;
+ HTMLOptionElement* option = select_->SelectedOption();
+ int list_index = option ? option->ListIndex() : -1;
+
+ if (key == "ArrowDown" || key == "ArrowRight") {
+ option = NextValidOption(list_index, kSkipForwards, 1);
+ } else if (key == "ArrowUp" || key == "ArrowLeft") {
+ option = NextValidOption(list_index, kSkipBackwards, 1);
+ } else if (key == "PageDown") {
+ option = NextValidOption(list_index, kSkipForwards, 3);
+ } else if (key == "PageUp") {
+ option = NextValidOption(list_index, kSkipBackwards, 3);
+ } else if (key == "Home") {
+ option = FirstSelectableOption();
+ } else if (key == "End") {
+ option = LastSelectableOption();
+ } else {
+ handled = false;
+ }
+
+ if (handled && option) {
+ select_->SelectOption(
+ option, HTMLSelectElement::kDeselectOtherOptionsFlag |
+ HTMLSelectElement::kMakeOptionDirtyFlag |
+ HTMLSelectElement::kDispatchInputAndChangeEventFlag);
+ }
+ return handled;
+ }
+
+ if (event.type() == event_type_names::kKeypress) {
+ if (!select_->GetLayoutObject() || !key_event)
+ return false;
+
+ int key_code = key_event->keyCode();
+ if (key_code == ' ' &&
+ IsSpatialNavigationEnabled(select_->GetDocument().GetFrame())) {
+ // Use space to toggle arrow key handling for selection change or
+ // spatial navigation.
+ snav_arrow_key_selection_ = !snav_arrow_key_selection_;
+ return true;
+ }
+
+ if (ShouldOpenPopupForKeyPressEvent(*key_event))
+ return HandlePopupOpenKeyboardEvent();
+
+ if (!LayoutTheme::GetTheme().PopsMenuByReturnKey() && key_code == '\r') {
+ if (HTMLFormElement* form = select_->Form())
+ form->SubmitImplicitly(event, false);
+ DispatchEventsIfSelectedOptionChanged();
+ return true;
+ }
+ return false;
+ }
+
+ const auto* mouse_event = DynamicTo<MouseEvent>(event);
+ if (event.type() == event_type_names::kMousedown && mouse_event &&
+ mouse_event->button() ==
+ static_cast<int16_t>(WebPointerProperties::Button::kLeft)) {
+ InputDeviceCapabilities* source_capabilities =
+ select_->GetDocument()
+ .domWindow()
+ ->GetInputDeviceCapabilities()
+ ->FiresTouchEvents(mouse_event->FromTouch());
+ select_->focus(FocusParams(SelectionBehaviorOnFocus::kRestore,
+ mojom::blink::FocusType::kNone,
+ source_capabilities));
+ if (select_->GetLayoutObject() && !will_be_destroyed_ &&
+ !select_->IsDisabledFormControl()) {
+ if (PopupIsVisible()) {
+ HidePopup();
+ } else {
+ // Save the selection so it can be compared to the new selection
+ // when we call onChange during selectOption, which gets called
+ // from selectOptionByPopup, which gets called after the user
+ // makes a selection from the menu.
+ SaveLastSelection();
+ // TODO(lanwei): Will check if we need to add
+ // InputDeviceCapabilities here when select menu list gets
+ // focus, see https://crbug.com/476530.
+ ShowPopup();
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+bool MenuListSelectType::ShouldOpenPopupForKeyDownEvent(
+ const KeyboardEvent& event) {
+ const String& key = event.key();
+ LayoutTheme& layout_theme = LayoutTheme::GetTheme();
+
+ if (IsSpatialNavigationEnabled(select_->GetDocument().GetFrame()))
+ return false;
+
+ return ((layout_theme.PopsMenuByArrowKeys() &&
+ (key == "ArrowDown" || key == "ArrowUp")) ||
+ (layout_theme.PopsMenuByAltDownUpOrF4Key() &&
+ (key == "ArrowDown" || key == "ArrowUp") && event.altKey()) ||
+ (layout_theme.PopsMenuByAltDownUpOrF4Key() &&
+ (!event.altKey() && !event.ctrlKey() && key == "F4")));
+}
+
+bool MenuListSelectType::ShouldOpenPopupForKeyPressEvent(
+ const KeyboardEvent& event) {
+ LayoutTheme& layout_theme = LayoutTheme::GetTheme();
+ int key_code = event.keyCode();
+
+ return ((layout_theme.PopsMenuBySpaceKey() && key_code == ' ' &&
+ !select_->type_ahead_.HasActiveSession(event)) ||
+ (layout_theme.PopsMenuByReturnKey() && key_code == '\r'));
+}
+
+bool MenuListSelectType::HandlePopupOpenKeyboardEvent() {
+ select_->focus();
+ // Calling focus() may cause us to lose our LayoutObject. Return true so
+ // that our caller doesn't process the event further, but don't set
+ // the event as handled.
+ if (!select_->GetLayoutObject() || will_be_destroyed_ ||
+ select_->IsDisabledFormControl())
+ return false;
+ // Save the selection so it can be compared to the new selection when
+ // dispatching change events during SelectOption, which gets called from
+ // SelectOptionByPopup, which gets called after the user makes a selection
+ // from the menu.
+ SaveLastSelection();
+ ShowPopup();
+ return true;
+}
+
+void MenuListSelectType::ShowPopup() {
+ if (PopupIsVisible())
+ return;
+ Document& document = select_->GetDocument();
+ if (document.GetPage()->GetChromeClient().HasOpenedPopup())
+ return;
+ if (!select_->GetLayoutObject())
+ return;
+ if (select_->VisibleBoundsInVisualViewport().IsEmpty())
+ return;
+
+ if (!popup_) {
+ popup_ = document.GetPage()->GetChromeClient().OpenPopupMenu(
+ *document.GetFrame(), *select_);
+ }
+ if (!popup_)
+ return;
+
+ SetPopupIsVisible(true);
+ ObserveTreeMutation();
+
+ popup_->Show();
+ if (AXObjectCache* cache = document.ExistingAXObjectCache())
+ cache->DidShowMenuListPopup(select_->GetLayoutObject());
+}
+
+void MenuListSelectType::HidePopup() {
+ if (popup_)
+ popup_->Hide();
+}
+
+void MenuListSelectType::PopupDidHide() {
+ SetPopupIsVisible(false);
+ UnobserveTreeMutation();
+ if (AXObjectCache* cache = select_->GetDocument().ExistingAXObjectCache()) {
+ if (auto* layout_object = select_->GetLayoutObject())
+ cache->DidHideMenuListPopup(layout_object);
+ }
+}
+
+bool MenuListSelectType::PopupIsVisible() const {
+ return popup_is_visible_;
+}
+
+void MenuListSelectType::SetPopupIsVisible(bool popup_is_visible) {
+ popup_is_visible_ = popup_is_visible;
+ if (!::features::IsFormControlsRefreshEnabled())
+ return;
+ if (auto* layout_object = select_->GetLayoutObject()) {
+ // Invalidate paint to ensure that the focus ring is updated.
+ layout_object->SetShouldDoFullPaintInvalidation();
+ }
+}
+
+PopupMenu* MenuListSelectType::PopupForTesting() const {
+ return popup_.Get();
+}
+
+void MenuListSelectType::DidSelectOption(
+ HTMLOptionElement* element,
+ HTMLSelectElement::SelectOptionFlags flags,
+ bool should_update_popup) {
+ // Need to update last_on_change_option_ before UpdateFromElement().
+ const bool should_dispatch_events =
+ (flags & HTMLSelectElement::kDispatchInputAndChangeEventFlag) &&
+ select_->last_on_change_option_ != element;
+ select_->last_on_change_option_ = element;
+
+ UpdateTextStyleAndContent();
+ // PopupMenu::UpdateFromElement() posts an O(N) task.
+ if (PopupIsVisible() && should_update_popup)
+ popup_->UpdateFromElement(PopupMenu::kBySelectionChange);
+
+ SelectType::DidSelectOption(element, flags, should_update_popup);
+
+ if (should_dispatch_events) {
+ select_->DispatchInputEvent();
+ select_->DispatchChangeEvent();
+ }
+ if (select_->GetLayoutObject()) {
+ // Need to check will_be_destroyed_ because event handlers might
+ // disassociate |this| and select_.
+ if (!will_be_destroyed_) {
+ // DidUpdateActiveOption() is O(N) because of HTMLOptionElement::index().
+ DidUpdateActiveOption(element);
+ }
+ }
+}
+
+void MenuListSelectType::DispatchEventsIfSelectedOptionChanged() {
+ HTMLOptionElement* selected_option = select_->SelectedOption();
+ if (select_->last_on_change_option_.Get() != selected_option) {
+ select_->last_on_change_option_ = selected_option;
+ select_->DispatchInputEvent();
+ select_->DispatchChangeEvent();
+ }
+}
+
+void MenuListSelectType::DidBlur() {
+ // We only need to fire change events here for menu lists, because we fire
+ // change events for list boxes whenever the selection change is actually
+ // made. This matches other browsers' behavior.
+ DispatchEventsIfSelectedOptionChanged();
+ if (PopupIsVisible())
+ HidePopup();
+}
+
+void MenuListSelectType::DidSetSuggestedOption(HTMLOptionElement*) {
+ UpdateTextStyleAndContent();
+ if (PopupIsVisible())
+ popup_->UpdateFromElement(PopupMenu::kBySelectionChange);
+}
+
+void MenuListSelectType::SaveLastSelection() {
+ select_->last_on_change_option_ = select_->SelectedOption();
+}
+
+void MenuListSelectType::DidDetachLayoutTree() {
+ if (popup_)
+ popup_->DisconnectClient();
+ SetPopupIsVisible(false);
+ popup_ = nullptr;
+ UnobserveTreeMutation();
+}
+
+void MenuListSelectType::DidRecalcStyle(const StyleRecalcChange change) {
+ if (change.ReattachLayoutTree())
+ return;
+ UpdateTextStyle();
+ if (PopupIsVisible())
+ popup_->UpdateFromElement(PopupMenu::kByStyleChange);
+}
+
+String MenuListSelectType::UpdateTextStyleInternal() {
+ HTMLOptionElement* option = OptionToBeShown();
+ String text = g_empty_string;
+ const ComputedStyle* option_style = nullptr;
+
+ if (select_->IsMultiple()) {
+ unsigned selected_count = 0;
+ HTMLOptionElement* selected_option_element = nullptr;
+ for (auto* const option : select_->GetOptionList()) {
+ if (option->Selected()) {
+ if (++selected_count == 1)
+ selected_option_element = option;
+ }
+ }
+
+ if (selected_count == 1) {
+ text = selected_option_element->TextIndentedToRespectGroupLabel();
+ option_style = selected_option_element->GetComputedStyle();
+ } else {
+ Locale& locale = select_->GetLocale();
+ String localized_number_string =
+ locale.ConvertToLocalizedNumber(String::Number(selected_count));
+ text = locale.QueryString(IDS_FORM_SELECT_MENU_LIST_TEXT,
+ localized_number_string);
+ DCHECK(!option_style);
+ }
+ } else {
+ if (option) {
+ text = option->TextIndentedToRespectGroupLabel();
+ option_style = option->GetComputedStyle();
+ }
+ }
+ option_style_ = option_style;
+
+ auto& inner_element = select_->InnerElement();
+ const ComputedStyle* inner_style = inner_element.GetComputedStyle();
+ if (inner_style && option_style &&
+ ((option_style->Direction() != inner_style->Direction() ||
+ option_style->GetUnicodeBidi() != inner_style->GetUnicodeBidi()))) {
+ scoped_refptr<ComputedStyle> cloned_style =
+ ComputedStyle::Clone(*inner_style);
+ cloned_style->SetDirection(option_style->Direction());
+ cloned_style->SetUnicodeBidi(option_style->GetUnicodeBidi());
+ if (auto* inner_layout = inner_element.GetLayoutObject()) {
+ inner_layout->SetModifiedStyleOutsideStyleRecalc(
+ std::move(cloned_style), LayoutObject::ApplyStyleChanges::kYes);
+ } else {
+ inner_element.SetComputedStyle(std::move(cloned_style));
+ }
+ }
+ if (select_->GetLayoutObject())
+ DidUpdateActiveOption(option);
+
+ return text.StripWhiteSpace();
+}
+
+void MenuListSelectType::UpdateTextStyleAndContent() {
+ select_->InnerElement().firstChild()->setNodeValue(UpdateTextStyleInternal());
+ if (auto* box = select_->GetLayoutBox()) {
+ if (auto* cache = select_->GetDocument().ExistingAXObjectCache())
+ cache->TextChanged(box);
+ }
+}
+
+void MenuListSelectType::DidUpdateActiveOption(HTMLOptionElement* option) {
+ Document& document = select_->GetDocument();
+ if (!document.ExistingAXObjectCache())
+ return;
+
+ int option_index = option ? option->index() : -1;
+ if (ax_menulist_last_active_index_ == option_index)
+ return;
+ ax_menulist_last_active_index_ = option_index;
+
+ // We skip sending accessiblity notifications for the very first option,
+ // otherwise we get extra focus and select events that are undesired.
+ if (!has_updated_menulist_active_option_) {
+ has_updated_menulist_active_option_ = true;
+ return;
+ }
+
+ document.ExistingAXObjectCache()->HandleUpdateActiveMenuOption(
+ select_->GetLayoutObject(), option_index);
+}
+
+HTMLOptionElement* MenuListSelectType::OptionToBeShown() const {
+ if (auto* option =
+ select_->OptionAtListIndex(select_->index_to_select_on_cancel_))
+ return option;
+ if (select_->suggested_option_)
+ return select_->suggested_option_;
+ // TODO(tkent): We should not call OptionToBeShown() in IsMultiple() case.
+ if (select_->IsMultiple())
+ return select_->SelectedOption();
+ DCHECK_EQ(select_->SelectedOption(), select_->last_on_change_option_);
+ return select_->last_on_change_option_;
+}
+
+void MenuListSelectType::MaximumOptionWidthMightBeChanged() const {
+ if (LayoutObject* layout_object = select_->GetLayoutObject()) {
+ layout_object->SetNeedsLayoutAndIntrinsicWidthsRecalc(
+ layout_invalidation_reason::kMenuOptionsChanged);
+ }
+}
+
+// PopupUpdater notifies updates of the specified SELECT element subtree to
+// a PopupMenu object.
+class PopupUpdater : public MutationObserver::Delegate {
+ public:
+ explicit PopupUpdater(MenuListSelectType& select_type,
+ HTMLSelectElement& select)
+ : select_type_(select_type),
+ select_(select),
+ observer_(MutationObserver::Create(this)) {
+ MutationObserverInit* init = MutationObserverInit::Create();
+ init->setAttributeOldValue(true);
+ init->setAttributes(true);
+ // Observe only attributes which affect popup content.
+ init->setAttributeFilter({"disabled", "label", "selected", "value"});
+ init->setCharacterData(true);
+ init->setCharacterDataOldValue(true);
+ init->setChildList(true);
+ init->setSubtree(true);
+ observer_->observe(select_, init, ASSERT_NO_EXCEPTION);
+ }
+
+ ExecutionContext* GetExecutionContext() const override {
+ return select_->GetExecutionContext();
+ }
+
+ void Deliver(const MutationRecordVector& records,
+ MutationObserver&) override {
+ // We disconnect the MutationObserver when a popup is closed. However
+ // MutationObserver can call back after disconnection.
+ if (!select_type_->PopupIsVisible())
+ return;
+ for (const auto& record : records) {
+ if (record->type() == "attributes") {
+ const auto& element = *To<Element>(record->target());
+ if (record->oldValue() == element.getAttribute(record->attributeName()))
+ continue;
+ } else if (record->type() == "characterData") {
+ if (record->oldValue() == record->target()->nodeValue())
+ continue;
+ }
+ select_type_->DidMutateSubtree();
+ return;
+ }
+ }
+
+ void Dispose() { observer_->disconnect(); }
+
+ void Trace(Visitor* visitor) override {
+ visitor->Trace(select_type_);
+ visitor->Trace(select_);
+ visitor->Trace(observer_);
+ MutationObserver::Delegate::Trace(visitor);
+ }
+
+ private:
+ Member<MenuListSelectType> select_type_;
+ Member<HTMLSelectElement> select_;
+ Member<MutationObserver> observer_;
+};
+
+void MenuListSelectType::ObserveTreeMutation() {
+ DCHECK(!popup_updater_);
+ popup_updater_ = MakeGarbageCollected<PopupUpdater>(*this, *select_);
+}
+
+void MenuListSelectType::UnobserveTreeMutation() {
+ if (!popup_updater_)
+ return;
+ popup_updater_->Dispose();
+ popup_updater_ = nullptr;
+}
+
+void MenuListSelectType::DidMutateSubtree() {
+ DCHECK(PopupIsVisible());
+ DCHECK(popup_);
+ popup_->UpdateFromElement(PopupMenu::kByDOMChange);
+}
+
+// ============================================================================
+
+class ListBoxSelectType final : public SelectType {
+ public:
+ explicit ListBoxSelectType(HTMLSelectElement& select) : SelectType(select) {}
+ bool DefaultEventHandler(const Event& event) override;
+ void DidBlur() override;
+ void DidSetSuggestedOption(HTMLOptionElement* option) override;
+ void SaveLastSelection() override;
+ void SelectAll() override;
+ void SaveListboxActiveSelection() override;
+ void HandleMouseRelease() override;
+ void ListBoxOnChange() override;
+ void ClearLastOnChangeSelection() override;
+
+ private:
+ HTMLOptionElement* NextSelectableOptionPageAway(HTMLOptionElement*,
+ SkipDirection) const;
+ // Update :-internal-multi-select-focus state of selected OPTIONs.
+ void UpdateMultiSelectFocus();
+ void ToggleSelection(HTMLOptionElement& option);
+ enum class SelectionMode {
+ kDeselectOthers,
+ kRange,
+ kNotChangeOthers,
+ };
+ void UpdateSelectedState(HTMLOptionElement* clicked_option,
+ SelectionMode mode);
+ void UpdateListBoxSelection(bool deselect_other_options, bool scroll = true);
+
+ Vector<bool> cached_state_for_active_selection_;
+ Vector<bool> last_on_change_selection_;
+ bool is_in_non_contiguous_selection_ = false;
+ bool active_selection_state_ = false;
+};
+
+bool ListBoxSelectType::DefaultEventHandler(const Event& event) {
+ const auto* mouse_event = DynamicTo<MouseEvent>(event);
+ const auto* gesture_event = DynamicTo<GestureEvent>(event);
+ if (event.type() == event_type_names::kGesturetap && gesture_event) {
+ select_->focus();
+ // Calling focus() may cause us to lose our layoutObject or change the
+ // layoutObject type, in which case do not want to handle the event.
+ if (!select_->GetLayoutObject() || will_be_destroyed_)
+ return false;
+
+ // Convert to coords relative to the list box if needed.
+ if (HTMLOptionElement* option = EventTargetOption(*gesture_event)) {
+ if (!select_->IsDisabledFormControl()) {
+ UpdateSelectedState(option, gesture_event->shiftKey()
+ ? SelectionMode::kRange
+ : SelectionMode::kNotChangeOthers);
+ ListBoxOnChange();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ if (event.type() == event_type_names::kMousedown && mouse_event &&
+ mouse_event->button() ==
+ static_cast<int16_t>(WebPointerProperties::Button::kLeft)) {
+ select_->focus();
+ // Calling focus() may cause us to lose our layoutObject, in which case
+ // do not want to handle the event.
+ if (!select_->GetLayoutObject() || will_be_destroyed_ ||
+ select_->IsDisabledFormControl())
+ return false;
+
+ // Convert to coords relative to the list box if needed.
+ if (HTMLOptionElement* option = EventTargetOption(*mouse_event)) {
+ if (!option->IsDisabledFormControl()) {
+#if defined(OS_MACOSX)
+ const bool meta_or_ctrl = mouse_event->metaKey();
+#else
+ const bool meta_or_ctrl = mouse_event->ctrlKey();
+#endif
+ UpdateSelectedState(option, mouse_event->shiftKey()
+ ? SelectionMode::kRange
+ : meta_or_ctrl
+ ? SelectionMode::kNotChangeOthers
+ : SelectionMode::kDeselectOthers);
+ }
+ if (LocalFrame* frame = select_->GetDocument().GetFrame())
+ frame->GetEventHandler().SetMouseDownMayStartAutoscroll();
+
+ return true;
+ }
+ return false;
+ }
+
+ if (event.type() == event_type_names::kMousemove && mouse_event) {
+ if (mouse_event->button() !=
+ static_cast<int16_t>(WebPointerProperties::Button::kLeft) ||
+ !mouse_event->ButtonDown())
+ return false;
+
+ if (auto* layout_object = select_->GetLayoutObject()) {
+ layout_object->GetFrameView()->UpdateAllLifecyclePhasesExceptPaint(
+ DocumentUpdateReason::kScroll);
+
+ if (Page* page = select_->GetDocument().GetPage()) {
+ page->GetAutoscrollController().StartAutoscrollForSelection(
+ layout_object);
+ }
+ }
+ // Mousedown didn't happen in this element.
+ if (last_on_change_selection_.IsEmpty())
+ return false;
+
+ if (HTMLOptionElement* option = EventTargetOption(*mouse_event)) {
+ if (!select_->IsDisabledFormControl()) {
+ if (select_->is_multiple_) {
+ // Only extend selection if there is something selected.
+ if (!select_->active_selection_anchor_)
+ return false;
+
+ select_->SetActiveSelectionEnd(option);
+ UpdateListBoxSelection(false);
+ } else {
+ select_->SetActiveSelectionAnchor(option);
+ select_->SetActiveSelectionEnd(option);
+ UpdateListBoxSelection(true);
+ }
+ }
+ }
+ return false;
+ }
+
+ if (event.type() == event_type_names::kMouseup && mouse_event &&
+ mouse_event->button() ==
+ static_cast<int16_t>(WebPointerProperties::Button::kLeft) &&
+ select_->GetLayoutObject()) {
+ auto* page = select_->GetDocument().GetPage();
+ if (page && page->GetAutoscrollController().AutoscrollInProgressFor(
+ select_->GetLayoutBox()))
+ page->GetAutoscrollController().StopAutoscroll();
+ else
+ HandleMouseRelease();
+ return false;
+ }
+
+ if (event.type() == event_type_names::kKeydown) {
+ const auto* keyboard_event = DynamicTo<KeyboardEvent>(event);
+ if (!keyboard_event)
+ return false;
+ const String& key = keyboard_event->key();
+
+ bool handled = false;
+ HTMLOptionElement* end_option = nullptr;
+ if (!select_->active_selection_end_) {
+ // Initialize the end index
+ if (key == "ArrowDown" || key == "PageDown") {
+ HTMLOptionElement* start_option = select_->LastSelectedOption();
+ handled = true;
+ if (key == "ArrowDown") {
+ end_option = NextSelectableOption(start_option);
+ } else {
+ end_option =
+ NextSelectableOptionPageAway(start_option, kSkipForwards);
+ }
+ } else if (key == "ArrowUp" || key == "PageUp") {
+ HTMLOptionElement* start_option = select_->SelectedOption();
+ handled = true;
+ if (key == "ArrowUp") {
+ end_option = PreviousSelectableOption(start_option);
+ } else {
+ end_option =
+ NextSelectableOptionPageAway(start_option, kSkipBackwards);
+ }
+ }
+ } else {
+ // Set the end index based on the current end index.
+ if (key == "ArrowDown") {
+ end_option = NextSelectableOption(select_->active_selection_end_.Get());
+ handled = true;
+ } else if (key == "ArrowUp") {
+ end_option =
+ PreviousSelectableOption(select_->active_selection_end_.Get());
+ handled = true;
+ } else if (key == "PageDown") {
+ end_option = NextSelectableOptionPageAway(
+ select_->active_selection_end_.Get(), kSkipForwards);
+ handled = true;
+ } else if (key == "PageUp") {
+ end_option = NextSelectableOptionPageAway(
+ select_->active_selection_end_.Get(), kSkipBackwards);
+ handled = true;
+ }
+ }
+ if (key == "Home") {
+ end_option = FirstSelectableOption();
+ handled = true;
+ } else if (key == "End") {
+ end_option = LastSelectableOption();
+ handled = true;
+ }
+
+ if (IsSpatialNavigationEnabled(select_->GetDocument().GetFrame())) {
+ // Check if the selection moves to the boundary.
+ if (key == "ArrowLeft" || key == "ArrowRight" ||
+ ((key == "ArrowDown" || key == "ArrowUp") &&
+ end_option == select_->active_selection_end_))
+ return false;
+ }
+
+ bool is_control_key = false;
+#if defined(OS_MACOSX)
+ is_control_key = keyboard_event->metaKey();
+#else
+ is_control_key = keyboard_event->ctrlKey();
+#endif
+
+ if (select_->is_multiple_ && keyboard_event->keyCode() == ' ' &&
+ is_control_key && select_->active_selection_end_) {
+ // Use ctrl+space to toggle selection change.
+ ToggleSelection(*select_->active_selection_end_);
+ return true;
+ }
+
+ if (end_option && handled) {
+ // Save the selection so it can be compared to the new selection
+ // when dispatching change events immediately after making the new
+ // selection.
+ SaveLastSelection();
+
+ select_->SetActiveSelectionEnd(end_option);
+
+ is_in_non_contiguous_selection_ = select_->is_multiple_ && is_control_key;
+ bool select_new_item =
+ !select_->is_multiple_ || keyboard_event->shiftKey() ||
+ (!IsSpatialNavigationEnabled(select_->GetDocument().GetFrame()) &&
+ !is_in_non_contiguous_selection_);
+ if (select_new_item)
+ active_selection_state_ = true;
+ // If the anchor is uninitialized, or if we're going to deselect all
+ // other options, then set the anchor index equal to the end index.
+ bool deselect_others = !select_->is_multiple_ ||
+ (!keyboard_event->shiftKey() && select_new_item);
+ if (!select_->active_selection_anchor_ || deselect_others) {
+ if (deselect_others)
+ select_->DeselectItemsWithoutValidation();
+ select_->SetActiveSelectionAnchor(select_->active_selection_end_.Get());
+ }
+
+ select_->ScrollToOption(end_option);
+ if (select_new_item || is_in_non_contiguous_selection_) {
+ if (select_new_item) {
+ UpdateListBoxSelection(deselect_others);
+ ListBoxOnChange();
+ }
+ UpdateMultiSelectFocus();
+ } else {
+ select_->ScrollToSelection();
+ }
+
+ return true;
+ }
+ return false;
+ }
+
+ if (event.type() == event_type_names::kKeypress) {
+ auto* keyboard_event = DynamicTo<KeyboardEvent>(event);
+ if (!keyboard_event)
+ return false;
+ int key_code = keyboard_event->keyCode();
+
+ if (key_code == '\r') {
+ if (HTMLFormElement* form = select_->Form())
+ form->SubmitImplicitly(event, false);
+ return true;
+ } else if (select_->is_multiple_ && key_code == ' ' &&
+ (IsSpatialNavigationEnabled(select_->GetDocument().GetFrame()) ||
+ is_in_non_contiguous_selection_)) {
+ HTMLOptionElement* option = select_->active_selection_end_;
+ // If there's no active selection,
+ // act as if "ArrowDown" had been pressed.
+ if (!option)
+ option = NextSelectableOption(select_->LastSelectedOption());
+ if (option) {
+ // Use space to toggle selection change.
+ ToggleSelection(*option);
+ return true;
+ }
+ }
+ return false;
+ }
+ return false;
+}
+
+void ListBoxSelectType::DidBlur() {
+ ClearLastOnChangeSelection();
+}
+
+void ListBoxSelectType::DidSetSuggestedOption(HTMLOptionElement* option) {
+ if (select_->GetLayoutObject())
+ select_->ScrollToOption(option);
+}
+
+void ListBoxSelectType::SaveLastSelection() {
+ last_on_change_selection_.clear();
+ for (auto& element : select_->GetListItems()) {
+ auto* option_element = DynamicTo<HTMLOptionElement>(element.Get());
+ last_on_change_selection_.push_back(option_element &&
+ option_element->Selected());
+ }
+}
+
+void ListBoxSelectType::UpdateMultiSelectFocus() {
+ if (!select_->is_multiple_)
+ return;
+
+ for (auto* const option : select_->GetOptionList()) {
+ if (option->IsDisabledFormControl() || !option->GetLayoutObject())
+ continue;
+ bool is_focused = (option == select_->active_selection_end_) &&
+ is_in_non_contiguous_selection_;
+ option->SetMultiSelectFocusedState(is_focused);
+ }
+ select_->ScrollToSelection();
+}
+
+void ListBoxSelectType::SelectAll() {
+ if (!select_->GetLayoutObject() || !select_->is_multiple_)
+ return;
+
+ // Save the selection so it can be compared to the new selectAll selection
+ // when dispatching change events.
+ SaveLastSelection();
+
+ active_selection_state_ = true;
+ select_->SetActiveSelectionAnchor(NextSelectableOption(nullptr));
+ select_->SetActiveSelectionEnd(PreviousSelectableOption(nullptr));
+
+ UpdateListBoxSelection(false, false);
+ ListBoxOnChange();
+ select_->SetNeedsValidityCheck();
+}
+
+// Returns the index of the next valid item one page away from |start_option|
+// in direction |direction|.
+HTMLOptionElement* ListBoxSelectType::NextSelectableOptionPageAway(
+ HTMLOptionElement* start_option,
+ SkipDirection direction) const {
+ const auto& items = select_->GetListItems();
+ // -1 so we still show context.
+ int page_size = select_->ListBoxSize() - 1;
+
+ // One page away, but not outside valid bounds.
+ // If there is a valid option item one page away, the index is chosen.
+ // If there is no exact one page away valid option, returns start_index or
+ // the most far index.
+ int start_index = start_option ? start_option->ListIndex() : -1;
+ int edge_index = (direction == kSkipForwards) ? 0 : (items.size() - 1);
+ int skip_amount =
+ page_size +
+ ((direction == kSkipForwards) ? start_index : (edge_index - start_index));
+ return NextValidOption(edge_index, direction, skip_amount);
+}
+
+void ListBoxSelectType::ToggleSelection(HTMLOptionElement& option) {
+ active_selection_state_ = !active_selection_state_;
+ UpdateSelectedState(&option, SelectionMode::kNotChangeOthers);
+ ListBoxOnChange();
+}
+
+void ListBoxSelectType::UpdateSelectedState(HTMLOptionElement* clicked_option,
+ SelectionMode mode) {
+ DCHECK(clicked_option);
+ // Save the selection so it can be compared to the new selection when
+ // dispatching change events during mouseup, or after autoscroll finishes.
+ SaveLastSelection();
+
+ active_selection_state_ = true;
+
+ if (!select_->is_multiple_)
+ mode = SelectionMode::kDeselectOthers;
+
+ // Keep track of whether an active selection (like during drag selection),
+ // should select or deselect.
+ if (clicked_option->Selected() && mode == SelectionMode::kNotChangeOthers) {
+ active_selection_state_ = false;
+ clicked_option->SetSelectedState(false);
+ clicked_option->SetDirty(true);
+ }
+
+ // If we're not in any special multiple selection mode, then deselect all
+ // other items, excluding the clicked OPTION. If no option was clicked, then
+ // this will deselect all items in the list.
+ if (mode == SelectionMode::kDeselectOthers)
+ select_->DeselectItemsWithoutValidation(clicked_option);
+
+ // If the anchor hasn't been set, and we're doing kDeselectOthers or kRange,
+ // then initialize the anchor to the first selected OPTION.
+ if (!select_->active_selection_anchor_ &&
+ mode != SelectionMode::kNotChangeOthers)
+ select_->SetActiveSelectionAnchor(select_->SelectedOption());
+
+ // Set the selection state of the clicked OPTION.
+ if (!clicked_option->IsDisabledFormControl()) {
+ clicked_option->SetSelectedState(true);
+ clicked_option->SetDirty(true);
+ }
+
+ // If there was no selectedIndex() for the previous initialization, or if
+ // we're doing kDeselectOthers, or kNotChangeOthers (using cmd or ctrl),
+ // then initialize the anchor OPTION to the clicked OPTION.
+ if (!select_->active_selection_anchor_ || mode != SelectionMode::kRange)
+ select_->SetActiveSelectionAnchor(clicked_option);
+
+ select_->SetActiveSelectionEnd(clicked_option);
+ UpdateListBoxSelection(mode != SelectionMode::kNotChangeOthers);
+}
+
+void ListBoxSelectType::UpdateListBoxSelection(bool deselect_other_options,
+ bool scroll) {
+ DCHECK(select_->GetLayoutObject());
+ HTMLOptionElement* const anchor_option = select_->active_selection_anchor_;
+ HTMLOptionElement* const end_option = select_->active_selection_end_;
+ const int anchor_index = anchor_option ? anchor_option->index() : -1;
+ const int end_index = end_option ? end_option->index() : -1;
+ const int start = std::min(anchor_index, end_index);
+ const int end = std::max(anchor_index, end_index);
+
+ int i = 0;
+ for (auto* const option : select_->GetOptionList()) {
+ if (option->IsDisabledFormControl() || !option->GetLayoutObject()) {
+ ++i;
+ continue;
+ }
+ if (i >= start && i <= end) {
+ option->SetSelectedState(active_selection_state_);
+ option->SetDirty(true);
+ } else if (deselect_other_options ||
+ i >= static_cast<int>(
+ cached_state_for_active_selection_.size())) {
+ option->SetSelectedState(false);
+ option->SetDirty(true);
+ } else {
+ option->SetSelectedState(cached_state_for_active_selection_[i]);
+ }
+ ++i;
+ }
+
+ UpdateMultiSelectFocus();
+ select_->SetNeedsValidityCheck();
+ if (scroll)
+ select_->ScrollToSelection();
+ select_->NotifyFormStateChanged();
+}
+
+void ListBoxSelectType::SaveListboxActiveSelection() {
+ // Cache the selection state so we can restore the old selection as the new
+ // selection pivots around this anchor index.
+ // Example:
+ // 1. Press the mouse button on the second OPTION
+ // active_selection_anchor_ points the second OPTION.
+ // 2. Drag the mouse pointer onto the fifth OPTION
+ // active_selection_end_ points the fifth OPTION, OPTIONs at 1-4 indices
+ // are selected.
+ // 3. Drag the mouse pointer onto the fourth OPTION
+ // active_selection_end_ points the fourth OPTION, OPTIONs at 1-3 indices
+ // are selected.
+ // UpdateListBoxSelection needs to clear selection of the fifth OPTION.
+ cached_state_for_active_selection_.resize(0);
+ for (auto* const option : select_->GetOptionList()) {
+ cached_state_for_active_selection_.push_back(option->Selected());
+ }
+}
+
+void ListBoxSelectType::HandleMouseRelease() {
+ // We didn't start this click/drag on any options.
+ if (last_on_change_selection_.IsEmpty())
+ return;
+ ListBoxOnChange();
+}
+
+void ListBoxSelectType::ListBoxOnChange() {
+ const auto& items = select_->GetListItems();
+
+ // If the cached selection list is empty, or the size has changed, then fire
+ // 'change' event, and return early.
+ // FIXME: Why? This looks unreasonable.
+ if (last_on_change_selection_.IsEmpty() ||
+ last_on_change_selection_.size() != items.size()) {
+ select_->DispatchChangeEvent();
+ return;
+ }
+
+ // Update last_on_change_selection_ and fire a 'change' event.
+ bool fire_on_change = false;
+ for (unsigned i = 0; i < items.size(); ++i) {
+ HTMLElement* element = items[i];
+ auto* option_element = DynamicTo<HTMLOptionElement>(element);
+ bool selected = option_element && option_element->Selected();
+ if (selected != last_on_change_selection_[i])
+ fire_on_change = true;
+ last_on_change_selection_[i] = selected;
+ }
+
+ if (fire_on_change) {
+ select_->DispatchInputEvent();
+ select_->DispatchChangeEvent();
+ }
+}
+
+void ListBoxSelectType::ClearLastOnChangeSelection() {
+ last_on_change_selection_.clear();
+}
+
+// ============================================================================
+
+SelectType::SelectType(HTMLSelectElement& select) : select_(select) {}
+
+SelectType* SelectType::Create(HTMLSelectElement& select) {
+ if (select.UsesMenuList())
+ return MakeGarbageCollected<MenuListSelectType>(select);
+ else
+ return MakeGarbageCollected<ListBoxSelectType>(select);
+}
+
+void SelectType::WillBeDestroyed() {
+ will_be_destroyed_ = true;
+}
+
+void SelectType::Trace(Visitor* visitor) {
+ visitor->Trace(select_);
+}
+
+void SelectType::DidSelectOption(HTMLOptionElement*,
+ HTMLSelectElement::SelectOptionFlags,
+ bool) {
+ select_->ScrollToSelection();
+ select_->SetNeedsValidityCheck();
+}
+
+void SelectType::DidDetachLayoutTree() {}
+
+void SelectType::DidRecalcStyle(const StyleRecalcChange) {}
+
+void SelectType::UpdateTextStyle() {}
+
+void SelectType::UpdateTextStyleAndContent() {}
+
+HTMLOptionElement* SelectType::OptionToBeShown() const {
+ NOTREACHED();
+ return nullptr;
+}
+
+const ComputedStyle* SelectType::OptionStyle() const {
+ NOTREACHED();
+ return nullptr;
+}
+
+void SelectType::MaximumOptionWidthMightBeChanged() const {}
+
+void SelectType::SelectAll() {
+ NOTREACHED();
+}
+
+void SelectType::SaveListboxActiveSelection() {}
+
+void SelectType::HandleMouseRelease() {}
+
+void SelectType::ListBoxOnChange() {}
+
+void SelectType::ClearLastOnChangeSelection() {}
+
+void SelectType::ShowPopup() {
+ NOTREACHED();
+}
+
+void SelectType::HidePopup() {
+ NOTREACHED();
+}
+
+void SelectType::PopupDidHide() {
+ NOTREACHED();
+}
+
+bool SelectType::PopupIsVisible() const {
+ return false;
+}
+
+PopupMenu* SelectType::PopupForTesting() const {
+ NOTREACHED();
+ return nullptr;
+}
+
+// Returns the 1st valid OPTION |skip| items from |list_index| in direction
+// |direction| if there is one.
+// Otherwise, it returns the valid OPTION closest to that boundary which is past
+// |list_index| if there is one.
+// Otherwise, it returns nullptr.
+// Valid means that it is enabled and visible.
+HTMLOptionElement* SelectType::NextValidOption(int list_index,
+ SkipDirection direction,
+ int skip) const {
+ DCHECK(direction == kSkipBackwards || direction == kSkipForwards);
+ const auto& list_items = select_->GetListItems();
+ HTMLOptionElement* last_good_option = nullptr;
+ int size = list_items.size();
+ for (list_index += direction; list_index >= 0 && list_index < size;
+ list_index += direction) {
+ --skip;
+ HTMLElement* element = list_items[list_index];
+ auto* option_element = DynamicTo<HTMLOptionElement>(element);
+ if (!option_element)
+ continue;
+ if (option_element->IsDisplayNone())
+ continue;
+ if (element->IsDisabledFormControl())
+ continue;
+ if (!select_->UsesMenuList() && !element->GetLayoutObject())
+ continue;
+ last_good_option = option_element;
+ if (skip <= 0)
+ break;
+ }
+ return last_good_option;
+}
+
+HTMLOptionElement* SelectType::NextSelectableOption(
+ HTMLOptionElement* start_option) const {
+ return NextValidOption(start_option ? start_option->ListIndex() : -1,
+ kSkipForwards, 1);
+}
+
+HTMLOptionElement* SelectType::PreviousSelectableOption(
+ HTMLOptionElement* start_option) const {
+ return NextValidOption(
+ start_option ? start_option->ListIndex() : select_->GetListItems().size(),
+ kSkipBackwards, 1);
+}
+
+HTMLOptionElement* SelectType::FirstSelectableOption() const {
+ return NextValidOption(-1, kSkipForwards, 1);
+}
+
+HTMLOptionElement* SelectType::LastSelectableOption() const {
+ return NextValidOption(select_->GetListItems().size(), kSkipBackwards, 1);
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/select_type.h b/chromium/third_party/blink/renderer/core/html/forms/select_type.h
new file mode 100644
index 00000000000..2ce4e1bcb56
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/html/forms/select_type.h
@@ -0,0 +1,84 @@
+// Copyright 2020 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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_SELECT_TYPE_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_SELECT_TYPE_H_
+
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/html/forms/html_select_element.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+
+namespace blink {
+
+// SelectType class is an abstraction of the MenuList behavior and the ListBox
+// behavior of HTMLSelectElement.
+class SelectType : public GarbageCollected<SelectType> {
+ public:
+ // Creates an instance of a SelectType subclass depending on the current mode
+ // of |select|.
+ static SelectType* Create(HTMLSelectElement& select);
+ void WillBeDestroyed();
+ virtual void Trace(Visitor* visitor);
+
+ // Returns true if the event is handled.
+ virtual bool DefaultEventHandler(const Event& event) = 0;
+
+ virtual void DidSelectOption(HTMLOptionElement* element,
+ HTMLSelectElement::SelectOptionFlags flags,
+ bool should_update_popup);
+
+ virtual void DidBlur() = 0;
+ virtual void DidDetachLayoutTree();
+ virtual void DidRecalcStyle(const StyleRecalcChange change);
+ virtual void DidSetSuggestedOption(HTMLOptionElement* option) = 0;
+ virtual void SaveLastSelection() = 0;
+
+ // Update style of text in the CSS box on style or selected OPTION change.
+ virtual void UpdateTextStyle();
+
+ // Update style of text in the CSS box on style or selected OPTION change,
+ // and update the text.
+ virtual void UpdateTextStyleAndContent();
+
+ virtual HTMLOptionElement* OptionToBeShown() const;
+ virtual const ComputedStyle* OptionStyle() const;
+ virtual void MaximumOptionWidthMightBeChanged() const;
+
+ virtual void SelectAll();
+ virtual void SaveListboxActiveSelection();
+ virtual void HandleMouseRelease();
+ virtual void ListBoxOnChange();
+ // Clear OPTION selection information saved by SaveLastSelection().
+ // This is for ListBoxes.
+ virtual void ClearLastOnChangeSelection();
+
+ virtual void ShowPopup();
+ virtual void HidePopup();
+ virtual void PopupDidHide();
+ virtual bool PopupIsVisible() const;
+ virtual PopupMenu* PopupForTesting() const;
+
+ enum SkipDirection { kSkipBackwards = -1, kSkipForwards = 1 };
+ CORE_EXPORT HTMLOptionElement* NextSelectableOption(HTMLOptionElement*) const;
+ CORE_EXPORT HTMLOptionElement* PreviousSelectableOption(
+ HTMLOptionElement*) const;
+ CORE_EXPORT HTMLOptionElement* FirstSelectableOption() const;
+ CORE_EXPORT HTMLOptionElement* LastSelectableOption() const;
+
+ protected:
+ explicit SelectType(HTMLSelectElement& select);
+ HTMLOptionElement* NextValidOption(int list_index,
+ SkipDirection direction,
+ int skip) const;
+
+ const Member<HTMLSelectElement> select_;
+ bool will_be_destroyed_ = false;
+
+ private:
+ SelectType(const SelectType&) = delete;
+ SelectType& operator=(const SelectType&) = delete;
+};
+
+} // namespace blink
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_SELECT_TYPE_H_
diff --git a/chromium/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc b/chromium/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc
index a1cd68864ae..4c987d13ff6 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/slider_thumb_element.cc
@@ -47,13 +47,10 @@
#include "third_party/blink/renderer/core/layout/layout_object_factory.h"
#include "third_party/blink/renderer/core/layout/layout_slider_container.h"
#include "third_party/blink/renderer/core/layout/layout_theme.h"
+#include "ui/base/ui_base_features.h"
namespace blink {
-inline static bool HasVerticalAppearance(HTMLInputElement* input) {
- return input->ComputedStyleRef().EffectiveAppearance() == kSliderVerticalPart;
-}
-
SliderThumbElement::SliderThumbElement(Document& document)
: HTMLDivElement(document), in_drag_mode_(false) {
SetHasCustomStyleCallbacks();
@@ -67,7 +64,7 @@ void SliderThumbElement::SetPositionFromValue() {
if (GetLayoutObject()) {
GetLayoutObject()->SetNeedsLayoutAndFullPaintInvalidation(
layout_invalidation_reason::kSliderValueChanged);
- if (RuntimeEnabledFeatures::FormControlsRefreshEnabled()) {
+ if (features::IsFormControlsRefreshEnabled()) {
HTMLInputElement* input(HostInput());
if (input && input->GetLayoutObject()) {
// the slider track selected value needs to be updated.
@@ -116,7 +113,7 @@ void SliderThumbElement::SetPositionFromPoint(const LayoutPoint& point) {
PhysicalOffset point_in_track =
track_box->AbsoluteToLocalPoint(PhysicalOffsetToBeNoop(point));
- bool is_vertical = HasVerticalAppearance(input);
+ const bool is_vertical = !thumb_box->StyleRef().IsHorizontalWritingMode();
bool is_left_to_right_direction =
thumb_box->StyleRef().IsLeftToRightDirection();
LayoutUnit track_size;
@@ -199,13 +196,13 @@ void SliderThumbElement::StopDragging() {
}
void SliderThumbElement::DefaultEventHandler(Event& event) {
- if (event.IsPointerEvent() &&
+ if (IsA<PointerEvent>(event) &&
event.type() == event_type_names::kLostpointercapture) {
StopDragging();
return;
}
- if (!event.IsMouseEvent()) {
+ if (!IsA<MouseEvent>(event)) {
HTMLDivElement::DefaultEventHandler(event);
return;
}
@@ -220,7 +217,7 @@ void SliderThumbElement::DefaultEventHandler(Event& event) {
return;
}
- auto& mouse_event = ToMouseEvent(event);
+ auto& mouse_event = To<MouseEvent>(event);
bool is_left_button =
mouse_event.button() ==
static_cast<int16_t>(WebPointerProperties::Button::kLeft);
@@ -348,8 +345,8 @@ LayoutObject* SliderContainerElement::CreateLayoutObject(const ComputedStyle&,
}
void SliderContainerElement::DefaultEventHandler(Event& event) {
- if (event.IsTouchEvent()) {
- HandleTouchEvent(ToTouchEvent(&event));
+ if (auto* touch_event = DynamicTo<TouchEvent>(event)) {
+ HandleTouchEvent(touch_event);
return;
}
}
@@ -430,10 +427,9 @@ bool SliderContainerElement::CanSlide() {
}
}
}
- if ((sliding_direction_ == kVertical &&
- slider_style->EffectiveAppearance() == kSliderHorizontalPart) ||
- (sliding_direction_ == kHorizontal &&
- slider_style->EffectiveAppearance() == kSliderVerticalPart)) {
+ bool is_horizontal = GetComputedStyle()->IsHorizontalWritingMode();
+ if ((sliding_direction_ == kVertical && is_horizontal) ||
+ (sliding_direction_ == kHorizontal && !is_horizontal)) {
return false;
}
return true;
@@ -486,14 +482,4 @@ void SliderContainerElement::RemoveAllEventListeners() {
has_touch_event_handler_ = false;
}
-scoped_refptr<ComputedStyle>
-SliderContainerElement::CustomStyleForLayoutObject() {
- HTMLInputElement* input = HostInput();
- DCHECK(input);
- scoped_refptr<ComputedStyle> style = OriginalStyleForLayoutObject();
- style->SetFlexDirection(HasVerticalAppearance(input) ? EFlexDirection::kColumn
- : EFlexDirection::kRow);
- return style;
-}
-
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/slider_thumb_element.h b/chromium/third_party/blink/renderer/core/html/forms/slider_thumb_element.h
index 82977b7378d..cc4dd4d78f1 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/slider_thumb_element.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/slider_thumb_element.h
@@ -104,7 +104,6 @@ class SliderContainerElement final : public HTMLDivElement {
private:
LayoutObject* CreateLayoutObject(const ComputedStyle&, LegacyLayout) override;
- scoped_refptr<ComputedStyle> CustomStyleForLayoutObject() final;
const AtomicString& ShadowPseudoId() const override;
Direction GetDirection(LayoutPoint&, LayoutPoint&);
bool CanSlide();
diff --git a/chromium/third_party/blink/renderer/core/html/forms/spin_button_element.cc b/chromium/third_party/blink/renderer/core/html/forms/spin_button_element.cc
index 7dbfef38a01..24ea89a2682 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/spin_button_element.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/spin_button_element.cc
@@ -62,7 +62,8 @@ void SpinButtonElement::DetachLayoutTree(bool performing_reattach) {
}
void SpinButtonElement::DefaultEventHandler(Event& event) {
- if (!event.IsMouseEvent()) {
+ auto* mouse_event = DynamicTo<MouseEvent>(event);
+ if (!mouse_event) {
if (!event.DefaultHandled())
HTMLDivElement::DefaultEventHandler(event);
return;
@@ -81,11 +82,10 @@ void SpinButtonElement::DefaultEventHandler(Event& event) {
return;
}
- auto& mouse_event = ToMouseEvent(event);
IntPoint local = RoundedIntPoint(box->AbsoluteToLocalFloatPoint(
- FloatPoint(mouse_event.AbsoluteLocation())));
- if (mouse_event.type() == event_type_names::kMousedown &&
- mouse_event.button() ==
+ FloatPoint(mouse_event->AbsoluteLocation())));
+ if (mouse_event->type() == event_type_names::kMousedown &&
+ mouse_event->button() ==
static_cast<int16_t>(WebPointerProperties::Button::kLeft)) {
if (box->PixelSnappedBorderBoxRect().Contains(local)) {
if (spin_button_owner_)
@@ -114,8 +114,8 @@ void SpinButtonElement::DefaultEventHandler(Event& event) {
}
event.SetDefaultHandled();
}
- } else if (mouse_event.type() == event_type_names::kMouseup &&
- mouse_event.button() ==
+ } else if (mouse_event->type() == event_type_names::kMouseup &&
+ mouse_event->button() ==
static_cast<int16_t>(WebPointerProperties::Button::kLeft)) {
ReleaseCapture();
} else if (event.type() == event_type_names::kMousemove) {
@@ -152,7 +152,7 @@ void SpinButtonElement::ForwardEvent(Event& event) {
if (!spin_button_owner_->ShouldSpinButtonRespondToWheelEvents())
return;
- DoStepAction(ToWheelEvent(event).wheelDeltaY());
+ DoStepAction(To<WheelEvent>(event).wheelDeltaY());
event.SetDefaultHandled();
}
diff --git a/chromium/third_party/blink/renderer/core/html/forms/step_range.cc b/chromium/third_party/blink/renderer/core/html/forms/step_range.cc
index d8ea6ddb19f..b4bcb5d9ebc 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/step_range.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/step_range.cc
@@ -34,7 +34,8 @@ StepRange::StepRange()
step_(1),
step_base_(0),
has_step_(false),
- has_range_limitations_(false) {}
+ has_range_limitations_(false),
+ supports_reversed_range_(false) {}
StepRange::StepRange(const StepRange& step_range) = default;
@@ -42,6 +43,7 @@ StepRange::StepRange(const Decimal& step_base,
const Decimal& minimum,
const Decimal& maximum,
bool has_range_limitations,
+ bool supports_reversed_range,
const Decimal& step,
const StepDescription& step_description)
: maximum_(maximum),
@@ -50,7 +52,8 @@ StepRange::StepRange(const Decimal& step_base,
step_base_(step_base.IsFinite() ? step_base : 1),
step_description_(step_description),
has_step_(step.IsFinite()),
- has_range_limitations_(has_range_limitations) {
+ has_range_limitations_(has_range_limitations),
+ supports_reversed_range_(supports_reversed_range) {
DCHECK(maximum_.IsFinite());
DCHECK(minimum_.IsFinite());
DCHECK(step_.IsFinite());
@@ -100,7 +103,7 @@ Decimal StepRange::ParseStep(AnyStepHandling any_step_handling,
if (step_string.IsEmpty())
return step_description.DefaultValue();
- if (DeprecatedEqualIgnoringCase(step_string, "any")) {
+ if (EqualIgnoringASCIICase(step_string, "any")) {
switch (any_step_handling) {
case kRejectAny:
return Decimal::Nan();
@@ -183,4 +186,9 @@ Decimal StepRange::StepSnappedMaximum() const {
return aligned_maximum;
}
+// https://html.spec.whatwg.org/C/#has-a-reversed-range
+bool StepRange::HasReversedRange() const {
+ return supports_reversed_range_ && Maximum() < Minimum();
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/step_range.h b/chromium/third_party/blink/renderer/core/html/forms/step_range.h
index 796a496c438..70f2005da08 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/step_range.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/step_range.h
@@ -74,6 +74,7 @@ class CORE_EXPORT StepRange {
const Decimal& minimum,
const Decimal& maximum,
bool has_range_limitations,
+ bool supports_reversed_range,
const Decimal& step,
const StepDescription&);
@@ -85,6 +86,8 @@ class CORE_EXPORT StepRange {
Decimal Minimum() const { return minimum_; }
// https://html.spec.whatwg.org/C/#have-range-limitations
bool HasRangeLimitations() const { return has_range_limitations_; }
+ // https://html.spec.whatwg.org/C/#has-a-reversed-range
+ bool HasReversedRange() const;
static Decimal ParseStep(AnyStepHandling,
const StepDescription&,
const String&);
@@ -123,6 +126,7 @@ class CORE_EXPORT StepRange {
const StepDescription step_description_;
const bool has_step_;
const bool has_range_limitations_;
+ const bool supports_reversed_range_;
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/step_range_test.cc b/chromium/third_party/blink/renderer/core/html/forms/step_range_test.cc
index eddc58b9b28..9916c50d8b7 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/step_range_test.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/step_range_test.cc
@@ -11,7 +11,8 @@ namespace blink {
TEST(StepRangeTest, ClampValueWithOutStepMatchedValue) {
// <input type=range value=200 min=0 max=100 step=1000>
StepRange step_range(Decimal(200), Decimal(0), Decimal(100), true,
- Decimal(1000), StepRange::StepDescription());
+ /*has_reversed_range=*/false, Decimal(1000),
+ StepRange::StepDescription());
EXPECT_EQ(Decimal(100), step_range.ClampValue(Decimal(200)));
EXPECT_EQ(Decimal(0), step_range.ClampValue(Decimal(-100)));
@@ -20,7 +21,8 @@ TEST(StepRangeTest, ClampValueWithOutStepMatchedValue) {
TEST(StepRangeTest, StepSnappedMaximum) {
// <input type=number value="1110" max=100 step="20">
StepRange step_range(Decimal::FromDouble(1110), Decimal(0), Decimal(100),
- true, Decimal(20), StepRange::StepDescription());
+ true, /*has_reversed_range=*/false, Decimal(20),
+ StepRange::StepDescription());
EXPECT_EQ(Decimal(90), step_range.StepSnappedMaximum());
// crbug.com/617809
@@ -28,9 +30,34 @@ TEST(StepRangeTest, StepSnappedMaximum) {
// value="8624024784918570374158793713225864658725102756338798521486349461900449498315865014065406918592181034633618363349807887404915072776534917803019477033072906290735591367789665757384135591225430117374220731087966"
// min=0 max=100 step="18446744073709551575">
StepRange step_range2(Decimal::FromDouble(8.62402e+207), Decimal(0),
- Decimal(100), true, Decimal::FromDouble(1.84467e+19),
+ Decimal(100), true, /*has_reversed_range=*/false,
+ Decimal::FromDouble(1.84467e+19),
StepRange::StepDescription());
EXPECT_FALSE(step_range2.StepSnappedMaximum().IsFinite());
}
+TEST(StepRangeTest, ReversedRange) {
+ // <input type=time min="23:00" max="01:00">
+ StepRange reversed_time_range(
+ /*step_base=*/Decimal::FromDouble(82800000),
+ /*minimum=*/Decimal::FromDouble(82800000),
+ /*maximum=*/Decimal::FromDouble(3600000),
+ /*has_range_limitations=*/true,
+ /*supports_reversed_range=*/true,
+ /*step=*/Decimal::FromDouble(60000),
+ /*step_description=*/StepRange::StepDescription());
+ EXPECT_TRUE(reversed_time_range.HasReversedRange());
+
+ // <input type=time min="01:00" max="23:00">
+ StepRange regular_time_range(
+ /*step_base=*/Decimal::FromDouble(3600000),
+ /*minimum=*/Decimal::FromDouble(3600000),
+ /*maximum=*/Decimal::FromDouble(82800000),
+ /*has_range_limitations=*/true,
+ /*supports_reversed_range=*/true,
+ /*step=*/Decimal::FromDouble(60000),
+ /*step_description=*/StepRange::StepDescription());
+ EXPECT_FALSE(regular_time_range.HasReversedRange());
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/submit_event.cc b/chromium/third_party/blink/renderer/core/html/forms/submit_event.cc
new file mode 100644
index 00000000000..04c84a91404
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/html/forms/submit_event.cc
@@ -0,0 +1,32 @@
+// Copyright 2020 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/submit_event.h"
+
+#include "third_party/blink/renderer/bindings/core/v8/v8_submit_event_init.h"
+#include "third_party/blink/renderer/core/event_interface_names.h"
+#include "third_party/blink/renderer/core/html/html_element.h"
+
+namespace blink {
+
+SubmitEvent::SubmitEvent(const AtomicString& type,
+ const SubmitEventInit* event_init)
+ : Event(type, event_init),
+ submitter_(event_init ? event_init->submitter() : nullptr) {}
+
+SubmitEvent* SubmitEvent::Create(const AtomicString& type,
+ const SubmitEventInit* event_init) {
+ return MakeGarbageCollected<SubmitEvent>(type, event_init);
+}
+
+void SubmitEvent::Trace(Visitor* visitor) {
+ visitor->Trace(submitter_);
+ Event::Trace(visitor);
+}
+
+const AtomicString& SubmitEvent::InterfaceName() const {
+ return event_interface_names::kSubmitEvent;
+}
+
+} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/submit_event.h b/chromium/third_party/blink/renderer/core/html/forms/submit_event.h
new file mode 100644
index 00000000000..2e242d3a791
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/html/forms/submit_event.h
@@ -0,0 +1,33 @@
+// Copyright 2020 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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_SUBMIT_EVENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_SUBMIT_EVENT_H_
+
+#include "third_party/blink/renderer/core/dom/events/event.h"
+
+namespace blink {
+
+class HTMLElement;
+class SubmitEventInit;
+
+class SubmitEvent : public Event {
+ DEFINE_WRAPPERTYPEINFO();
+
+ public:
+ static SubmitEvent* Create(const AtomicString& type,
+ const SubmitEventInit* event_init);
+ SubmitEvent(const AtomicString& type, const SubmitEventInit* event_init);
+
+ void Trace(Visitor* visitor) override;
+ HTMLElement* submitter() const { return submitter_.Get(); }
+ const AtomicString& InterfaceName() const override;
+
+ private:
+ Member<HTMLElement> submitter_;
+};
+
+} // namespace blink
+
+#endif // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_SUBMIT_EVENT_H_
diff --git a/chromium/third_party/blink/renderer/core/html/forms/submit_event.idl b/chromium/third_party/blink/renderer/core/html/forms/submit_event.idl
new file mode 100644
index 00000000000..06de5b4286c
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/html/forms/submit_event.idl
@@ -0,0 +1,12 @@
+// Copyright 2020 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.
+
+// https://html.spec.whatwg.org/C/#submitevent
+
+[
+ Exposed=Window
+] interface SubmitEvent : Event {
+ constructor(DOMString type, optional SubmitEventInit eventInitDict = {});
+ readonly attribute HTMLElement? submitter;
+};
diff --git a/chromium/third_party/blink/renderer/core/html/forms/submit_event_init.idl b/chromium/third_party/blink/renderer/core/html/forms/submit_event_init.idl
new file mode 100644
index 00000000000..e38c93d7680
--- /dev/null
+++ b/chromium/third_party/blink/renderer/core/html/forms/submit_event_init.idl
@@ -0,0 +1,9 @@
+// Copyright 2020 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.
+
+// https://html.spec.whatwg.org/C/#submitevent
+
+dictionary SubmitEventInit : EventInit {
+ HTMLElement? submitter = null;
+};
diff --git a/chromium/third_party/blink/renderer/core/html/forms/text_control_element.cc b/chromium/third_party/blink/renderer/core/html/forms/text_control_element.cc
index 418baf6f877..3792c0037a5 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/text_control_element.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/text_control_element.cc
@@ -24,6 +24,7 @@
#include "third_party/blink/renderer/core/html/forms/text_control_element.h"
+#include "third_party/blink/public/mojom/input/focus_type.mojom-blink.h"
#include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
#include "third_party/blink/renderer/core/css/style_change_reason.h"
#include "third_party/blink/renderer/core/dom/document.h"
@@ -82,7 +83,7 @@ TextControlElement::~TextControlElement() = default;
void TextControlElement::DispatchFocusEvent(
Element* old_focused_element,
- WebFocusType type,
+ mojom::blink::FocusType type,
InputDeviceCapabilities* source_capabilities) {
if (SupportsPlaceholder())
UpdatePlaceholderVisibility();
@@ -93,7 +94,7 @@ void TextControlElement::DispatchFocusEvent(
void TextControlElement::DispatchBlurEvent(
Element* new_focused_element,
- WebFocusType type,
+ mojom::blink::FocusType type,
InputDeviceCapabilities* source_capabilities) {
if (SupportsPlaceholder())
UpdatePlaceholderVisibility();
@@ -222,8 +223,8 @@ void TextControlElement::select() {
setSelectionRangeForBinding(0, std::numeric_limits<unsigned>::max());
// Avoid SelectionBehaviorOnFocus::Restore, which scrolls containers to show
// the selection.
- focus(
- FocusParams(SelectionBehaviorOnFocus::kNone, kWebFocusTypeNone, nullptr));
+ focus(FocusParams(SelectionBehaviorOnFocus::kNone,
+ mojom::blink::FocusType::kNone, nullptr));
RestoreCachedSelection();
}
@@ -246,7 +247,8 @@ void TextControlElement::ClearValueBeforeFirstUserEdit() {
value_before_first_user_edit_ = String();
}
-void TextControlElement::SetFocused(bool flag, WebFocusType focus_type) {
+void TextControlElement::SetFocused(bool flag,
+ mojom::blink::FocusType focus_type) {
HTMLFormControlElementWithState::SetFocused(flag, focus_type);
if (!flag)
@@ -972,11 +974,11 @@ String TextControlElement::DirectionForFormData() const {
if (dir_attribute_value.IsNull())
continue;
- if (DeprecatedEqualIgnoringCase(dir_attribute_value, "rtl") ||
- DeprecatedEqualIgnoringCase(dir_attribute_value, "ltr"))
+ if (EqualIgnoringASCIICase(dir_attribute_value, "rtl") ||
+ EqualIgnoringASCIICase(dir_attribute_value, "ltr"))
return dir_attribute_value;
- if (DeprecatedEqualIgnoringCase(dir_attribute_value, "auto")) {
+ if (EqualIgnoringASCIICase(dir_attribute_value, "auto")) {
bool is_auto;
TextDirection text_direction =
element->DirectionalityIfhasDirAutoAttribute(is_auto);
diff --git a/chromium/third_party/blink/renderer/core/html/forms/text_control_element.h b/chromium/third_party/blink/renderer/core/html/forms/text_control_element.h
index 47c266d0474..740cab80101 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/text_control_element.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/text_control_element.h
@@ -27,7 +27,7 @@
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_TEXT_CONTROL_ELEMENT_H_
#include "base/gtest_prod_util.h"
-#include "third_party/blink/public/platform/web_focus_type.h"
+#include "third_party/blink/public/mojom/input/focus_type.mojom-blink-forward.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/editing/forward.h"
#include "third_party/blink/renderer/core/html/forms/html_form_control_element_with_state.h"
@@ -45,6 +45,7 @@ enum TextFieldSelectionDirection {
enum class TextFieldEventBehavior {
kDispatchNoEvent,
kDispatchChangeEvent,
+ kDispatchInputEvent,
kDispatchInputAndChangeEvent
};
@@ -65,7 +66,7 @@ class CORE_EXPORT TextControlElement : public HTMLFormControlElementWithState {
void ForwardEvent(Event&);
- void SetFocused(bool, WebFocusType) override;
+ void SetFocused(bool, mojom::blink::FocusType) override;
// The derived class should return true if placeholder processing is needed.
virtual bool IsPlaceholderVisible() const = 0;
@@ -186,10 +187,10 @@ class CORE_EXPORT TextControlElement : public HTMLFormControlElementWithState {
static unsigned IndexForPosition(HTMLElement* inner_editor, const Position&);
void DispatchFocusEvent(Element* old_focused_element,
- WebFocusType,
+ mojom::blink::FocusType,
InputDeviceCapabilities* source_capabilities) final;
void DispatchBlurEvent(Element* new_focused_element,
- WebFocusType,
+ mojom::blink::FocusType,
InputDeviceCapabilities* source_capabilities) final;
void ScheduleSelectEvent();
void DisabledOrReadonlyAttributeChanged(const QualifiedName&);
@@ -202,7 +203,8 @@ class CORE_EXPORT TextControlElement : public HTMLFormControlElementWithState {
bool IsEmptySuggestedValue() const { return SuggestedValue().IsEmpty(); }
// Called in dispatchFocusEvent(), after placeholder process, before calling
// parent's dispatchFocusEvent().
- virtual void HandleFocusEvent(Element* /* oldFocusedNode */, WebFocusType) {}
+ virtual void HandleFocusEvent(Element* /* oldFocusedNode */,
+ mojom::blink::FocusType) {}
// Called in dispatchBlurEvent(), after placeholder process, before calling
// parent's dispatchBlurEvent().
virtual void HandleBlurEvent() {}
diff --git a/chromium/third_party/blink/renderer/core/html/forms/text_control_element_test.cc b/chromium/third_party/blink/renderer/core/html/forms/text_control_element_test.cc
index 64536a9caa5..e5f900301bc 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/text_control_element_test.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/text_control_element_test.cc
@@ -30,8 +30,7 @@ class TextControlElementTest : public testing::Test {
HTMLInputElement& Input() const { return *input_; }
void UpdateAllLifecyclePhases() {
- GetDocument().View()->UpdateAllLifecyclePhases(
- DocumentLifecycle::LifecycleUpdateReason::kTest);
+ GetDocument().View()->UpdateAllLifecyclePhases(DocumentUpdateReason::kTest);
}
private:
@@ -49,7 +48,7 @@ void TextControlElementTest::SetUp() {
std::make_unique<DummyPageHolder>(IntSize(800, 600), &page_clients);
document_ = &dummy_page_holder_->GetDocument();
- document_->documentElement()->SetInnerHTMLFromString(
+ document_->documentElement()->setInnerHTML(
"<body><textarea id=textarea></textarea><input id=input /></body>");
UpdateAllLifecyclePhases();
text_control_ = ToTextControl(document_->getElementById("textarea"));
diff --git a/chromium/third_party/blink/renderer/core/html/forms/text_control_inner_elements.cc b/chromium/third_party/blink/renderer/core/html/forms/text_control_inner_elements.cc
index 88b66536810..c1f64e3421c 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/text_control_inner_elements.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/text_control_inner_elements.cc
@@ -39,19 +39,6 @@
namespace blink {
-TextControlInnerContainer::TextControlInnerContainer(Document& document)
- : HTMLDivElement(document) {
- setAttribute(html_names::kIdAttr, shadow_element_names::TextFieldContainer());
-}
-
-LayoutObject* TextControlInnerContainer::CreateLayoutObject(
- const ComputedStyle&,
- LegacyLayout) {
- return new LayoutTextControlInnerContainer(this);
-}
-
-// ---------------------------
-
EditingViewPortElement::EditingViewPortElement(Document& document)
: HTMLDivElement(document) {
SetHasCustomStyleCallbacks();
@@ -100,6 +87,17 @@ void TextControlInnerEditorElement::DefaultEventHandler(Event& event) {
if (shadow_ancestor)
shadow_ancestor->DefaultEventHandler(event);
}
+
+ if (event.type() == event_type_names::kScroll) {
+ // The scroller for a text control is inside of a shadow tree but the
+ // scroll event won't bubble past the shadow root and authors cannot add
+ // an event listener to it. Fire the scroll event at the shadow host so
+ // that the page can hear about the scroll.
+ Element* shadow_ancestor = OwnerShadowHost();
+ if (shadow_ancestor)
+ shadow_ancestor->DispatchEvent(event);
+ }
+
if (!event.DefaultHandled())
HTMLDivElement::DefaultEventHandler(event);
}
@@ -148,6 +146,8 @@ TextControlInnerEditorElement::CreateInnerEditorStyle() const {
? EUserModify::kReadOnly
: EUserModify::kReadWritePlaintextOnly);
text_block_style->SetDisplay(EDisplay::kBlock);
+ text_block_style->SetHasLineIfEmpty(true);
+ text_block_style->SetShouldIgnoreOverflowPropertyForInlineBlockBaseline();
if (!IsA<HTMLTextAreaElement>(host)) {
text_block_style->SetWhiteSpace(EWhiteSpace::kPre);
@@ -204,6 +204,7 @@ SearchFieldCancelButtonElement::SearchFieldCancelButtonElement(
void SearchFieldCancelButtonElement::DefaultEventHandler(Event& event) {
// If the element is visible, on mouseup, clear the value, and set selection
+ auto* mouse_event = DynamicTo<MouseEvent>(event);
auto* input = To<HTMLInputElement>(OwnerShadowHost());
if (!input || input->IsDisabledOrReadOnly()) {
if (!event.DefaultHandled())
@@ -211,8 +212,8 @@ void SearchFieldCancelButtonElement::DefaultEventHandler(Event& event) {
return;
}
- if (event.type() == event_type_names::kClick && event.IsMouseEvent() &&
- ToMouseEvent(event).button() ==
+ if (event.type() == event_type_names::kClick && mouse_event &&
+ mouse_event->button() ==
static_cast<int16_t>(WebPointerProperties::Button::kLeft)) {
input->SetValueForUser("");
input->SetAutofillState(WebAutofillState::kNotFilled);
@@ -250,7 +251,7 @@ void PasswordRevealButtonElement::DefaultEventHandler(Event& event) {
}
// Toggle the should-reveal-password state when clicked.
- if (event.type() == event_type_names::kClick && event.IsMouseEvent()) {
+ if (event.type() == event_type_names::kClick && IsA<MouseEvent>(event)) {
bool shouldRevealPassword = !input->ShouldRevealPassword();
input->SetShouldRevealPassword(shouldRevealPassword);
diff --git a/chromium/third_party/blink/renderer/core/html/forms/text_control_inner_elements.h b/chromium/third_party/blink/renderer/core/html/forms/text_control_inner_elements.h
index cd0ad1b92fa..f800144a200 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/text_control_inner_elements.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/text_control_inner_elements.h
@@ -32,15 +32,6 @@
namespace blink {
-class TextControlInnerContainer final : public HTMLDivElement {
- public:
- explicit TextControlInnerContainer(Document&);
-
- protected:
- LayoutObject* CreateLayoutObject(const ComputedStyle&, LegacyLayout) override;
- bool TypeShouldForceLegacyLayout() const final { return true; }
-};
-
class EditingViewPortElement final : public HTMLDivElement {
public:
explicit EditingViewPortElement(Document&);
diff --git a/chromium/third_party/blink/renderer/core/html/forms/text_field_input_type.cc b/chromium/third_party/blink/renderer/core/html/forms/text_field_input_type.cc
index 5953d45d82b..efb2e1ef525 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/text_field_input_type.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/text_field_input_type.cc
@@ -35,7 +35,9 @@
#include "third_party/blink/renderer/core/dom/shadow_root.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/events/before_text_inserted_event.h"
+#include "third_party/blink/renderer/core/events/drag_event.h"
#include "third_party/blink/renderer/core/events/keyboard_event.h"
+#include "third_party/blink/renderer/core/events/mouse_event.h"
#include "third_party/blink/renderer/core/events/text_event.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/html/forms/form_data.h"
@@ -134,7 +136,10 @@ bool TextFieldInputType::IsTextField() const {
}
bool TextFieldInputType::ValueMissing(const String& value) const {
- return GetElement().IsRequired() && value.IsEmpty();
+ // For text-mode input elements, the value is missing only if it is mutable.
+ // https://html.spec.whatwg.org/multipage/input.html#the-required-attribute
+ return GetElement().IsRequired() && value.IsEmpty() &&
+ !GetElement().IsDisabledOrReadOnly();
}
bool TextFieldInputType::CanSetSuggestedValue() {
@@ -177,11 +182,14 @@ void TextFieldInputType::SetValue(const String& sanitized_value,
GetElement().DispatchFormControlChangeEvent();
break;
- case TextFieldEventBehavior::kDispatchInputAndChangeEvent: {
+ case TextFieldEventBehavior::kDispatchInputEvent:
+ GetElement().DispatchInputEvent();
+ break;
+
+ case TextFieldEventBehavior::kDispatchInputAndChangeEvent:
GetElement().DispatchInputEvent();
GetElement().DispatchFormControlChangeEvent();
break;
- }
case TextFieldEventBehavior::kDispatchNoEvent:
break;
@@ -226,12 +234,12 @@ void TextFieldInputType::ForwardEvent(Event& event) {
// input element.
if (GetElement().GetLayoutObject() &&
!GetElement().GetForceReattachLayoutTree() &&
- (event.IsMouseEvent() || event.IsDragEvent() ||
+ (IsA<MouseEvent>(event) || IsA<DragEvent>(event) ||
event.HasInterface(event_interface_names::kWheelEvent) ||
event.type() == event_type_names::kBlur ||
event.type() == event_type_names::kFocus)) {
- LayoutTextControlSingleLine* layout_text_control =
- ToLayoutTextControlSingleLine(GetElement().GetLayoutObject());
+ auto* layout_text_control =
+ To<LayoutTextControlSingleLine>(GetElement().GetLayoutObject());
if (event.type() == event_type_names::kBlur) {
if (LayoutBox* inner_editor_layout_object =
GetElement().InnerEditorElement()->GetLayoutBox()) {
@@ -239,8 +247,8 @@ void TextFieldInputType::ForwardEvent(Event& event) {
if (PaintLayer* inner_layer = inner_editor_layout_object->Layer()) {
if (PaintLayerScrollableArea* inner_scrollable_area =
inner_layer->GetScrollableArea()) {
- inner_scrollable_area->SetScrollOffset(ScrollOffset(0, 0),
- kProgrammaticScroll);
+ inner_scrollable_area->SetScrollOffset(
+ ScrollOffset(0, 0), mojom::blink::ScrollType::kProgrammatic);
}
}
}
@@ -268,6 +276,16 @@ bool TextFieldInputType::ShouldSubmitImplicitly(const Event& event) {
InputTypeView::ShouldSubmitImplicitly(event);
}
+void TextFieldInputType::CustomStyleForLayoutObject(ComputedStyle& style) {
+ // The flag is necessary in order that a text field <input> with non-'visible'
+ // overflow property doesn't change its baseline.
+ style.SetShouldIgnoreOverflowPropertyForInlineBlockBaseline();
+}
+
+bool TextFieldInputType::TypeShouldForceLegacyLayout() const {
+ return true;
+}
+
LayoutObject* TextFieldInputType::CreateLayoutObject(const ComputedStyle&,
LegacyLayout) const {
return new LayoutTextControlSingleLine(&GetElement());
@@ -294,7 +312,8 @@ void TextFieldInputType::CreateShadowSubtree() {
return;
}
- auto* container = MakeGarbageCollected<TextControlInnerContainer>(document);
+ auto* container = MakeGarbageCollected<HTMLDivElement>(document);
+ container->SetIdAttribute(shadow_element_names::TextFieldContainer());
container->SetShadowPseudoId(
AtomicString("-webkit-textfield-decoration-container"));
shadow_root->AppendChild(container);
@@ -352,8 +371,8 @@ void TextFieldInputType::ListAttributeTargetChanged() {
// FIXME: The following code is similar to createShadowSubtree(),
// but they are different. We should simplify the code by making
// containerElement mandatory.
- auto* rp_container =
- MakeGarbageCollected<TextControlInnerContainer>(document);
+ auto* rp_container = MakeGarbageCollected<HTMLDivElement>(document);
+ rp_container->SetIdAttribute(shadow_element_names::TextFieldContainer());
rp_container->SetShadowPseudoId(
AtomicString("-webkit-textfield-decoration-container"));
Element* inner_editor = GetElement().InnerEditorElement();
@@ -429,7 +448,8 @@ void TextFieldInputType::HandleBeforeTextInsertedEvent(
if (GetElement().IsFocused()) {
// TODO(editing-dev): Use of UpdateStyleAndLayout
// needs to be audited. See http://crbug.com/590369 for more details.
- GetElement().GetDocument().UpdateStyleAndLayout();
+ GetElement().GetDocument().UpdateStyleAndLayout(
+ DocumentUpdateReason::kEditing);
selection_length = GetElement()
.GetDocument()
@@ -573,4 +593,8 @@ void TextFieldInputType::SpinButtonDidReleaseMouseCapture(
GetElement().DispatchFormControlChangeEvent();
}
+String TextFieldInputType::RawValue() const {
+ return GetElement().InnerEditorElement()->innerText();
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/text_field_input_type.h b/chromium/third_party/blink/renderer/core/html/forms/text_field_input_type.h
index 595bed48801..0e7994ca51a 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/text_field_input_type.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/text_field_input_type.h
@@ -48,6 +48,8 @@ class TextFieldInputType : public InputType,
void Trace(Visitor*) override;
using InputType::GetElement;
+ String RawValue() const override;
+
protected:
TextFieldInputType(HTMLInputElement&);
~TextFieldInputType() override;
@@ -68,6 +70,8 @@ class TextFieldInputType : public InputType,
TextFieldEventBehavior,
TextControlSetValueSelection) override;
void UpdateView() override;
+ void CustomStyleForLayoutObject(ComputedStyle& style) override;
+ bool TypeShouldForceLegacyLayout() const override;
LayoutObject* CreateLayoutObject(const ComputedStyle&,
LegacyLayout) const override;
diff --git a/chromium/third_party/blink/renderer/core/html/forms/text_input_type.cc b/chromium/third_party/blink/renderer/core/html/forms/text_input_type.cc
index b9d42cef516..a611a0333df 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/text_input_type.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/text_input_type.cc
@@ -42,9 +42,9 @@ void TextInputType::CountUsage() {
CountUsageIfVisible(WebFeature::kInputTypeTextMaxLength);
const AtomicString& type =
GetElement().FastGetAttribute(html_names::kTypeAttr);
- if (DeprecatedEqualIgnoringCase(type, input_type_names::kDatetime))
+ if (EqualIgnoringASCIICase(type, input_type_names::kDatetime))
CountUsageIfVisible(WebFeature::kInputTypeDateTimeFallback);
- else if (DeprecatedEqualIgnoringCase(type, input_type_names::kWeek))
+ else if (EqualIgnoringASCIICase(type, input_type_names::kWeek))
CountUsageIfVisible(WebFeature::kInputTypeWeekFallback);
}
diff --git a/chromium/third_party/blink/renderer/core/html/forms/time_input_type.cc b/chromium/third_party/blink/renderer/core/html/forms/time_input_type.cc
index bbf3ccfc8d6..e56c81b5b43 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/time_input_type.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/time_input_type.cc
@@ -30,6 +30,7 @@
#include "third_party/blink/renderer/core/html/forms/time_input_type.h"
+#include "third_party/blink/public/strings/grit/blink_strings.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/frame/web_feature.h"
#include "third_party/blink/renderer/core/html/forms/date_time_fields_state.h"
@@ -76,7 +77,7 @@ StepRange TimeInputType::CreateStepRange(
(kTimeDefaultStep, kTimeDefaultStepBase, kTimeStepScaleFactor,
StepRange::kScaledStepValueShouldBeInteger));
- return InputType::CreateStepRange(
+ return InputType::CreateReversibleStepRange(
any_step_handling, kTimeDefaultStepBase,
Decimal::FromDouble(DateComponents::MinimumTime()),
Decimal::FromDouble(DateComponents::MaximumTime()), step_description);
@@ -173,4 +174,16 @@ bool TimeInputType::IsValidFormat(bool has_year,
return has_hour && has_minute && has_ampm;
}
+String TimeInputType::AriaRoleForPickerIndicator() const {
+ return GetLocale().QueryString(IDS_AX_CALENDAR_SHOW_TIME_PICKER);
+}
+
+String TimeInputType::ReversedRangeOutOfRangeText(
+ const Decimal& minimum,
+ const Decimal& maximum) const {
+ return GetLocale().QueryString(
+ IDS_FORM_VALIDATION_REVERSED_RANGE_OUT_OF_RANGE_TIME,
+ LocalizeValue(Serialize(minimum)), LocalizeValue(Serialize(maximum)));
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/time_input_type.h b/chromium/third_party/blink/renderer/core/html/forms/time_input_type.h
index 58711ec43f5..2b520a8899d 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/time_input_type.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/time_input_type.h
@@ -62,6 +62,9 @@ class TimeInputType final : public BaseTemporalInputType {
bool has_hour,
bool has_minute,
bool has_second) const override;
+ String AriaRoleForPickerIndicator() const override;
+ String ReversedRangeOutOfRangeText(const Decimal& minimum,
+ const Decimal& maximum) const override;
};
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/type_ahead_test.cc b/chromium/third_party/blink/renderer/core/html/forms/type_ahead_test.cc
index 5077a9152e2..dc43dbf7bb4 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/type_ahead_test.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/type_ahead_test.cc
@@ -6,7 +6,7 @@
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/platform/web_keyboard_event.h"
+#include "third_party/blink/public/common/input/web_keyboard_event.h"
#include "third_party/blink/renderer/core/events/keyboard_event.h"
namespace blink {
diff --git a/chromium/third_party/blink/renderer/core/html/forms/week_input_type.cc b/chromium/third_party/blink/renderer/core/html/forms/week_input_type.cc
index 3c53ffb9098..0501230b551 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/week_input_type.cc
+++ b/chromium/third_party/blink/renderer/core/html/forms/week_input_type.cc
@@ -30,6 +30,7 @@
#include "third_party/blink/renderer/core/html/forms/week_input_type.h"
+#include "third_party/blink/public/strings/grit/blink_strings.h"
#include "third_party/blink/renderer/core/frame/web_feature.h"
#include "third_party/blink/renderer/core/html/forms/date_time_fields_state.h"
#include "third_party/blink/renderer/core/html/forms/html_input_element.h"
@@ -125,4 +126,8 @@ bool WeekInputType::IsValidFormat(bool has_year,
return has_year && has_week;
}
+String WeekInputType::AriaRoleForPickerIndicator() const {
+ return GetLocale().QueryString(IDS_AX_CALENDAR_SHOW_WEEK_PICKER);
+}
+
} // namespace blink
diff --git a/chromium/third_party/blink/renderer/core/html/forms/week_input_type.h b/chromium/third_party/blink/renderer/core/html/forms/week_input_type.h
index 783aeff2766..b8b176bbaad 100644
--- a/chromium/third_party/blink/renderer/core/html/forms/week_input_type.h
+++ b/chromium/third_party/blink/renderer/core/html/forms/week_input_type.h
@@ -61,6 +61,7 @@ class WeekInputType final : public BaseTemporalInputType {
bool has_hour,
bool has_minute,
bool has_second) const override;
+ String AriaRoleForPickerIndicator() const override;
};
} // namespace blink