1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2001 Dirk Mueller (mueller@kde.org)
5 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
6 * rights reserved.
7 * (C) 2006 Alexey Proskuryakov (ap@nypop.com)
8 * Copyright (C) 2007 Samuel Weinig (sam@webkit.org)
9 * Copyright (C) 2010 Google Inc. All rights reserved.
10 * Copyright (C) 2008 Torch Mobile Inc. All rights reserved.
11 * (http://www.torchmobile.com/)
12 * Copyright (C) 2012 Samsung Electronics. All rights reserved.
13 *
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Library General Public
16 * License as published by the Free Software Foundation; either
17 * version 2 of the License, or (at your option) any later version.
18 *
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Library General Public License for more details.
23 *
24 * You should have received a copy of the GNU Library General Public License
25 * along with this library; see the file COPYING.LIB. If not, write to
26 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
27 * Boston, MA 02110-1301, USA.
28 *
29 */
30
31 #include "third_party/blink/renderer/core/html/forms/html_input_element.h"
32
33 #include "third_party/blink/public/mojom/choosers/date_time_chooser.mojom-blink.h"
34 #include "third_party/blink/public/platform/task_type.h"
35 #include "third_party/blink/public/strings/grit/blink_strings.h"
36 #include "third_party/blink/renderer/bindings/core/v8/js_event_handler_for_content_attribute.h"
37 #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
38 #include "third_party/blink/renderer/bindings/core/v8/v8_focus_options.h"
39 #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
40 #include "third_party/blink/renderer/core/css/css_property_names.h"
41 #include "third_party/blink/renderer/core/css/style_change_reason.h"
42 #include "third_party/blink/renderer/core/dom/document.h"
43 #include "third_party/blink/renderer/core/dom/events/scoped_event_queue.h"
44 #include "third_party/blink/renderer/core/dom/id_target_observer.h"
45 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
46 #include "third_party/blink/renderer/core/dom/shadow_root.h"
47 #include "third_party/blink/renderer/core/dom/v0_insertion_point.h"
48 #include "third_party/blink/renderer/core/editing/frame_selection.h"
49 #include "third_party/blink/renderer/core/editing/spellcheck/spell_checker.h"
50 #include "third_party/blink/renderer/core/events/before_text_inserted_event.h"
51 #include "third_party/blink/renderer/core/events/keyboard_event.h"
52 #include "third_party/blink/renderer/core/events/mouse_event.h"
53 #include "third_party/blink/renderer/core/fileapi/file_list.h"
54 #include "third_party/blink/renderer/core/frame/deprecation.h"
55 #include "third_party/blink/renderer/core/frame/local_frame.h"
56 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
57 #include "third_party/blink/renderer/core/html/forms/color_chooser.h"
58 #include "third_party/blink/renderer/core/html/forms/date_time_chooser.h"
59 #include "third_party/blink/renderer/core/html/forms/email_input_type.h"
60 #include "third_party/blink/renderer/core/html/forms/file_input_type.h"
61 #include "third_party/blink/renderer/core/html/forms/form_controller.h"
62 #include "third_party/blink/renderer/core/html/forms/html_data_list_element.h"
63 #include "third_party/blink/renderer/core/html/forms/html_data_list_options_collection.h"
64 #include "third_party/blink/renderer/core/html/forms/html_form_element.h"
65 #include "third_party/blink/renderer/core/html/forms/html_option_element.h"
66 #include "third_party/blink/renderer/core/html/forms/input_type.h"
67 #include "third_party/blink/renderer/core/html/forms/search_input_type.h"
68 #include "third_party/blink/renderer/core/html/forms/text_input_type.h"
69 #include "third_party/blink/renderer/core/html/html_collection.h"
70 #include "third_party/blink/renderer/core/html/html_image_loader.h"
71 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
72 #include "third_party/blink/renderer/core/html_names.h"
73 #include "third_party/blink/renderer/core/input_type_names.h"
74 #include "third_party/blink/renderer/core/layout/adjust_for_absolute_zoom.h"
75 #include "third_party/blink/renderer/core/layout/layout_box.h"
76 #include "third_party/blink/renderer/core/page/chrome_client.h"
77 #include "third_party/blink/renderer/core/page/page.h"
78 #include "third_party/blink/renderer/platform/bindings/exception_messages.h"
79 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
80 #include "third_party/blink/renderer/platform/bindings/to_v8.h"
81 #include "third_party/blink/renderer/platform/heap/heap.h"
82 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
83 #include "third_party/blink/renderer/platform/language.h"
84 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
85 #include "third_party/blink/renderer/platform/text/platform_locale.h"
86 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
87 #include "ui/base/ui_base_features.h"
88
89 namespace blink {
90
91 namespace {
92
93 const unsigned kMaxEmailFieldLength = 254;
94
95 } // namespace
96
97 using ValueMode = InputType::ValueMode;
98
99 class ListAttributeTargetObserver : public IdTargetObserver {
100 public:
101 ListAttributeTargetObserver(const AtomicString& id, HTMLInputElement*);
102
103 void Trace(Visitor*) const override;
104 void IdTargetChanged() override;
105
106 private:
107 Member<HTMLInputElement> element_;
108 };
109
110 const int kDefaultSize = 20;
111
HTMLInputElement(Document & document,const CreateElementFlags flags)112 HTMLInputElement::HTMLInputElement(Document& document,
113 const CreateElementFlags flags)
114 : TextControlElement(html_names::kInputTag, document),
115 size_(kDefaultSize),
116 has_dirty_value_(false),
117 is_checked_(false),
118 dirty_checkedness_(false),
119 is_indeterminate_(false),
120 is_activated_submit_(false),
121 autocomplete_(kUninitialized),
122 has_non_empty_list_(false),
123 state_restored_(false),
124 parsing_in_progress_(flags.IsCreatedByParser()),
125 can_receive_dropped_files_(false),
126 should_reveal_password_(false),
127 needs_to_update_view_value_(true),
128 is_placeholder_visible_(false),
129 has_been_password_field_(false),
130 // |input_type_| is lazily created when constructed by the parser to avoid
131 // constructing unnecessarily a text InputType and its shadow subtree,
132 // just to destroy them when the |type| attribute gets set by the parser
133 // to something else than 'text'.
134 input_type_(flags.IsCreatedByParser()
135 ? nullptr
136 : MakeGarbageCollected<TextInputType>(*this)),
137 input_type_view_(input_type_ ? input_type_->CreateView() : nullptr) {
138 SetHasCustomStyleCallbacks();
139
140 if (!flags.IsCreatedByParser()) {
141 DCHECK(input_type_view_->NeedsShadowSubtree());
142 CreateUserAgentShadowRoot();
143 CreateShadowSubtree();
144 }
145 }
146
Trace(Visitor * visitor) const147 void HTMLInputElement::Trace(Visitor* visitor) const {
148 visitor->Trace(input_type_);
149 visitor->Trace(input_type_view_);
150 visitor->Trace(list_attribute_target_observer_);
151 visitor->Trace(image_loader_);
152 TextControlElement::Trace(visitor);
153 }
154
HasPendingActivity() const155 bool HTMLInputElement::HasPendingActivity() const {
156 return ImageLoader() && ImageLoader()->HasPendingActivity();
157 }
158
EnsureImageLoader()159 HTMLImageLoader& HTMLInputElement::EnsureImageLoader() {
160 if (!image_loader_)
161 image_loader_ = MakeGarbageCollected<HTMLImageLoader>(this);
162 return *image_loader_;
163 }
164
165 HTMLInputElement::~HTMLInputElement() = default;
166
GetName() const167 const AtomicString& HTMLInputElement::GetName() const {
168 return name_.IsNull() ? g_empty_atom : name_;
169 }
170
FilesFromFileInputFormControlState(const FormControlState & state)171 Vector<String> HTMLInputElement::FilesFromFileInputFormControlState(
172 const FormControlState& state) {
173 return FileInputType::FilesFromFormControlState(state);
174 }
175
ShouldAutocomplete() const176 bool HTMLInputElement::ShouldAutocomplete() const {
177 if (autocomplete_ != kUninitialized)
178 return autocomplete_ == kOn;
179 return TextControlElement::ShouldAutocomplete();
180 }
181
IsValidValue(const String & value) const182 bool HTMLInputElement::IsValidValue(const String& value) const {
183 if (!input_type_->CanSetStringValue()) {
184 NOTREACHED();
185 return false;
186 }
187 return !input_type_->TypeMismatchFor(value) &&
188 !input_type_->StepMismatch(value) &&
189 !input_type_->RangeUnderflow(value) &&
190 !input_type_->RangeOverflow(value) &&
191 !TooLong(value, kIgnoreDirtyFlag) &&
192 !TooShort(value, kIgnoreDirtyFlag) &&
193 !input_type_->PatternMismatch(value) &&
194 !input_type_->ValueMissing(value);
195 }
196
TooLong() const197 bool HTMLInputElement::TooLong() const {
198 return TooLong(value(), kCheckDirtyFlag);
199 }
200
TooShort() const201 bool HTMLInputElement::TooShort() const {
202 return TooShort(value(), kCheckDirtyFlag);
203 }
204
TypeMismatch() const205 bool HTMLInputElement::TypeMismatch() const {
206 return input_type_->TypeMismatch();
207 }
208
ValueMissing() const209 bool HTMLInputElement::ValueMissing() const {
210 return input_type_->ValueMissing(value());
211 }
212
HasBadInput() const213 bool HTMLInputElement::HasBadInput() const {
214 return input_type_view_->HasBadInput();
215 }
216
PatternMismatch() const217 bool HTMLInputElement::PatternMismatch() const {
218 return input_type_->PatternMismatch(value());
219 }
220
TooLong(const String & value,NeedsToCheckDirtyFlag check) const221 bool HTMLInputElement::TooLong(const String& value,
222 NeedsToCheckDirtyFlag check) const {
223 return input_type_->TooLong(value, check);
224 }
225
TooShort(const String & value,NeedsToCheckDirtyFlag check) const226 bool HTMLInputElement::TooShort(const String& value,
227 NeedsToCheckDirtyFlag check) const {
228 return input_type_->TooShort(value, check);
229 }
230
RangeUnderflow() const231 bool HTMLInputElement::RangeUnderflow() const {
232 return input_type_->RangeUnderflow(value());
233 }
234
RangeOverflow() const235 bool HTMLInputElement::RangeOverflow() const {
236 return input_type_->RangeOverflow(value());
237 }
238
validationMessage() const239 String HTMLInputElement::validationMessage() const {
240 if (!willValidate())
241 return String();
242 if (CustomError())
243 return CustomValidationMessage();
244
245 return input_type_->ValidationMessage(*input_type_view_).first;
246 }
247
ValidationSubMessage() const248 String HTMLInputElement::ValidationSubMessage() const {
249 if (CustomError())
250 return String();
251 return input_type_->ValidationMessage(*input_type_view_).second;
252 }
253
Minimum() const254 double HTMLInputElement::Minimum() const {
255 return input_type_->Minimum();
256 }
257
Maximum() const258 double HTMLInputElement::Maximum() const {
259 return input_type_->Maximum();
260 }
261
StepMismatch() const262 bool HTMLInputElement::StepMismatch() const {
263 return input_type_->StepMismatch(value());
264 }
265
GetAllowedValueStep(Decimal * step) const266 bool HTMLInputElement::GetAllowedValueStep(Decimal* step) const {
267 return input_type_->GetAllowedValueStep(step);
268 }
269
CreateStepRange(AnyStepHandling any_step_handling) const270 StepRange HTMLInputElement::CreateStepRange(
271 AnyStepHandling any_step_handling) const {
272 return input_type_->CreateStepRange(any_step_handling);
273 }
274
FindClosestTickMarkValue(const Decimal & value)275 Decimal HTMLInputElement::FindClosestTickMarkValue(const Decimal& value) {
276 return input_type_->FindClosestTickMarkValue(value);
277 }
278
stepUp(int n,ExceptionState & exception_state)279 void HTMLInputElement::stepUp(int n, ExceptionState& exception_state) {
280 input_type_->StepUp(n, exception_state);
281 }
282
stepDown(int n,ExceptionState & exception_state)283 void HTMLInputElement::stepDown(int n, ExceptionState& exception_state) {
284 input_type_->StepUp(-1.0 * n, exception_state);
285 }
286
blur()287 void HTMLInputElement::blur() {
288 input_type_view_->Blur();
289 }
290
DefaultBlur()291 void HTMLInputElement::DefaultBlur() {
292 TextControlElement::blur();
293 }
294
HasCustomFocusLogic() const295 bool HTMLInputElement::HasCustomFocusLogic() const {
296 return input_type_view_->HasCustomFocusLogic();
297 }
298
IsKeyboardFocusable() const299 bool HTMLInputElement::IsKeyboardFocusable() const {
300 return input_type_->IsKeyboardFocusable();
301 }
302
MayTriggerVirtualKeyboard() const303 bool HTMLInputElement::MayTriggerVirtualKeyboard() const {
304 return input_type_->MayTriggerVirtualKeyboard();
305 }
306
ShouldHaveFocusAppearance() const307 bool HTMLInputElement::ShouldHaveFocusAppearance() const {
308 // For FormControlsRefresh don't draw focus ring for an input that has its
309 // popup open.
310 if (::features::IsFormControlsRefreshEnabled() &&
311 input_type_view_->HasOpenedPopup())
312 return false;
313
314 return TextControlElement::ShouldHaveFocusAppearance();
315 }
316
UpdateFocusAppearanceWithOptions(SelectionBehaviorOnFocus selection_behavior,const FocusOptions * options)317 void HTMLInputElement::UpdateFocusAppearanceWithOptions(
318 SelectionBehaviorOnFocus selection_behavior,
319 const FocusOptions* options) {
320 if (IsTextField()) {
321 switch (selection_behavior) {
322 case SelectionBehaviorOnFocus::kReset:
323 select();
324 break;
325 case SelectionBehaviorOnFocus::kRestore:
326 RestoreCachedSelection();
327 break;
328 case SelectionBehaviorOnFocus::kNone:
329 return;
330 }
331 // TODO(tkent): scrollRectToVisible is a workaround of a bug of
332 // FrameSelection::revealSelection(). It doesn't scroll correctly in a
333 // case of RangeSelection. crbug.com/443061.
334 GetDocument().EnsurePaintLocationDataValidForNode(
335 this, DocumentUpdateReason::kFocus);
336 if (!options->preventScroll()) {
337 if (GetLayoutObject()) {
338 GetLayoutObject()->ScrollRectToVisible(
339 BoundingBoxForScrollIntoView(),
340 ScrollAlignment::CreateScrollIntoViewParams());
341 }
342 if (GetDocument().GetFrame())
343 GetDocument().GetFrame()->Selection().RevealSelection();
344 }
345 } else {
346 TextControlElement::UpdateFocusAppearanceWithOptions(selection_behavior,
347 options);
348 }
349 }
350
EndEditing()351 void HTMLInputElement::EndEditing() {
352 DCHECK(GetDocument().IsActive());
353 if (!GetDocument().IsActive())
354 return;
355
356 if (!IsTextField())
357 return;
358
359 LocalFrame* frame = GetDocument().GetFrame();
360 frame->GetSpellChecker().DidEndEditingOnTextField(this);
361 frame->GetPage()->GetChromeClient().DidEndEditingOnTextField(*this);
362
363 MaybeReportPiiMetrics();
364 }
365
DispatchFocusInEvent(const AtomicString & event_type,Element * old_focused_element,mojom::blink::FocusType type,InputDeviceCapabilities * source_capabilities)366 void HTMLInputElement::DispatchFocusInEvent(
367 const AtomicString& event_type,
368 Element* old_focused_element,
369 mojom::blink::FocusType type,
370 InputDeviceCapabilities* source_capabilities) {
371 if (event_type == event_type_names::kDOMFocusIn)
372 input_type_view_->HandleFocusInEvent(old_focused_element, type);
373 HTMLFormControlElementWithState::DispatchFocusInEvent(
374 event_type, old_focused_element, type, source_capabilities);
375 }
376
HandleBlurEvent()377 void HTMLInputElement::HandleBlurEvent() {
378 input_type_view_->HandleBlurEvent();
379 }
380
setType(const AtomicString & type)381 void HTMLInputElement::setType(const AtomicString& type) {
382 setAttribute(html_names::kTypeAttr, type);
383 }
384
InitializeTypeInParsing()385 void HTMLInputElement::InitializeTypeInParsing() {
386 DCHECK(parsing_in_progress_);
387 DCHECK(!input_type_);
388 DCHECK(!input_type_view_);
389
390 const AtomicString& new_type_name =
391 InputType::NormalizeTypeName(FastGetAttribute(html_names::kTypeAttr));
392 input_type_ = InputType::Create(*this, new_type_name);
393 input_type_view_ = input_type_->CreateView();
394 String default_value = FastGetAttribute(html_names::kValueAttr);
395 if (input_type_->GetValueMode() == ValueMode::kValue)
396 non_attribute_value_ = SanitizeValue(default_value);
397 has_been_password_field_ |= new_type_name == input_type_names::kPassword;
398
399 if (input_type_view_->NeedsShadowSubtree()) {
400 CreateUserAgentShadowRoot();
401 CreateShadowSubtree();
402 }
403
404 UpdateWillValidateCache();
405
406 if (!default_value.IsNull())
407 input_type_->WarnIfValueIsInvalid(default_value);
408
409 input_type_view_->UpdateView();
410 }
411
UpdateType()412 void HTMLInputElement::UpdateType() {
413 DCHECK(input_type_);
414 DCHECK(input_type_view_);
415
416 const AtomicString& new_type_name =
417 InputType::NormalizeTypeName(FastGetAttribute(html_names::kTypeAttr));
418 if (input_type_->FormControlType() == new_type_name)
419 return;
420
421 InputType* new_type = InputType::Create(*this, new_type_name);
422 RemoveFromRadioButtonGroup();
423
424 ValueMode old_value_mode = input_type_->GetValueMode();
425 bool did_respect_height_and_width =
426 input_type_->ShouldRespectHeightAndWidthAttributes();
427 bool could_be_successful_submit_button = CanBeSuccessfulSubmitButton();
428
429 input_type_view_->ClosePopupView();
430 input_type_view_->DestroyShadowSubtree();
431 DropInnerEditorElement();
432 SetForceReattachLayoutTree();
433
434 if (input_type_->SupportsRequired() != new_type->SupportsRequired() &&
435 IsRequired()) {
436 PseudoStateChanged(CSSSelector::kPseudoRequired);
437 PseudoStateChanged(CSSSelector::kPseudoOptional);
438 }
439 if (input_type_->SupportsReadOnly() != new_type->SupportsReadOnly()) {
440 PseudoStateChanged(CSSSelector::kPseudoReadOnly);
441 PseudoStateChanged(CSSSelector::kPseudoReadWrite);
442 }
443 if (input_type_->IsCheckable() != new_type->IsCheckable()) {
444 PseudoStateChanged(CSSSelector::kPseudoChecked);
445 }
446 PseudoStateChanged(CSSSelector::kPseudoIndeterminate);
447 if (input_type_->IsSteppable() || new_type->IsSteppable()) {
448 PseudoStateChanged(CSSSelector::kPseudoInRange);
449 PseudoStateChanged(CSSSelector::kPseudoOutOfRange);
450 }
451 if (input_type_->ShouldRespectListAttribute() !=
452 new_type->ShouldRespectListAttribute())
453 PseudoStateChanged(CSSSelector::kPseudoHasDatalist);
454
455 bool placeholder_changed =
456 input_type_->SupportsPlaceholder() != new_type->SupportsPlaceholder();
457
458 has_been_password_field_ |= new_type_name == input_type_names::kPassword;
459
460 // 7. Let previouslySelectable be true if setRangeText() previously applied
461 // to the element, and false otherwise.
462 const bool previously_selectable = input_type_->SupportsSelectionAPI();
463
464 input_type_view_->WillBeDestroyed();
465 input_type_ = new_type;
466 input_type_view_ = input_type_->CreateView();
467 if (input_type_view_->NeedsShadowSubtree()) {
468 EnsureUserAgentShadowRoot();
469 CreateShadowSubtree();
470 }
471
472 UpdateWillValidateCache();
473
474 if (placeholder_changed) {
475 // We need to update the UA shadow and then the placeholder visibility flag
476 // here. Otherwise it would happen as part of attaching the layout tree
477 // which would be too late in order to make style invalidation work for
478 // the upcoming frame.
479 UpdatePlaceholderText();
480 UpdatePlaceholderVisibility();
481 PseudoStateChanged(CSSSelector::kPseudoPlaceholderShown);
482 }
483
484 ValueMode new_value_mode = input_type_->GetValueMode();
485
486 // https://html.spec.whatwg.org/C/#input-type-change
487 //
488 // 1. If the previous state of the element's type attribute put the value IDL
489 // attribute in the value mode, and the element's value is not the empty
490 // string, and the new state of the element's type attribute puts the value
491 // IDL attribute in either the default mode or the default/on mode, then set
492 // the element's value content attribute to the element's value.
493 if (old_value_mode == ValueMode::kValue &&
494 (new_value_mode == ValueMode::kDefault ||
495 new_value_mode == ValueMode::kDefaultOn)) {
496 if (HasDirtyValue())
497 setAttribute(html_names::kValueAttr, AtomicString(non_attribute_value_));
498 non_attribute_value_ = String();
499 has_dirty_value_ = false;
500 }
501 // 2. Otherwise, if the previous state of the element's type attribute put the
502 // value IDL attribute in any mode other than the value mode, and the new
503 // state of the element's type attribute puts the value IDL attribute in the
504 // value mode, then set the value of the element to the value of the value
505 // content attribute, if there is one, or the empty string otherwise, and then
506 // set the control's dirty value flag to false.
507 else if (old_value_mode != ValueMode::kValue &&
508 new_value_mode == ValueMode::kValue) {
509 AtomicString value_string = FastGetAttribute(html_names::kValueAttr);
510 input_type_->WarnIfValueIsInvalid(value_string);
511 non_attribute_value_ = SanitizeValue(value_string);
512 has_dirty_value_ = false;
513 }
514 // 3. Otherwise, if the previous state of the element's type attribute put the
515 // value IDL attribute in any mode other than the filename mode, and the new
516 // state of the element's type attribute puts the value IDL attribute in the
517 // filename mode, then set the value of the element to the empty string.
518 else if (old_value_mode != ValueMode::kFilename &&
519 new_value_mode == ValueMode::kFilename) {
520 non_attribute_value_ = String();
521 has_dirty_value_ = false;
522
523 } else {
524 // ValueMode wasn't changed, or kDefault <-> kDefaultOn.
525 if (!HasDirtyValue()) {
526 String default_value = FastGetAttribute(html_names::kValueAttr);
527 if (!default_value.IsNull())
528 input_type_->WarnIfValueIsInvalid(default_value);
529 }
530
531 if (new_value_mode == ValueMode::kValue) {
532 String new_value = SanitizeValue(non_attribute_value_);
533 if (!EqualIgnoringNullity(new_value, non_attribute_value_)) {
534 if (HasDirtyValue())
535 setValue(new_value);
536 else
537 SetNonDirtyValue(new_value);
538 }
539 }
540 }
541
542 needs_to_update_view_value_ = true;
543 input_type_view_->UpdateView();
544
545 if (did_respect_height_and_width !=
546 input_type_->ShouldRespectHeightAndWidthAttributes()) {
547 DCHECK(GetElementData());
548 AttributeCollection attributes = AttributesWithoutUpdate();
549 if (const Attribute* height = attributes.Find(html_names::kHeightAttr)) {
550 TextControlElement::AttributeChanged(AttributeModificationParams(
551 html_names::kHeightAttr, height->Value(), height->Value(),
552 AttributeModificationReason::kDirectly));
553 }
554 if (const Attribute* width = attributes.Find(html_names::kWidthAttr)) {
555 TextControlElement::AttributeChanged(AttributeModificationParams(
556 html_names::kWidthAttr, width->Value(), width->Value(),
557 AttributeModificationReason::kDirectly));
558 }
559 if (const Attribute* align = attributes.Find(html_names::kAlignAttr)) {
560 TextControlElement::AttributeChanged(AttributeModificationParams(
561 html_names::kAlignAttr, align->Value(), align->Value(),
562 AttributeModificationReason::kDirectly));
563 }
564 }
565
566 // UA Shadow tree was recreated. We need to set selection again. We do it
567 // later in order to avoid force layout.
568 if (GetDocument().FocusedElement() == this)
569 GetDocument().UpdateFocusAppearanceAfterLayout();
570
571 // TODO(tkent): Should we dispatch a change event?
572 ClearValueBeforeFirstUserEdit();
573
574 // 5. Signal a type change for the element. (The Radio Button state uses
575 // this, in particular.)
576 AddToRadioButtonGroup();
577
578 // 8. Let nowSelectable be true if setRangeText() now applies to the element,
579 // and false otherwise.
580 const bool now_selectable = input_type_->SupportsSelectionAPI();
581
582 // 9. If previouslySelectable is false and nowSelectable is true, set the
583 // element's text entry cursor position to the beginning of the text control,
584 // and set its selection direction to "none".
585 if (!previously_selectable && now_selectable)
586 SetSelectionRange(0, 0, kSelectionHasNoDirection);
587
588 SetNeedsValidityCheck();
589 if ((could_be_successful_submit_button || CanBeSuccessfulSubmitButton()) &&
590 formOwner() && isConnected())
591 formOwner()->InvalidateDefaultButtonStyle();
592 NotifyFormStateChanged();
593 }
594
SubtreeHasChanged()595 void HTMLInputElement::SubtreeHasChanged() {
596 input_type_view_->SubtreeHasChanged();
597 // When typing in an input field, childrenChanged is not called, so we need to
598 // force the directionality check.
599 CalculateAndAdjustDirectionality();
600 }
601
FormControlType() const602 const AtomicString& HTMLInputElement::FormControlType() const {
603 return input_type_->FormControlType();
604 }
605
ShouldSaveAndRestoreFormControlState() const606 bool HTMLInputElement::ShouldSaveAndRestoreFormControlState() const {
607 if (!input_type_->ShouldSaveAndRestoreFormControlState())
608 return false;
609 return TextControlElement::ShouldSaveAndRestoreFormControlState();
610 }
611
SaveFormControlState() const612 FormControlState HTMLInputElement::SaveFormControlState() const {
613 return input_type_view_->SaveFormControlState();
614 }
615
RestoreFormControlState(const FormControlState & state)616 void HTMLInputElement::RestoreFormControlState(const FormControlState& state) {
617 input_type_view_->RestoreFormControlState(state);
618 state_restored_ = true;
619 }
620
CanStartSelection() const621 bool HTMLInputElement::CanStartSelection() const {
622 if (!IsTextField())
623 return false;
624 return TextControlElement::CanStartSelection();
625 }
626
selectionStartForBinding(ExceptionState & exception_state) const627 base::Optional<uint32_t> HTMLInputElement::selectionStartForBinding(
628 ExceptionState& exception_state) const {
629 if (!input_type_->SupportsSelectionAPI())
630 return base::nullopt;
631 return TextControlElement::selectionStart();
632 }
633
selectionEndForBinding(ExceptionState & exception_state) const634 base::Optional<uint32_t> HTMLInputElement::selectionEndForBinding(
635 ExceptionState& exception_state) const {
636 if (!input_type_->SupportsSelectionAPI())
637 return base::nullopt;
638 return TextControlElement::selectionEnd();
639 }
640
selectionDirectionForBinding(ExceptionState & exception_state) const641 String HTMLInputElement::selectionDirectionForBinding(
642 ExceptionState& exception_state) const {
643 if (!input_type_->SupportsSelectionAPI()) {
644 return String();
645 }
646 return TextControlElement::selectionDirection();
647 }
648
setSelectionStartForBinding(base::Optional<uint32_t> start,ExceptionState & exception_state)649 void HTMLInputElement::setSelectionStartForBinding(
650 base::Optional<uint32_t> start,
651 ExceptionState& exception_state) {
652 if (!input_type_->SupportsSelectionAPI()) {
653 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
654 "The input element's type ('" +
655 input_type_->FormControlType() +
656 "') does not support selection.");
657 return;
658 }
659 TextControlElement::setSelectionStart(start.value_or(0));
660 }
661
setSelectionEndForBinding(base::Optional<uint32_t> end,ExceptionState & exception_state)662 void HTMLInputElement::setSelectionEndForBinding(
663 base::Optional<uint32_t> end,
664 ExceptionState& exception_state) {
665 if (!input_type_->SupportsSelectionAPI()) {
666 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
667 "The input element's type ('" +
668 input_type_->FormControlType() +
669 "') does not support selection.");
670 return;
671 }
672 TextControlElement::setSelectionEnd(end.value_or(0));
673 }
674
setSelectionDirectionForBinding(const String & direction,ExceptionState & exception_state)675 void HTMLInputElement::setSelectionDirectionForBinding(
676 const String& direction,
677 ExceptionState& exception_state) {
678 if (!input_type_->SupportsSelectionAPI()) {
679 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
680 "The input element's type ('" +
681 input_type_->FormControlType() +
682 "') does not support selection.");
683 return;
684 }
685 TextControlElement::setSelectionDirection(direction);
686 }
687
setSelectionRangeForBinding(unsigned start,unsigned end,ExceptionState & exception_state)688 void HTMLInputElement::setSelectionRangeForBinding(
689 unsigned start,
690 unsigned end,
691 ExceptionState& exception_state) {
692 if (!input_type_->SupportsSelectionAPI()) {
693 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
694 "The input element's type ('" +
695 input_type_->FormControlType() +
696 "') does not support selection.");
697 return;
698 }
699 TextControlElement::setSelectionRangeForBinding(start, end);
700 }
701
setSelectionRangeForBinding(unsigned start,unsigned end,const String & direction,ExceptionState & exception_state)702 void HTMLInputElement::setSelectionRangeForBinding(
703 unsigned start,
704 unsigned end,
705 const String& direction,
706 ExceptionState& exception_state) {
707 if (!input_type_->SupportsSelectionAPI()) {
708 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
709 "The input element's type ('" +
710 input_type_->FormControlType() +
711 "') does not support selection.");
712 return;
713 }
714 TextControlElement::setSelectionRangeForBinding(start, end, direction);
715 }
716
717 // This function can be used to allow tests to set the selection
718 // range for Number inputs, which do not support the ordinary
719 // selection API.
SetSelectionRangeForTesting(unsigned start,unsigned end,ExceptionState & exception_state)720 void HTMLInputElement::SetSelectionRangeForTesting(
721 unsigned start,
722 unsigned end,
723 ExceptionState& exception_state) {
724 if (FormControlType() != input_type_names::kNumber) {
725 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
726 "The input element's type ('" +
727 input_type_->FormControlType() +
728 "') is not a number input.");
729 }
730 TextControlElement::setSelectionRangeForBinding(start, end);
731 }
732
AccessKeyAction(bool send_mouse_events)733 void HTMLInputElement::AccessKeyAction(bool send_mouse_events) {
734 input_type_view_->AccessKeyAction(send_mouse_events);
735 }
736
IsPresentationAttribute(const QualifiedName & name) const737 bool HTMLInputElement::IsPresentationAttribute(
738 const QualifiedName& name) const {
739 // FIXME: Remove type check.
740 if (name == html_names::kVspaceAttr || name == html_names::kHspaceAttr ||
741 name == html_names::kAlignAttr || name == html_names::kWidthAttr ||
742 name == html_names::kHeightAttr ||
743 (name == html_names::kBorderAttr && type() == input_type_names::kImage))
744 return true;
745 return TextControlElement::IsPresentationAttribute(name);
746 }
747
CollectStyleForPresentationAttribute(const QualifiedName & name,const AtomicString & value,MutableCSSPropertyValueSet * style)748 void HTMLInputElement::CollectStyleForPresentationAttribute(
749 const QualifiedName& name,
750 const AtomicString& value,
751 MutableCSSPropertyValueSet* style) {
752 if (name == html_names::kVspaceAttr) {
753 AddHTMLLengthToStyle(style, CSSPropertyID::kMarginTop, value);
754 AddHTMLLengthToStyle(style, CSSPropertyID::kMarginBottom, value);
755 } else if (name == html_names::kHspaceAttr) {
756 AddHTMLLengthToStyle(style, CSSPropertyID::kMarginLeft, value);
757 AddHTMLLengthToStyle(style, CSSPropertyID::kMarginRight, value);
758 } else if (name == html_names::kAlignAttr) {
759 if (input_type_->ShouldRespectAlignAttribute())
760 ApplyAlignmentAttributeToStyle(value, style);
761 } else if (name == html_names::kWidthAttr) {
762 if (input_type_->ShouldRespectHeightAndWidthAttributes())
763 AddHTMLLengthToStyle(style, CSSPropertyID::kWidth, value);
764 } else if (name == html_names::kHeightAttr) {
765 if (input_type_->ShouldRespectHeightAndWidthAttributes())
766 AddHTMLLengthToStyle(style, CSSPropertyID::kHeight, value);
767 } else if (name == html_names::kBorderAttr &&
768 type() == input_type_names::kImage) { // FIXME: Remove type check.
769 ApplyBorderAttributeToStyle(value, style);
770 } else {
771 TextControlElement::CollectStyleForPresentationAttribute(name, value,
772 style);
773 }
774 }
775
ParseAttribute(const AttributeModificationParams & params)776 void HTMLInputElement::ParseAttribute(
777 const AttributeModificationParams& params) {
778 DCHECK(input_type_);
779 DCHECK(input_type_view_);
780 const QualifiedName& name = params.name;
781 const AtomicString& value = params.new_value;
782
783 if (name == html_names::kNameAttr) {
784 RemoveFromRadioButtonGroup();
785 name_ = value;
786 AddToRadioButtonGroup();
787 TextControlElement::ParseAttribute(params);
788 } else if (name == html_names::kAutocompleteAttr) {
789 if (EqualIgnoringASCIICase(value, "off")) {
790 autocomplete_ = kOff;
791 } else {
792 if (value.IsEmpty())
793 autocomplete_ = kUninitialized;
794 else
795 autocomplete_ = kOn;
796 }
797 } else if (name == html_names::kTypeAttr) {
798 UpdateType();
799 } else if (name == html_names::kValueAttr) {
800 // We only need to setChanged if the form is looking at the default value
801 // right now.
802 if (!HasDirtyValue()) {
803 if (input_type_->GetValueMode() == ValueMode::kValue)
804 non_attribute_value_ = SanitizeValue(value);
805 UpdatePlaceholderVisibility();
806 SetNeedsStyleRecalc(
807 kSubtreeStyleChange,
808 StyleChangeReasonForTracing::FromAttribute(html_names::kValueAttr));
809 needs_to_update_view_value_ = true;
810 }
811 SetNeedsValidityCheck();
812 input_type_->WarnIfValueIsInvalidAndElementIsVisible(value);
813 input_type_->InRangeChanged();
814 input_type_view_->ValueAttributeChanged();
815 } else if (name == html_names::kCheckedAttr) {
816 // Another radio button in the same group might be checked by state
817 // restore. We shouldn't call setChecked() even if this has the checked
818 // attribute. So, delay the setChecked() call until
819 // finishParsingChildren() is called if parsing is in progress.
820 if ((!parsing_in_progress_ ||
821 !GetDocument().GetFormController().HasControlStates()) &&
822 !dirty_checkedness_) {
823 setChecked(!value.IsNull());
824 dirty_checkedness_ = false;
825 }
826 PseudoStateChanged(CSSSelector::kPseudoDefault);
827 } else if (name == html_names::kMaxlengthAttr) {
828 SetNeedsValidityCheck();
829 } else if (name == html_names::kMinlengthAttr) {
830 SetNeedsValidityCheck();
831 } else if (name == html_names::kSizeAttr) {
832 unsigned size = 0;
833 if (value.IsEmpty() || !ParseHTMLNonNegativeInteger(value, size) ||
834 size == 0 || size > 0x7fffffffu)
835 size = kDefaultSize;
836 if (size_ != size) {
837 size_ = size;
838 if (GetLayoutObject()) {
839 GetLayoutObject()
840 ->SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
841 layout_invalidation_reason::kAttributeChanged);
842 }
843 }
844 } else if (name == html_names::kAltAttr) {
845 input_type_view_->AltAttributeChanged();
846 } else if (name == html_names::kSrcAttr) {
847 input_type_view_->SrcAttributeChanged();
848 } else if (name == html_names::kUsemapAttr ||
849 name == html_names::kAccesskeyAttr) {
850 // FIXME: ignore for the moment
851 } else if (name == html_names::kOnsearchAttr) {
852 // Search field and slider attributes all just cause updateFromElement to be
853 // called through style recalcing.
854 SetAttributeEventListener(event_type_names::kSearch,
855 JSEventHandlerForContentAttribute::Create(
856 GetExecutionContext(), name, value));
857 } else if (name == html_names::kIncrementalAttr) {
858 UseCounter::Count(GetDocument(), WebFeature::kIncrementalAttribute);
859 } else if (name == html_names::kMinAttr) {
860 input_type_view_->MinOrMaxAttributeChanged();
861 input_type_->SanitizeValueInResponseToMinOrMaxAttributeChange();
862 input_type_->InRangeChanged();
863 SetNeedsValidityCheck();
864 UseCounter::Count(GetDocument(), WebFeature::kMinAttribute);
865 } else if (name == html_names::kMaxAttr) {
866 input_type_view_->MinOrMaxAttributeChanged();
867 input_type_->SanitizeValueInResponseToMinOrMaxAttributeChange();
868 input_type_->InRangeChanged();
869 SetNeedsValidityCheck();
870 UseCounter::Count(GetDocument(), WebFeature::kMaxAttribute);
871 } else if (name == html_names::kMultipleAttr) {
872 input_type_view_->MultipleAttributeChanged();
873 SetNeedsValidityCheck();
874 } else if (name == html_names::kStepAttr) {
875 input_type_view_->StepAttributeChanged();
876 SetNeedsValidityCheck();
877 UseCounter::Count(GetDocument(), WebFeature::kStepAttribute);
878 } else if (name == html_names::kPatternAttr) {
879 SetNeedsValidityCheck();
880 UseCounter::Count(GetDocument(), WebFeature::kPatternAttribute);
881 } else if (name == html_names::kReadonlyAttr) {
882 TextControlElement::ParseAttribute(params);
883 input_type_view_->ReadonlyAttributeChanged();
884 } else if (name == html_names::kListAttr) {
885 has_non_empty_list_ = !value.IsEmpty();
886 if (has_non_empty_list_) {
887 ResetListAttributeTargetObserver();
888 ListAttributeTargetChanged();
889 }
890 PseudoStateChanged(CSSSelector::kPseudoHasDatalist);
891 UseCounter::Count(GetDocument(), WebFeature::kListAttribute);
892 } else if (name == html_names::kWebkitdirectoryAttr) {
893 TextControlElement::ParseAttribute(params);
894 UseCounter::Count(GetDocument(), WebFeature::kPrefixedDirectoryAttribute);
895 } else {
896 if (name == html_names::kFormactionAttr)
897 LogUpdateAttributeIfIsolatedWorldAndInDocument("input", params);
898 TextControlElement::ParseAttribute(params);
899 }
900 }
901
ParserDidSetAttributes()902 void HTMLInputElement::ParserDidSetAttributes() {
903 DCHECK(parsing_in_progress_);
904 InitializeTypeInParsing();
905 }
906
FinishParsingChildren()907 void HTMLInputElement::FinishParsingChildren() {
908 parsing_in_progress_ = false;
909 DCHECK(input_type_);
910 DCHECK(input_type_view_);
911 TextControlElement::FinishParsingChildren();
912 if (!state_restored_) {
913 bool checked = FastHasAttribute(html_names::kCheckedAttr);
914 if (checked)
915 setChecked(checked);
916 dirty_checkedness_ = false;
917 }
918 }
919
LayoutObjectIsNeeded(const ComputedStyle & style) const920 bool HTMLInputElement::LayoutObjectIsNeeded(const ComputedStyle& style) const {
921 return input_type_->LayoutObjectIsNeeded() &&
922 TextControlElement::LayoutObjectIsNeeded(style);
923 }
924
925 // TODO(crbug.com/1040826): Remove this override.
TypeShouldForceLegacyLayout() const926 bool HTMLInputElement::TypeShouldForceLegacyLayout() const {
927 return input_type_view_->TypeShouldForceLegacyLayout();
928 }
929
CreateLayoutObject(const ComputedStyle & style,LegacyLayout legacy)930 LayoutObject* HTMLInputElement::CreateLayoutObject(const ComputedStyle& style,
931 LegacyLayout legacy) {
932 return input_type_view_->CreateLayoutObject(style, legacy);
933 }
934
AttachLayoutTree(AttachContext & context)935 void HTMLInputElement::AttachLayoutTree(AttachContext& context) {
936 TextControlElement::AttachLayoutTree(context);
937 if (GetLayoutObject())
938 input_type_->OnAttachWithLayoutObject();
939 input_type_->CountUsage();
940 }
941
DetachLayoutTree(bool performing_reattach)942 void HTMLInputElement::DetachLayoutTree(bool performing_reattach) {
943 TextControlElement::DetachLayoutTree(performing_reattach);
944 needs_to_update_view_value_ = true;
945 input_type_view_->ClosePopupView();
946 }
947
AltText() const948 String HTMLInputElement::AltText() const {
949 // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
950 // also heavily discussed by Hixie on bugzilla
951 // note this is intentionally different to HTMLImageElement::altText()
952 String alt = FastGetAttribute(html_names::kAltAttr);
953 // fall back to title attribute
954 if (alt.IsNull())
955 alt = FastGetAttribute(html_names::kTitleAttr);
956 if (alt.IsNull())
957 alt = FastGetAttribute(html_names::kValueAttr);
958 if (alt.IsNull())
959 alt = GetLocale().QueryString(IDS_FORM_INPUT_ALT);
960 return alt;
961 }
962
CanBeSuccessfulSubmitButton() const963 bool HTMLInputElement::CanBeSuccessfulSubmitButton() const {
964 return input_type_->CanBeSuccessfulSubmitButton();
965 }
966
IsActivatedSubmit() const967 bool HTMLInputElement::IsActivatedSubmit() const {
968 return is_activated_submit_;
969 }
970
SetActivatedSubmit(bool flag)971 void HTMLInputElement::SetActivatedSubmit(bool flag) {
972 is_activated_submit_ = flag;
973 }
974
AppendToFormData(FormData & form_data)975 void HTMLInputElement::AppendToFormData(FormData& form_data) {
976 if (input_type_->IsFormDataAppendable())
977 input_type_->AppendToFormData(form_data);
978 }
979
ResultForDialogSubmit()980 String HTMLInputElement::ResultForDialogSubmit() {
981 return input_type_->ResultForDialogSubmit();
982 }
983
ResetImpl()984 void HTMLInputElement::ResetImpl() {
985 if (input_type_->GetValueMode() == ValueMode::kValue) {
986 SetNonDirtyValue(DefaultValue());
987 SetNeedsValidityCheck();
988 } else if (input_type_->GetValueMode() == ValueMode::kFilename) {
989 SetNonDirtyValue(String());
990 SetNeedsValidityCheck();
991 }
992
993 setChecked(FastHasAttribute(html_names::kCheckedAttr));
994 dirty_checkedness_ = false;
995 }
996
IsTextField() const997 bool HTMLInputElement::IsTextField() const {
998 return input_type_->IsTextField();
999 }
1000
HasBeenPasswordField() const1001 bool HTMLInputElement::HasBeenPasswordField() const {
1002 return has_been_password_field_;
1003 }
1004
DispatchChangeEventIfNeeded()1005 void HTMLInputElement::DispatchChangeEventIfNeeded() {
1006 if (isConnected() && input_type_->ShouldSendChangeEventAfterCheckedChanged())
1007 DispatchChangeEvent();
1008 }
1009
DispatchInputAndChangeEventIfNeeded()1010 void HTMLInputElement::DispatchInputAndChangeEventIfNeeded() {
1011 if (isConnected() &&
1012 input_type_->ShouldSendChangeEventAfterCheckedChanged()) {
1013 DispatchInputEvent();
1014 DispatchChangeEvent();
1015 }
1016 }
1017
checked() const1018 bool HTMLInputElement::checked() const {
1019 input_type_->ReadingChecked();
1020 return is_checked_;
1021 }
1022
setChecked(bool now_checked,TextFieldEventBehavior event_behavior)1023 void HTMLInputElement::setChecked(bool now_checked,
1024 TextFieldEventBehavior event_behavior) {
1025 dirty_checkedness_ = true;
1026 if (checked() == now_checked)
1027 return;
1028
1029 input_type_->WillUpdateCheckedness(now_checked);
1030 is_checked_ = now_checked;
1031
1032 if (RadioButtonGroupScope* scope = GetRadioButtonGroupScope())
1033 scope->UpdateCheckedState(this);
1034 InvalidateIfHasEffectiveAppearance();
1035 SetNeedsValidityCheck();
1036
1037 // Ideally we'd do this from the layout tree (matching
1038 // LayoutTextView), but it's not possible to do it at the moment
1039 // because of the way the code is structured.
1040 if (GetLayoutObject()) {
1041 if (AXObjectCache* cache =
1042 GetLayoutObject()->GetDocument().ExistingAXObjectCache())
1043 cache->CheckedStateChanged(this);
1044 }
1045
1046 // Only send a change event for items in the document (avoid firing during
1047 // parsing) and don't send a change event for a radio button that's getting
1048 // unchecked to match other browsers. DOM is not a useful standard for this
1049 // because it says only to fire change events at "lose focus" time, which is
1050 // definitely wrong in practice for these types of elements.
1051 if (event_behavior == TextFieldEventBehavior::kDispatchInputAndChangeEvent &&
1052 isConnected() &&
1053 input_type_->ShouldSendChangeEventAfterCheckedChanged()) {
1054 DispatchInputEvent();
1055 }
1056
1057 PseudoStateChanged(CSSSelector::kPseudoChecked);
1058 }
1059
setIndeterminate(bool new_value)1060 void HTMLInputElement::setIndeterminate(bool new_value) {
1061 if (indeterminate() == new_value)
1062 return;
1063
1064 is_indeterminate_ = new_value;
1065
1066 PseudoStateChanged(CSSSelector::kPseudoIndeterminate);
1067
1068 InvalidateIfHasEffectiveAppearance();
1069
1070 if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache())
1071 cache->CheckedStateChanged(this);
1072 }
1073
size() const1074 unsigned HTMLInputElement::size() const {
1075 return size_;
1076 }
1077
SizeShouldIncludeDecoration(int & preferred_size) const1078 bool HTMLInputElement::SizeShouldIncludeDecoration(int& preferred_size) const {
1079 return input_type_view_->SizeShouldIncludeDecoration(kDefaultSize,
1080 preferred_size);
1081 }
1082
CloneNonAttributePropertiesFrom(const Element & source,CloneChildrenFlag flag)1083 void HTMLInputElement::CloneNonAttributePropertiesFrom(const Element& source,
1084 CloneChildrenFlag flag) {
1085 const auto& source_element = To<HTMLInputElement>(source);
1086
1087 non_attribute_value_ = source_element.non_attribute_value_;
1088 has_dirty_value_ = source_element.has_dirty_value_;
1089 setChecked(source_element.is_checked_);
1090 dirty_checkedness_ = source_element.dirty_checkedness_;
1091 is_indeterminate_ = source_element.is_indeterminate_;
1092 input_type_->CopyNonAttributeProperties(source_element);
1093
1094 TextControlElement::CloneNonAttributePropertiesFrom(source, flag);
1095
1096 needs_to_update_view_value_ = true;
1097 input_type_view_->UpdateView();
1098 }
1099
value() const1100 String HTMLInputElement::value() const {
1101 switch (input_type_->GetValueMode()) {
1102 case ValueMode::kFilename:
1103 return input_type_->ValueInFilenameValueMode();
1104 case ValueMode::kDefault:
1105 return FastGetAttribute(html_names::kValueAttr);
1106 case ValueMode::kDefaultOn: {
1107 AtomicString value_string = FastGetAttribute(html_names::kValueAttr);
1108 return value_string.IsNull() ? "on" : value_string;
1109 }
1110 case ValueMode::kValue:
1111 return non_attribute_value_;
1112 }
1113 NOTREACHED();
1114 return g_empty_string;
1115 }
1116
ValueOrDefaultLabel() const1117 String HTMLInputElement::ValueOrDefaultLabel() const {
1118 String value = this->value();
1119 if (!value.IsNull())
1120 return value;
1121 return input_type_->DefaultLabel();
1122 }
1123
SetValueForUser(const String & value)1124 void HTMLInputElement::SetValueForUser(const String& value) {
1125 // Call setValue and make it send a change event.
1126 setValue(value, TextFieldEventBehavior::kDispatchChangeEvent);
1127 }
1128
SetSuggestedValue(const String & value)1129 void HTMLInputElement::SetSuggestedValue(const String& value) {
1130 if (!input_type_->CanSetSuggestedValue())
1131 return;
1132 needs_to_update_view_value_ = true;
1133 TextControlElement::SetSuggestedValue(SanitizeValue(value));
1134 SetNeedsStyleRecalc(
1135 kSubtreeStyleChange,
1136 StyleChangeReasonForTracing::Create(style_change_reason::kControlValue));
1137 input_type_view_->UpdateView();
1138 }
1139
SetEditingValue(const String & value)1140 void HTMLInputElement::SetEditingValue(const String& value) {
1141 if (!GetLayoutObject() || !IsTextField())
1142 return;
1143 SetInnerEditorValue(value);
1144 SubtreeHasChanged();
1145
1146 unsigned max = value.length();
1147 SetSelectionRange(max, max);
1148 DispatchInputEvent();
1149 }
1150
SetInnerEditorValue(const String & value)1151 void HTMLInputElement::SetInnerEditorValue(const String& value) {
1152 TextControlElement::SetInnerEditorValue(value);
1153 needs_to_update_view_value_ = false;
1154 }
1155
setValue(const String & value,ExceptionState & exception_state,TextFieldEventBehavior event_behavior)1156 void HTMLInputElement::setValue(const String& value,
1157 ExceptionState& exception_state,
1158 TextFieldEventBehavior event_behavior) {
1159 // FIXME: Remove type check.
1160 if (type() == input_type_names::kFile && !value.IsEmpty()) {
1161 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
1162 "This input element accepts a filename, "
1163 "which may only be programmatically set "
1164 "to the empty string.");
1165 return;
1166 }
1167 setValue(value, event_behavior);
1168 }
1169
setValue(const String & value,TextFieldEventBehavior event_behavior,TextControlSetValueSelection selection)1170 void HTMLInputElement::setValue(const String& value,
1171 TextFieldEventBehavior event_behavior,
1172 TextControlSetValueSelection selection) {
1173 input_type_->WarnIfValueIsInvalidAndElementIsVisible(value);
1174 if (!input_type_->CanSetValue(value))
1175 return;
1176
1177 // Clear the suggested value. Use the base class version to not trigger a view
1178 // update.
1179 TextControlElement::SetSuggestedValue(String());
1180
1181 // Set autofilled to false, as the value might have been set by the website.
1182 // If the field was autofilled, it'll be set to true from that method.
1183 SetAutofillState(WebAutofillState::kNotFilled);
1184
1185 EventQueueScope scope;
1186 String sanitized_value = SanitizeValue(value);
1187 bool value_changed = sanitized_value != this->value();
1188
1189 SetLastChangeWasNotUserEdit();
1190 needs_to_update_view_value_ = true;
1191
1192 input_type_->SetValue(sanitized_value, value_changed, event_behavior,
1193 selection);
1194 input_type_view_->DidSetValue(sanitized_value, value_changed);
1195
1196 if (value_changed) {
1197 NotifyFormStateChanged();
1198 if (value.IsEmpty() && HasBeenPasswordField() && GetDocument().GetPage()) {
1199 GetDocument().GetPage()->GetChromeClient().PasswordFieldReset(*this);
1200 }
1201 }
1202 }
1203
SetNonAttributeValue(const String & sanitized_value)1204 void HTMLInputElement::SetNonAttributeValue(const String& sanitized_value) {
1205 // This is a common code for ValueMode::kValue.
1206 DCHECK_EQ(input_type_->GetValueMode(), ValueMode::kValue);
1207 non_attribute_value_ = sanitized_value;
1208 has_dirty_value_ = true;
1209 SetNeedsValidityCheck();
1210 input_type_->InRangeChanged();
1211 }
1212
SetNonAttributeValueByUserEdit(const String & sanitized_value)1213 void HTMLInputElement::SetNonAttributeValueByUserEdit(
1214 const String& sanitized_value) {
1215 SetValueBeforeFirstUserEditIfNotSet();
1216 SetNonAttributeValue(sanitized_value);
1217 CheckIfValueWasReverted(sanitized_value);
1218 }
1219
SetNonDirtyValue(const String & new_value)1220 void HTMLInputElement::SetNonDirtyValue(const String& new_value) {
1221 setValue(new_value);
1222 has_dirty_value_ = false;
1223 }
1224
HasDirtyValue() const1225 bool HTMLInputElement::HasDirtyValue() const {
1226 return has_dirty_value_;
1227 }
1228
UpdateView()1229 void HTMLInputElement::UpdateView() {
1230 input_type_view_->UpdateView();
1231 }
1232
valueAsDate(ScriptState * script_state) const1233 ScriptValue HTMLInputElement::valueAsDate(ScriptState* script_state) const {
1234 UseCounter::Count(GetDocument(), WebFeature::kInputElementValueAsDateGetter);
1235 // TODO(crbug.com/988343): InputType::ValueAsDate() should return
1236 // base::Optional<base::Time>.
1237 double date = input_type_->ValueAsDate();
1238 v8::Isolate* isolate = script_state->GetIsolate();
1239 if (!std::isfinite(date))
1240 return ScriptValue::CreateNull(isolate);
1241 return ScriptValue(isolate, ToV8(base::Time::FromJsTime(date), script_state));
1242 }
1243
setValueAsDate(ScriptState * script_state,const ScriptValue & value,ExceptionState & exception_state)1244 void HTMLInputElement::setValueAsDate(ScriptState* script_state,
1245 const ScriptValue& value,
1246 ExceptionState& exception_state) {
1247 UseCounter::Count(GetDocument(), WebFeature::kInputElementValueAsDateSetter);
1248 base::Optional<base::Time> date =
1249 NativeValueTraits<IDLNullable<IDLDate>>::NativeValue(
1250 script_state->GetIsolate(), value.V8Value(), exception_state);
1251 if (exception_state.HadException())
1252 return;
1253 input_type_->SetValueAsDate(date, exception_state);
1254 }
1255
valueAsNumber() const1256 double HTMLInputElement::valueAsNumber() const {
1257 return input_type_->ValueAsDouble();
1258 }
1259
setValueAsNumber(double new_value,ExceptionState & exception_state,TextFieldEventBehavior event_behavior)1260 void HTMLInputElement::setValueAsNumber(double new_value,
1261 ExceptionState& exception_state,
1262 TextFieldEventBehavior event_behavior) {
1263 // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-input-element-attributes.html#dom-input-valueasnumber
1264 // On setting, if the new value is infinite, then throw a TypeError exception.
1265 if (std::isinf(new_value)) {
1266 exception_state.ThrowTypeError(
1267 ExceptionMessages::NotAFiniteNumber(new_value));
1268 return;
1269 }
1270 input_type_->SetValueAsDouble(new_value, event_behavior, exception_state);
1271 }
1272
RatioValue() const1273 Decimal HTMLInputElement::RatioValue() const {
1274 DCHECK_EQ(type(), input_type_names::kRange);
1275 const StepRange step_range(CreateStepRange(kRejectAny));
1276 const Decimal old_value =
1277 ParseToDecimalForNumberType(value(), step_range.DefaultValue());
1278 return step_range.ProportionFromValue(step_range.ClampValue(old_value));
1279 }
1280
SetValueFromRenderer(const String & value)1281 void HTMLInputElement::SetValueFromRenderer(const String& value) {
1282 // File upload controls will never use this.
1283 DCHECK_NE(type(), input_type_names::kFile);
1284
1285 // Clear the suggested value. Use the base class version to not trigger a view
1286 // update.
1287 TextControlElement::SetSuggestedValue(String());
1288
1289 // Renderer and our event handler are responsible for sanitizing values.
1290 DCHECK(value == input_type_->SanitizeUserInputValue(value) ||
1291 input_type_->SanitizeUserInputValue(value).IsEmpty());
1292
1293 DCHECK(!value.IsNull());
1294 SetValueBeforeFirstUserEditIfNotSet();
1295 non_attribute_value_ = value;
1296 has_dirty_value_ = true;
1297 needs_to_update_view_value_ = false;
1298 CheckIfValueWasReverted(value);
1299
1300 // Input event is fired by the Node::defaultEventHandler for editable
1301 // controls.
1302 if (!IsTextField())
1303 DispatchInputEvent();
1304 NotifyFormStateChanged();
1305
1306 SetNeedsValidityCheck();
1307
1308 // Clear autofill flag (and yellow background) on user edit.
1309 SetAutofillState(WebAutofillState::kNotFilled);
1310 }
1311
PreDispatchEventHandler(Event & event)1312 EventDispatchHandlingState* HTMLInputElement::PreDispatchEventHandler(
1313 Event& event) {
1314 if (event.type() == event_type_names::kTextInput &&
1315 input_type_view_->ShouldSubmitImplicitly(event)) {
1316 event.stopPropagation();
1317 return nullptr;
1318 }
1319 if (event.type() != event_type_names::kClick)
1320 return nullptr;
1321
1322 auto* mouse_event = DynamicTo<MouseEvent>(event);
1323 if (!mouse_event ||
1324 mouse_event->button() !=
1325 static_cast<int16_t>(WebPointerProperties::Button::kLeft))
1326 return nullptr;
1327 return input_type_view_->WillDispatchClick();
1328 }
1329
PostDispatchEventHandler(Event & event,EventDispatchHandlingState * state)1330 void HTMLInputElement::PostDispatchEventHandler(
1331 Event& event,
1332 EventDispatchHandlingState* state) {
1333 if (!state)
1334 return;
1335 input_type_view_->DidDispatchClick(event,
1336 *static_cast<ClickHandlingState*>(state));
1337 }
1338
DefaultEventHandler(Event & evt)1339 void HTMLInputElement::DefaultEventHandler(Event& evt) {
1340 auto* mouse_event = DynamicTo<MouseEvent>(evt);
1341 if (mouse_event && evt.type() == event_type_names::kClick &&
1342 mouse_event->button() ==
1343 static_cast<int16_t>(WebPointerProperties::Button::kLeft)) {
1344 input_type_view_->HandleClickEvent(To<MouseEvent>(evt));
1345 if (evt.DefaultHandled())
1346 return;
1347 }
1348
1349 auto* keyboad_event = DynamicTo<KeyboardEvent>(evt);
1350 if (keyboad_event && evt.type() == event_type_names::kKeydown) {
1351 input_type_view_->HandleKeydownEvent(*keyboad_event);
1352 if (evt.DefaultHandled())
1353 return;
1354 }
1355
1356 // Call the base event handler before any of our own event handling for almost
1357 // all events in text fields. Makes editing keyboard handling take precedence
1358 // over the keydown and keypress handling in this function.
1359 bool call_base_class_early =
1360 IsTextField() && (evt.type() == event_type_names::kKeydown ||
1361 evt.type() == event_type_names::kKeypress);
1362 if (call_base_class_early) {
1363 TextControlElement::DefaultEventHandler(evt);
1364 if (evt.DefaultHandled())
1365 return;
1366 }
1367
1368 // DOMActivate events cause the input to be "activated" - in the case of image
1369 // and submit inputs, this means actually submitting the form. For reset
1370 // inputs, the form is reset. These events are sent when the user clicks on
1371 // the element, or presses enter while it is the active element. JavaScript
1372 // code wishing to activate the element must dispatch a DOMActivate event - a
1373 // click event will not do the job.
1374 if (evt.type() == event_type_names::kDOMActivate) {
1375 input_type_view_->HandleDOMActivateEvent(evt);
1376 if (evt.DefaultHandled())
1377 return;
1378 }
1379
1380 // Use key press event here since sending simulated mouse events
1381 // on key down blocks the proper sending of the key press event.
1382 if (keyboad_event && evt.type() == event_type_names::kKeypress) {
1383 input_type_view_->HandleKeypressEvent(*keyboad_event);
1384 if (evt.DefaultHandled())
1385 return;
1386 }
1387
1388 if (keyboad_event && evt.type() == event_type_names::kKeyup) {
1389 input_type_view_->HandleKeyupEvent(*keyboad_event);
1390 if (evt.DefaultHandled())
1391 return;
1392 }
1393
1394 if (input_type_view_->ShouldSubmitImplicitly(evt)) {
1395 // FIXME: Remove type check.
1396 if (type() == input_type_names::kSearch) {
1397 GetDocument()
1398 .GetTaskRunner(TaskType::kUserInteraction)
1399 ->PostTask(FROM_HERE, WTF::Bind(&HTMLInputElement::OnSearch,
1400 WrapPersistent(this)));
1401 }
1402 // Form submission finishes editing, just as loss of focus does.
1403 // If there was a change, send the event now.
1404 DispatchFormControlChangeEvent();
1405
1406 HTMLFormElement* form_for_submission =
1407 input_type_view_->FormForSubmission();
1408 // Form may never have been present, or may have been destroyed by code
1409 // responding to the change event.
1410 if (form_for_submission) {
1411 form_for_submission->SubmitImplicitly(evt,
1412 CanTriggerImplicitSubmission());
1413 }
1414 evt.SetDefaultHandled();
1415 return;
1416 }
1417
1418 if (evt.IsBeforeTextInsertedEvent()) {
1419 input_type_view_->HandleBeforeTextInsertedEvent(
1420 static_cast<BeforeTextInsertedEvent&>(evt));
1421 }
1422
1423 if (mouse_event && evt.type() == event_type_names::kMousedown) {
1424 input_type_view_->HandleMouseDownEvent(*mouse_event);
1425 if (evt.DefaultHandled())
1426 return;
1427 }
1428
1429 input_type_view_->ForwardEvent(evt);
1430
1431 if (!call_base_class_early && !evt.DefaultHandled())
1432 TextControlElement::DefaultEventHandler(evt);
1433 }
1434
CreateShadowSubtree()1435 void HTMLInputElement::CreateShadowSubtree() {
1436 input_type_view_->CreateShadowSubtree();
1437 }
1438
HasActivationBehavior() const1439 bool HTMLInputElement::HasActivationBehavior() const {
1440 return true;
1441 }
1442
WillRespondToMouseClickEvents()1443 bool HTMLInputElement::WillRespondToMouseClickEvents() {
1444 // FIXME: Consider implementing willRespondToMouseClickEvents() in InputType
1445 // if more accurate results are necessary.
1446 if (!IsDisabledFormControl())
1447 return true;
1448
1449 return TextControlElement::WillRespondToMouseClickEvents();
1450 }
1451
IsURLAttribute(const Attribute & attribute) const1452 bool HTMLInputElement::IsURLAttribute(const Attribute& attribute) const {
1453 return attribute.GetName() == html_names::kSrcAttr ||
1454 attribute.GetName() == html_names::kFormactionAttr ||
1455 TextControlElement::IsURLAttribute(attribute);
1456 }
1457
HasLegalLinkAttribute(const QualifiedName & name) const1458 bool HTMLInputElement::HasLegalLinkAttribute(const QualifiedName& name) const {
1459 return input_type_->HasLegalLinkAttribute(name) ||
1460 TextControlElement::HasLegalLinkAttribute(name);
1461 }
1462
SubResourceAttributeName() const1463 const QualifiedName& HTMLInputElement::SubResourceAttributeName() const {
1464 return input_type_->SubResourceAttributeName();
1465 }
1466
DefaultValue() const1467 const AtomicString& HTMLInputElement::DefaultValue() const {
1468 return FastGetAttribute(html_names::kValueAttr);
1469 }
1470
IsRFC2616TokenCharacter(UChar ch)1471 static inline bool IsRFC2616TokenCharacter(UChar ch) {
1472 return IsASCII(ch) && ch > ' ' && ch != '"' && ch != '(' && ch != ')' &&
1473 ch != ',' && ch != '/' && (ch < ':' || ch > '@') &&
1474 (ch < '[' || ch > ']') && ch != '{' && ch != '}' && ch != 0x7f;
1475 }
1476
IsValidMIMEType(const String & type)1477 static bool IsValidMIMEType(const String& type) {
1478 size_t slash_position = type.find('/');
1479 if (slash_position == kNotFound || !slash_position ||
1480 slash_position == type.length() - 1)
1481 return false;
1482 for (wtf_size_t i = 0; i < type.length(); ++i) {
1483 if (!IsRFC2616TokenCharacter(type[i]) && i != slash_position)
1484 return false;
1485 }
1486 return true;
1487 }
1488
IsValidFileExtension(const String & type)1489 static bool IsValidFileExtension(const String& type) {
1490 if (type.length() < 2)
1491 return false;
1492 return type[0] == '.';
1493 }
1494
ParseAcceptAttribute(const String & accept_string,bool (* predicate)(const String &))1495 static Vector<String> ParseAcceptAttribute(const String& accept_string,
1496 bool (*predicate)(const String&)) {
1497 Vector<String> types;
1498 if (accept_string.IsEmpty())
1499 return types;
1500
1501 Vector<String> split_types;
1502 accept_string.Split(',', false, split_types);
1503 for (const String& split_type : split_types) {
1504 String trimmed_type = StripLeadingAndTrailingHTMLSpaces(split_type);
1505 if (trimmed_type.IsEmpty())
1506 continue;
1507 if (!predicate(trimmed_type))
1508 continue;
1509 types.push_back(trimmed_type.DeprecatedLower());
1510 }
1511
1512 return types;
1513 }
1514
AcceptMIMETypes() const1515 Vector<String> HTMLInputElement::AcceptMIMETypes() const {
1516 return ParseAcceptAttribute(FastGetAttribute(html_names::kAcceptAttr),
1517 IsValidMIMEType);
1518 }
1519
AcceptFileExtensions() const1520 Vector<String> HTMLInputElement::AcceptFileExtensions() const {
1521 return ParseAcceptAttribute(FastGetAttribute(html_names::kAcceptAttr),
1522 IsValidFileExtension);
1523 }
1524
Alt() const1525 const AtomicString& HTMLInputElement::Alt() const {
1526 return FastGetAttribute(html_names::kAltAttr);
1527 }
1528
Multiple() const1529 bool HTMLInputElement::Multiple() const {
1530 return FastHasAttribute(html_names::kMultipleAttr);
1531 }
1532
setSize(unsigned size,ExceptionState & exception_state)1533 void HTMLInputElement::setSize(unsigned size, ExceptionState& exception_state) {
1534 if (size == 0) {
1535 exception_state.ThrowDOMException(
1536 DOMExceptionCode::kIndexSizeError,
1537 "The value provided is 0, which is an invalid size.");
1538 } else {
1539 SetUnsignedIntegralAttribute(html_names::kSizeAttr,
1540 size ? size : kDefaultSize, kDefaultSize);
1541 }
1542 }
1543
Src() const1544 KURL HTMLInputElement::Src() const {
1545 return GetDocument().CompleteURL(FastGetAttribute(html_names::kSrcAttr));
1546 }
1547
files() const1548 FileList* HTMLInputElement::files() const {
1549 return input_type_->Files();
1550 }
1551
setFiles(FileList * files)1552 void HTMLInputElement::setFiles(FileList* files) {
1553 input_type_->SetFiles(files);
1554 }
1555
ReceiveDroppedFiles(const DragData * drag_data)1556 bool HTMLInputElement::ReceiveDroppedFiles(const DragData* drag_data) {
1557 return input_type_->ReceiveDroppedFiles(drag_data);
1558 }
1559
DroppedFileSystemId()1560 String HTMLInputElement::DroppedFileSystemId() {
1561 return input_type_->DroppedFileSystemId();
1562 }
1563
CanReceiveDroppedFiles() const1564 bool HTMLInputElement::CanReceiveDroppedFiles() const {
1565 return can_receive_dropped_files_;
1566 }
1567
SetCanReceiveDroppedFiles(bool can_receive_dropped_files)1568 void HTMLInputElement::SetCanReceiveDroppedFiles(
1569 bool can_receive_dropped_files) {
1570 if (!!can_receive_dropped_files_ == can_receive_dropped_files)
1571 return;
1572 can_receive_dropped_files_ = can_receive_dropped_files;
1573 if (HTMLInputElement* button = UploadButton())
1574 button->SetActive(can_receive_dropped_files);
1575 }
1576
UploadButton() const1577 HTMLInputElement* HTMLInputElement::UploadButton() const {
1578 return input_type_view_->UploadButton();
1579 }
1580
SanitizeValue(const String & proposed_value) const1581 String HTMLInputElement::SanitizeValue(const String& proposed_value) const {
1582 return input_type_->SanitizeValue(proposed_value);
1583 }
1584
LocalizeValue(const String & proposed_value) const1585 String HTMLInputElement::LocalizeValue(const String& proposed_value) const {
1586 if (proposed_value.IsNull())
1587 return proposed_value;
1588 return input_type_->LocalizeValue(proposed_value);
1589 }
1590
IsInRange() const1591 bool HTMLInputElement::IsInRange() const {
1592 return willValidate() && input_type_->IsInRange(value());
1593 }
1594
IsOutOfRange() const1595 bool HTMLInputElement::IsOutOfRange() const {
1596 return willValidate() && input_type_->IsOutOfRange(value());
1597 }
1598
IsRequiredFormControl() const1599 bool HTMLInputElement::IsRequiredFormControl() const {
1600 return input_type_->SupportsRequired() && IsRequired();
1601 }
1602
MatchesReadOnlyPseudoClass() const1603 bool HTMLInputElement::MatchesReadOnlyPseudoClass() const {
1604 return input_type_->SupportsReadOnly() && IsReadOnly();
1605 }
1606
MatchesReadWritePseudoClass() const1607 bool HTMLInputElement::MatchesReadWritePseudoClass() const {
1608 return input_type_->SupportsReadOnly() && !IsReadOnly();
1609 }
1610
OnSearch()1611 void HTMLInputElement::OnSearch() {
1612 input_type_->DispatchSearchEvent();
1613 }
1614
UpdateClearButtonVisibility()1615 void HTMLInputElement::UpdateClearButtonVisibility() {
1616 input_type_view_->UpdateClearButtonVisibility();
1617 }
1618
WillChangeForm()1619 void HTMLInputElement::WillChangeForm() {
1620 if (input_type_)
1621 RemoveFromRadioButtonGroup();
1622 TextControlElement::WillChangeForm();
1623 }
1624
DidChangeForm()1625 void HTMLInputElement::DidChangeForm() {
1626 TextControlElement::DidChangeForm();
1627 if (input_type_)
1628 AddToRadioButtonGroup();
1629 }
1630
InsertedInto(ContainerNode & insertion_point)1631 Node::InsertionNotificationRequest HTMLInputElement::InsertedInto(
1632 ContainerNode& insertion_point) {
1633 TextControlElement::InsertedInto(insertion_point);
1634 if (insertion_point.isConnected() && !Form())
1635 AddToRadioButtonGroup();
1636 ResetListAttributeTargetObserver();
1637 LogAddElementIfIsolatedWorldAndInDocument("input", html_names::kTypeAttr,
1638 html_names::kFormactionAttr);
1639 return kInsertionShouldCallDidNotifySubtreeInsertions;
1640 }
1641
RemovedFrom(ContainerNode & insertion_point)1642 void HTMLInputElement::RemovedFrom(ContainerNode& insertion_point) {
1643 input_type_view_->ClosePopupView();
1644 if (insertion_point.isConnected() && !Form())
1645 RemoveFromRadioButtonGroup();
1646 TextControlElement::RemovedFrom(insertion_point);
1647 DCHECK(!isConnected());
1648 ResetListAttributeTargetObserver();
1649 }
1650
DidMoveToNewDocument(Document & old_document)1651 void HTMLInputElement::DidMoveToNewDocument(Document& old_document) {
1652 if (ImageLoader())
1653 ImageLoader()->ElementDidMoveToNewDocument();
1654
1655 // FIXME: Remove type check.
1656 if (type() == input_type_names::kRadio)
1657 GetTreeScope().GetRadioButtonGroupScope().RemoveButton(this);
1658
1659 TextControlElement::DidMoveToNewDocument(old_document);
1660 }
1661
RecalcWillValidate() const1662 bool HTMLInputElement::RecalcWillValidate() const {
1663 return input_type_->SupportsValidation() &&
1664 TextControlElement::RecalcWillValidate();
1665 }
1666
RequiredAttributeChanged()1667 void HTMLInputElement::RequiredAttributeChanged() {
1668 TextControlElement::RequiredAttributeChanged();
1669 if (RadioButtonGroupScope* scope = GetRadioButtonGroupScope())
1670 scope->RequiredAttributeChanged(this);
1671 input_type_view_->RequiredAttributeChanged();
1672 }
1673
DisabledAttributeChanged()1674 void HTMLInputElement::DisabledAttributeChanged() {
1675 TextControlElement::DisabledAttributeChanged();
1676 input_type_view_->DisabledAttributeChanged();
1677 }
1678
SelectColorInColorChooser(const Color & color)1679 void HTMLInputElement::SelectColorInColorChooser(const Color& color) {
1680 if (ColorChooserClient* client = input_type_->GetColorChooserClient())
1681 client->DidChooseColor(color);
1682 }
1683
EndColorChooserForTesting()1684 void HTMLInputElement::EndColorChooserForTesting() {
1685 input_type_view_->ClosePopupView();
1686 }
1687
list() const1688 HTMLElement* HTMLInputElement::list() const {
1689 return DataList();
1690 }
1691
DataList() const1692 HTMLDataListElement* HTMLInputElement::DataList() const {
1693 if (!has_non_empty_list_)
1694 return nullptr;
1695
1696 if (!input_type_->ShouldRespectListAttribute())
1697 return nullptr;
1698
1699 return DynamicTo<HTMLDataListElement>(
1700 GetTreeScope().getElementById(FastGetAttribute(html_names::kListAttr)));
1701 }
1702
HasValidDataListOptions() const1703 bool HTMLInputElement::HasValidDataListOptions() const {
1704 HTMLDataListElement* data_list = DataList();
1705 if (!data_list)
1706 return false;
1707 HTMLDataListOptionsCollection* options = data_list->options();
1708 for (unsigned i = 0; HTMLOptionElement* option = options->Item(i); ++i) {
1709 if (!option->value().IsEmpty() && !option->IsDisabledFormControl())
1710 return true;
1711 }
1712 return false;
1713 }
1714
1715 HeapVector<Member<HTMLOptionElement>>
FilteredDataListOptions() const1716 HTMLInputElement::FilteredDataListOptions() const {
1717 HeapVector<Member<HTMLOptionElement>> filtered;
1718 HTMLDataListElement* data_list = DataList();
1719 if (!data_list)
1720 return filtered;
1721
1722 String value = InnerEditorValue();
1723 if (Multiple() && type() == input_type_names::kEmail) {
1724 Vector<String> emails;
1725 value.Split(',', true, emails);
1726 if (!emails.IsEmpty())
1727 value = emails.back().StripWhiteSpace();
1728 }
1729
1730 HTMLDataListOptionsCollection* options = data_list->options();
1731 filtered.ReserveCapacity(options->length());
1732 value = value.FoldCase();
1733 for (unsigned i = 0; i < options->length(); ++i) {
1734 HTMLOptionElement* option = options->Item(i);
1735 DCHECK(option);
1736 if (!value.IsEmpty()) {
1737 // Firefox shows OPTIONs with matched labels, Edge shows OPTIONs
1738 // with matches values. We show both.
1739 if (option->value().FoldCase().Find(value) == kNotFound &&
1740 option->label().FoldCase().Find(value) == kNotFound)
1741 continue;
1742 }
1743 if (option->value().IsEmpty() || option->IsDisabledFormControl())
1744 continue;
1745 filtered.push_back(option);
1746 }
1747 return filtered;
1748 }
1749
SetListAttributeTargetObserver(ListAttributeTargetObserver * new_observer)1750 void HTMLInputElement::SetListAttributeTargetObserver(
1751 ListAttributeTargetObserver* new_observer) {
1752 if (list_attribute_target_observer_)
1753 list_attribute_target_observer_->Unregister();
1754 list_attribute_target_observer_ = new_observer;
1755 }
1756
ResetListAttributeTargetObserver()1757 void HTMLInputElement::ResetListAttributeTargetObserver() {
1758 const AtomicString& value = FastGetAttribute(html_names::kListAttr);
1759 if (!value.IsNull() && isConnected()) {
1760 SetListAttributeTargetObserver(
1761 MakeGarbageCollected<ListAttributeTargetObserver>(value, this));
1762 } else {
1763 SetListAttributeTargetObserver(nullptr);
1764 }
1765 }
1766
ListAttributeTargetChanged()1767 void HTMLInputElement::ListAttributeTargetChanged() {
1768 input_type_view_->ListAttributeTargetChanged();
1769 PseudoStateChanged(CSSSelector::kPseudoHasDatalist);
1770 }
1771
IsSteppable() const1772 bool HTMLInputElement::IsSteppable() const {
1773 return input_type_->IsSteppable();
1774 }
1775
IsTextButton() const1776 bool HTMLInputElement::IsTextButton() const {
1777 return input_type_->IsTextButton();
1778 }
1779
IsEnumeratable() const1780 bool HTMLInputElement::IsEnumeratable() const {
1781 return input_type_->IsEnumeratable();
1782 }
1783
IsLabelable() const1784 bool HTMLInputElement::IsLabelable() const {
1785 return input_type_->IsInteractiveContent();
1786 }
1787
MatchesDefaultPseudoClass() const1788 bool HTMLInputElement::MatchesDefaultPseudoClass() const {
1789 return input_type_->MatchesDefaultPseudoClass();
1790 }
1791
scrollWidth()1792 int HTMLInputElement::scrollWidth() {
1793 if (!IsTextField())
1794 return TextControlElement::scrollWidth();
1795 // If in preview state, fake the scroll width to prevent that any information
1796 // about the suggested content can be derived from the size.
1797 if (!SuggestedValue().IsEmpty())
1798 return clientWidth();
1799
1800 GetDocument().UpdateStyleAndLayoutForNode(this,
1801 DocumentUpdateReason::kJavaScript);
1802 const auto* editor = InnerEditorElement();
1803 const auto* editor_box = editor ? editor->GetLayoutBox() : nullptr;
1804 const auto* box = GetLayoutBox();
1805 if (!editor_box || !box)
1806 return TextControlElement::scrollWidth();
1807 // Adjust scrollWidth to include input element horizontal paddings and
1808 // decoration width.
1809 LayoutUnit adjustment = box->ClientWidth() - editor_box->ClientWidth();
1810 return AdjustForAbsoluteZoom::AdjustLayoutUnit(
1811 editor_box->ScrollWidth() + adjustment, box->StyleRef())
1812 .Round();
1813 }
1814
scrollHeight()1815 int HTMLInputElement::scrollHeight() {
1816 if (!IsTextField())
1817 return TextControlElement::scrollHeight();
1818
1819 // If in preview state, fake the scroll height to prevent that any information
1820 // about the suggested content can be derived from the size.
1821 if (!SuggestedValue().IsEmpty())
1822 return clientHeight();
1823
1824 GetDocument().UpdateStyleAndLayoutForNode(this,
1825 DocumentUpdateReason::kJavaScript);
1826 const auto* editor = InnerEditorElement();
1827 const auto* editor_box = editor ? editor->GetLayoutBox() : nullptr;
1828 const auto* box = GetLayoutBox();
1829 if (!editor_box || !box)
1830 return TextControlElement::scrollHeight();
1831 // Adjust scrollHeight to include input element vertical paddings and
1832 // decoration height.
1833 LayoutUnit adjustment = box->ClientHeight() - editor_box->ClientHeight();
1834 return AdjustForAbsoluteZoom::AdjustLayoutUnit(
1835 editor_box->ScrollHeight() + adjustment, box->StyleRef())
1836 .Round();
1837 }
1838
ShouldAppearChecked() const1839 bool HTMLInputElement::ShouldAppearChecked() const {
1840 return checked() && input_type_->IsCheckable();
1841 }
1842
SetPlaceholderVisibility(bool visible)1843 void HTMLInputElement::SetPlaceholderVisibility(bool visible) {
1844 is_placeholder_visible_ = visible;
1845 }
1846
SupportsPlaceholder() const1847 bool HTMLInputElement::SupportsPlaceholder() const {
1848 return input_type_->SupportsPlaceholder();
1849 }
1850
UpdatePlaceholderText()1851 void HTMLInputElement::UpdatePlaceholderText() {
1852 return input_type_view_->UpdatePlaceholderText();
1853 }
1854
GetPlaceholderValue() const1855 String HTMLInputElement::GetPlaceholderValue() const {
1856 return !SuggestedValue().IsEmpty() ? SuggestedValue() : StrippedPlaceholder();
1857 }
1858
DefaultToolTip() const1859 String HTMLInputElement::DefaultToolTip() const {
1860 return input_type_->DefaultToolTip(*input_type_view_);
1861 }
1862
FileStatusText() const1863 String HTMLInputElement::FileStatusText() const {
1864 return input_type_view_->FileStatusText();
1865 }
1866
ShouldApplyMiddleEllipsis() const1867 bool HTMLInputElement::ShouldApplyMiddleEllipsis() const {
1868 return files() && files()->length() <= 1;
1869 }
1870
ShouldAppearIndeterminate() const1871 bool HTMLInputElement::ShouldAppearIndeterminate() const {
1872 return input_type_->ShouldAppearIndeterminate();
1873 }
1874
GetRadioButtonGroupScope() const1875 RadioButtonGroupScope* HTMLInputElement::GetRadioButtonGroupScope() const {
1876 // FIXME: Remove type check.
1877 if (type() != input_type_names::kRadio)
1878 return nullptr;
1879 if (HTMLFormElement* form_element = Form())
1880 return &form_element->GetRadioButtonGroupScope();
1881 if (isConnected())
1882 return &GetTreeScope().GetRadioButtonGroupScope();
1883 return nullptr;
1884 }
1885
SizeOfRadioGroup() const1886 unsigned HTMLInputElement::SizeOfRadioGroup() const {
1887 RadioButtonGroupScope* scope = GetRadioButtonGroupScope();
1888 if (!scope)
1889 return 0;
1890 return scope->GroupSizeFor(this);
1891 }
1892
AddToRadioButtonGroup()1893 inline void HTMLInputElement::AddToRadioButtonGroup() {
1894 if (RadioButtonGroupScope* scope = GetRadioButtonGroupScope())
1895 scope->AddButton(this);
1896 }
1897
RemoveFromRadioButtonGroup()1898 inline void HTMLInputElement::RemoveFromRadioButtonGroup() {
1899 if (RadioButtonGroupScope* scope = GetRadioButtonGroupScope())
1900 scope->RemoveButton(this);
1901 }
1902
height() const1903 unsigned HTMLInputElement::height() const {
1904 return input_type_->Height();
1905 }
1906
width() const1907 unsigned HTMLInputElement::width() const {
1908 return input_type_->Width();
1909 }
1910
setHeight(unsigned height)1911 void HTMLInputElement::setHeight(unsigned height) {
1912 SetUnsignedIntegralAttribute(html_names::kHeightAttr, height);
1913 }
1914
setWidth(unsigned width)1915 void HTMLInputElement::setWidth(unsigned width) {
1916 SetUnsignedIntegralAttribute(html_names::kWidthAttr, width);
1917 }
1918
ListAttributeTargetObserver(const AtomicString & id,HTMLInputElement * element)1919 ListAttributeTargetObserver::ListAttributeTargetObserver(
1920 const AtomicString& id,
1921 HTMLInputElement* element)
1922 : IdTargetObserver(element->GetTreeScope().GetIdTargetObserverRegistry(),
1923 id),
1924 element_(element) {}
1925
Trace(Visitor * visitor) const1926 void ListAttributeTargetObserver::Trace(Visitor* visitor) const {
1927 visitor->Trace(element_);
1928 IdTargetObserver::Trace(visitor);
1929 }
1930
IdTargetChanged()1931 void ListAttributeTargetObserver::IdTargetChanged() {
1932 element_->ListAttributeTargetChanged();
1933 }
1934
setRangeText(const String & replacement,ExceptionState & exception_state)1935 void HTMLInputElement::setRangeText(const String& replacement,
1936 ExceptionState& exception_state) {
1937 if (!input_type_->SupportsSelectionAPI()) {
1938 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
1939 "The input element's type ('" +
1940 input_type_->FormControlType() +
1941 "') does not support selection.");
1942 return;
1943 }
1944
1945 TextControlElement::setRangeText(replacement, exception_state);
1946 }
1947
setRangeText(const String & replacement,unsigned start,unsigned end,const String & selection_mode,ExceptionState & exception_state)1948 void HTMLInputElement::setRangeText(const String& replacement,
1949 unsigned start,
1950 unsigned end,
1951 const String& selection_mode,
1952 ExceptionState& exception_state) {
1953 if (!input_type_->SupportsSelectionAPI()) {
1954 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
1955 "The input element's type ('" +
1956 input_type_->FormControlType() +
1957 "') does not support selection.");
1958 return;
1959 }
1960
1961 TextControlElement::setRangeText(replacement, start, end, selection_mode,
1962 exception_state);
1963 }
1964
SetupDateTimeChooserParameters(DateTimeChooserParameters & parameters)1965 bool HTMLInputElement::SetupDateTimeChooserParameters(
1966 DateTimeChooserParameters& parameters) {
1967 if (!GetDocument().View())
1968 return false;
1969
1970 parameters.type = type();
1971 parameters.minimum = Minimum();
1972 parameters.maximum = Maximum();
1973 parameters.required = IsRequired();
1974 if (!RuntimeEnabledFeatures::LangAttributeAwareFormControlUIEnabled()) {
1975 parameters.locale = DefaultLanguage();
1976 } else {
1977 AtomicString computed_locale = ComputeInheritedLanguage();
1978 parameters.locale =
1979 computed_locale.IsEmpty() ? DefaultLanguage() : computed_locale;
1980 }
1981
1982 StepRange step_range = CreateStepRange(kRejectAny);
1983 if (step_range.HasStep()) {
1984 parameters.step = step_range.Step().ToDouble();
1985 parameters.step_base = step_range.StepBase().ToDouble();
1986 } else {
1987 parameters.step = 1.0;
1988 parameters.step_base = 0;
1989 }
1990
1991 parameters.anchor_rect_in_screen =
1992 GetDocument().View()->FrameToScreen(PixelSnappedBoundingBox());
1993 parameters.double_value = input_type_->ValueAsDouble();
1994 parameters.focused_field_index = input_type_view_->FocusedFieldIndex();
1995 parameters.is_anchor_element_rtl =
1996 input_type_view_->ComputedTextDirection() == TextDirection::kRtl;
1997 if (HTMLDataListElement* data_list = DataList()) {
1998 HTMLDataListOptionsCollection* options = data_list->options();
1999 for (unsigned i = 0; HTMLOptionElement* option = options->Item(i); ++i) {
2000 if (option->value().IsEmpty() || option->IsDisabledFormControl() ||
2001 !IsValidValue(option->value()))
2002 continue;
2003 auto suggestion = mojom::blink::DateTimeSuggestion::New();
2004 suggestion->value =
2005 input_type_->ParseToNumber(option->value(), Decimal::Nan())
2006 .ToDouble();
2007 if (std::isnan(suggestion->value))
2008 continue;
2009 suggestion->localized_value = LocalizeValue(option->value());
2010 suggestion->label =
2011 option->value() == option->label() ? String("") : option->label();
2012 parameters.suggestions.push_back(std::move(suggestion));
2013 }
2014 }
2015 return true;
2016 }
2017
SupportsInputModeAttribute() const2018 bool HTMLInputElement::SupportsInputModeAttribute() const {
2019 return input_type_->SupportsInputModeAttribute();
2020 }
2021
CapsLockStateMayHaveChanged()2022 void HTMLInputElement::CapsLockStateMayHaveChanged() {
2023 input_type_view_->CapsLockStateMayHaveChanged();
2024 }
2025
ShouldDrawCapsLockIndicator() const2026 bool HTMLInputElement::ShouldDrawCapsLockIndicator() const {
2027 return input_type_view_->ShouldDrawCapsLockIndicator();
2028 }
2029
SetShouldRevealPassword(bool value)2030 void HTMLInputElement::SetShouldRevealPassword(bool value) {
2031 if (!!should_reveal_password_ == value)
2032 return;
2033 should_reveal_password_ = value;
2034 if (HTMLElement* inner_editor = InnerEditorElement()) {
2035 // Update -webkit-text-security style.
2036 inner_editor->SetNeedsStyleRecalc(
2037 kLocalStyleChange,
2038 StyleChangeReasonForTracing::Create(style_change_reason::kControl));
2039 }
2040 }
2041
IsInteractiveContent() const2042 bool HTMLInputElement::IsInteractiveContent() const {
2043 return input_type_->IsInteractiveContent();
2044 }
2045
CustomStyleForLayoutObject()2046 scoped_refptr<ComputedStyle> HTMLInputElement::CustomStyleForLayoutObject() {
2047 scoped_refptr<ComputedStyle> style = OriginalStyleForLayoutObject();
2048 input_type_view_->CustomStyleForLayoutObject(*style);
2049 return style;
2050 }
2051
DidRecalcStyle(const StyleRecalcChange change)2052 void HTMLInputElement::DidRecalcStyle(const StyleRecalcChange change) {
2053 TextControlElement::DidRecalcStyle(change);
2054 if (NeedsReattachLayoutTree() && GetComputedStyle())
2055 input_type_view_->StartResourceLoading();
2056 }
2057
DidNotifySubtreeInsertionsToDocument()2058 void HTMLInputElement::DidNotifySubtreeInsertionsToDocument() {
2059 ListAttributeTargetChanged();
2060 }
2061
PopupRootAXObject()2062 AXObject* HTMLInputElement::PopupRootAXObject() {
2063 return input_type_view_->PopupRootAXObject();
2064 }
2065
EnsureFallbackContent()2066 void HTMLInputElement::EnsureFallbackContent() {
2067 input_type_view_->EnsureFallbackContent();
2068 }
2069
EnsurePrimaryContent()2070 void HTMLInputElement::EnsurePrimaryContent() {
2071 input_type_view_->EnsurePrimaryContent();
2072 }
2073
HasFallbackContent() const2074 bool HTMLInputElement::HasFallbackContent() const {
2075 return input_type_view_->HasFallbackContent();
2076 }
2077
SetFilesFromPaths(const Vector<String> & paths)2078 void HTMLInputElement::SetFilesFromPaths(const Vector<String>& paths) {
2079 return input_type_->SetFilesFromPaths(paths);
2080 }
2081
ChildrenChanged(const ChildrenChange & change)2082 void HTMLInputElement::ChildrenChanged(const ChildrenChange& change) {
2083 // Some input types only need shadow roots to hide any children that may
2084 // have been appended by script. For such types, shadow roots are lazily
2085 // created when children are added for the first time.
2086 EnsureUserAgentShadowRoot();
2087 ContainerNode::ChildrenChanged(change);
2088 }
2089
GetLayoutBoxForScrolling() const2090 LayoutBox* HTMLInputElement::GetLayoutBoxForScrolling() const {
2091 // If it's LayoutTextControlSingleLine, return InnerEditorElement's LayoutBox.
2092 if (IsTextField() && InnerEditorElement())
2093 return InnerEditorElement()->GetLayoutBox();
2094 return Element::GetLayoutBoxForScrolling();
2095 }
2096
IsDraggedSlider() const2097 bool HTMLInputElement::IsDraggedSlider() const {
2098 return input_type_view_->IsDraggedSlider();
2099 }
2100
MaybeReportPiiMetrics()2101 void HTMLInputElement::MaybeReportPiiMetrics() {
2102 // Don't report metrics if the field is empty.
2103 if (value().IsEmpty())
2104 return;
2105
2106 // Report the PII types derived from autofill field semantic type prediction.
2107 if (GetFormElementPiiType() != FormElementPiiType::kUnknown) {
2108 UseCounter::Count(GetDocument(),
2109 WebFeature::kAnyPiiFieldDetected_PredictedTypeMatch);
2110
2111 if (GetFormElementPiiType() == FormElementPiiType::kEmail) {
2112 UseCounter::Count(GetDocument(),
2113 WebFeature::kEmailFieldDetected_PredictedTypeMatch);
2114 } else if (GetFormElementPiiType() == FormElementPiiType::kPhone) {
2115 UseCounter::Count(GetDocument(),
2116 WebFeature::kPhoneFieldDetected_PredictedTypeMatch);
2117 }
2118 }
2119
2120 // Report the PII types derived by matching the field value with patterns.
2121
2122 // For Email, we add a length limitation (based on
2123 // https://www.rfc-editor.org/errata_search.php?rfc=3696) in addition to
2124 // matching with the pattern given by the HTML standard.
2125 if (value().length() <= kMaxEmailFieldLength &&
2126 EmailInputType::IsValidEmailAddress(GetDocument().EnsureEmailRegexp(),
2127 value())) {
2128 UseCounter::Count(GetDocument(),
2129 WebFeature::kEmailFieldDetected_PatternMatch);
2130 }
2131 }
2132
2133 } // namespace blink
2134