diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-01-29 11:01:19 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-02-02 09:41:31 +0000 |
commit | 5cb725e1b4b8e313c7d847ae46c1dbe3fbfaaa57 (patch) | |
tree | 682ae31aef0c4bf3fadffeb40a21def57971097e | |
parent | 0d62ec3da6b22576c666a839dddb7105f0047a68 (diff) | |
download | qtwebengine-chromium-5cb725e1b4b8e313c7d847ae46c1dbe3fbfaaa57.tar.gz |
[Backport] [Autofill] Use ShadowDOM placeholder to preview suggestions.
The first patch is a re-upload of
https://chromium-review.googlesource.com/c/chromium/src/+/646754
The follow-up patches will add some modifcations on how we preview
username and password suggestions.
The suggestions will be in black text, and the password suggestions
should be hidden behind dots.
Bug: 753645
Tbr: tkent@chromium.org
Reviewed-on: https://chromium-review.googlesource.com/702056
Commit-Queue: Sebastien Seguin-Gagnon <sebsg@chromium.org>
Reviewed-by: Yoshifumi Inoue <yosin@chromium.org>
Reviewed-by: Roger McFarlane <rogerm@chromium.org>
Cr-Commit-Position: refs/heads/master@{#509961}
(CVE-2018-6037)
Change-Id: I3b3ac18726598a3150329d5838d6811d5975d70f
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
8 files changed, 81 insertions, 41 deletions
diff --git a/chromium/third_party/WebKit/Source/core/css/html.css b/chromium/third_party/WebKit/Source/core/css/html.css index 9e38e56ba59..f571aa22f5c 100644 --- a/chromium/third_party/WebKit/Source/core/css/html.css +++ b/chromium/third_party/WebKit/Source/core/css/html.css @@ -522,6 +522,10 @@ input[type="password" i] { -webkit-text-security: disc !important; } +input[type="password" i]::-webkit-input-suggested { + -webkit-text-security: disc !important; +} + input[type="hidden" i], input[type="image" i], input[type="file" i] { -webkit-appearance: initial; padding: initial; diff --git a/chromium/third_party/WebKit/Source/core/html/HTMLInputElement.cpp b/chromium/third_party/WebKit/Source/core/html/HTMLInputElement.cpp index 1784443e4c9..d89d2fe30d8 100644 --- a/chromium/third_party/WebKit/Source/core/html/HTMLInputElement.cpp +++ b/chromium/third_party/WebKit/Source/core/html/HTMLInputElement.cpp @@ -1031,15 +1031,11 @@ void HTMLInputElement::SetValueForUser(const String& value) { setValue(value, kDispatchChangeEvent); } -const String& HTMLInputElement::SuggestedValue() const { - return suggested_value_; -} - void HTMLInputElement::SetSuggestedValue(const String& value) { if (!input_type_->CanSetSuggestedValue()) return; needs_to_update_view_value_ = true; - suggested_value_ = SanitizeValue(value); + TextControlElement::SetSuggestedValue(SanitizeValue(value)); SetNeedsStyleRecalc( kSubtreeStyleChange, StyleChangeReasonForTracing::Create(StyleChangeReason::kControlValue)); @@ -1083,14 +1079,16 @@ void HTMLInputElement::setValue(const String& value, if (!input_type_->CanSetValue(value)) return; + // Clear the suggested value. Use the base class version to not trigger a view + // update. + TextControlElement::SetSuggestedValue(String()); + EventQueueScope scope; String sanitized_value = SanitizeValue(value); bool value_changed = sanitized_value != this->value(); SetLastChangeWasNotUserEdit(); needs_to_update_view_value_ = true; - // Prevent TextFieldInputType::setValue from using the suggested value. - suggested_value_ = String(); input_type_->SetValue(sanitized_value, value_changed, event_behavior, selection); @@ -1162,7 +1160,9 @@ void HTMLInputElement::SetValueFromRenderer(const String& value) { // File upload controls will never use this. DCHECK_NE(type(), InputTypeNames::file); - suggested_value_ = String(); + // Clear the suggested value. Use the base class version to not trigger a view + // update. + TextControlElement::SetSuggestedValue(String()); // Renderer and our event handler are responsible for sanitizing values. DCHECK(value == input_type_->SanitizeUserInputValue(value) || @@ -1673,6 +1673,10 @@ void HTMLInputElement::UpdatePlaceholderText() { return input_type_view_->UpdatePlaceholderText(); } +String HTMLInputElement::GetPlaceholderValue() const { + return !SuggestedValue().IsEmpty() ? SuggestedValue() : StrippedPlaceholder(); +} + bool HTMLInputElement::SupportsAutocapitalize() const { return input_type_->SupportsAutocapitalize(); } diff --git a/chromium/third_party/WebKit/Source/core/html/HTMLInputElement.h b/chromium/third_party/WebKit/Source/core/html/HTMLInputElement.h index 31c6ccc8b18..fa18e72b2e5 100644 --- a/chromium/third_party/WebKit/Source/core/html/HTMLInputElement.h +++ b/chromium/third_party/WebKit/Source/core/html/HTMLInputElement.h @@ -144,8 +144,7 @@ class CORE_EXPORT HTMLInputElement String LocalizeValue(const String&) const; - const String& SuggestedValue() const; - void SetSuggestedValue(const String&); + void SetSuggestedValue(const String& value) override; void SetEditingValue(const String&); @@ -297,6 +296,8 @@ class CORE_EXPORT HTMLInputElement unsigned SizeOfRadioGroup() const; + String GetPlaceholderValue() const final; + protected: HTMLInputElement(Document&, bool created_by_parser); @@ -371,9 +372,6 @@ class CORE_EXPORT HTMLInputElement bool SupportsPlaceholder() const final; void UpdatePlaceholderText() final; bool IsEmptyValue() const final { return InnerEditorValue().IsEmpty(); } - bool IsEmptySuggestedValue() const final { - return SuggestedValue().IsEmpty(); - } void HandleFocusEvent(Element* old_focused_element, WebFocusType) final; void HandleBlurEvent() final; void DispatchFocusInEvent(const AtomicString& event_type, @@ -408,7 +406,6 @@ class CORE_EXPORT HTMLInputElement AtomicString name_; // The value string in |value| value mode. String non_attribute_value_; - String suggested_value_; int size_; // https://html.spec.whatwg.org/multipage/forms.html#concept-input-value-dirty-flag unsigned has_dirty_value_ : 1; diff --git a/chromium/third_party/WebKit/Source/core/html/HTMLTextAreaElement.cpp b/chromium/third_party/WebKit/Source/core/html/HTMLTextAreaElement.cpp index 6e2d9ffa976..1032546cafa 100644 --- a/chromium/third_party/WebKit/Source/core/html/HTMLTextAreaElement.cpp +++ b/chromium/third_party/WebKit/Source/core/html/HTMLTextAreaElement.cpp @@ -397,6 +397,10 @@ void HTMLTextAreaElement::SetValueCommon( normalized_value.Replace("\r\n", "\n"); normalized_value.Replace('\r', '\n'); + // Clear the suggested value. Use the base class version to not trigger a view + // update. + TextControlElement::SetSuggestedValue(String()); + // Return early because we don't want to trigger other side effects when the // value isn't changing. This is interoperable. if (normalized_value == value()) @@ -414,7 +418,6 @@ void HTMLTextAreaElement::SetValueCommon( SetNeedsStyleRecalc( kSubtreeStyleChange, StyleChangeReasonForTracing::Create(StyleChangeReason::kControlValue)); - suggested_value_ = String(); SetNeedsValidityCheck(); if (IsFinishedParsingChildren() && selection == TextControlSetValueSelection::kSetSelectionToEnd) { @@ -474,18 +477,8 @@ void HTMLTextAreaElement::setDefaultValue(const String& default_value) { SetNonDirtyValue(value); } -String HTMLTextAreaElement::SuggestedValue() const { - return suggested_value_; -} - void HTMLTextAreaElement::SetSuggestedValue(const String& value) { - suggested_value_ = value; - - if (!value.IsNull()) - SetInnerEditorValue(suggested_value_); - else - SetInnerEditorValue(value_); - UpdatePlaceholderVisibility(); + TextControlElement::SetSuggestedValue(value); SetNeedsStyleRecalc( kSubtreeStyleChange, StyleChangeReasonForTracing::Create(StyleChangeReason::kControlValue)); @@ -594,7 +587,7 @@ void HTMLTextAreaElement::SetPlaceholderVisibility(bool visible) { void HTMLTextAreaElement::UpdatePlaceholderText() { HTMLElement* placeholder = PlaceholderElement(); - const AtomicString& placeholder_text = FastGetAttribute(placeholderAttr); + const String placeholder_text = GetPlaceholderValue(); if (placeholder_text.IsEmpty()) { if (placeholder) UserAgentShadowRoot()->RemoveChild(placeholder); @@ -613,6 +606,11 @@ void HTMLTextAreaElement::UpdatePlaceholderText() { placeholder->setTextContent(placeholder_text); } +String HTMLTextAreaElement::GetPlaceholderValue() const { + return !SuggestedValue().IsEmpty() ? SuggestedValue() + : FastGetAttribute(placeholderAttr); +} + bool HTMLTextAreaElement::IsInteractiveContent() const { return true; } diff --git a/chromium/third_party/WebKit/Source/core/html/HTMLTextAreaElement.h b/chromium/third_party/WebKit/Source/core/html/HTMLTextAreaElement.h index 2953a745cba..0d144e0a9d7 100644 --- a/chromium/third_party/WebKit/Source/core/html/HTMLTextAreaElement.h +++ b/chromium/third_party/WebKit/Source/core/html/HTMLTextAreaElement.h @@ -52,8 +52,7 @@ class CORE_EXPORT HTMLTextAreaElement final : public TextControlElement { void setDefaultValue(const String&); int textLength() const { return value().length(); } - String SuggestedValue() const; - void SetSuggestedValue(const String&); + void SetSuggestedValue(const String& value) override; // For ValidityState String validationMessage() const override; @@ -87,11 +86,9 @@ class CORE_EXPORT HTMLTextAreaElement final : public TextControlElement { bool IsPlaceholderVisible() const override { return is_placeholder_visible_; } void SetPlaceholderVisibility(bool) override; bool SupportsPlaceholder() const override { return true; } + String GetPlaceholderValue() const final; void UpdatePlaceholderText() override; bool IsEmptyValue() const override { return value().IsEmpty(); } - bool IsEmptySuggestedValue() const final { - return SuggestedValue().IsEmpty(); - } bool SupportsAutocapitalize() const override { return true; } const AtomicString& DefaultAutocapitalize() const override; @@ -148,7 +145,6 @@ class CORE_EXPORT HTMLTextAreaElement final : public TextControlElement { mutable String value_; mutable bool is_dirty_; unsigned is_placeholder_visible_ : 1; - String suggested_value_; }; } // namespace blink diff --git a/chromium/third_party/WebKit/Source/core/html/TextControlElement.cpp b/chromium/third_party/WebKit/Source/core/html/TextControlElement.cpp index 799d5c4a849..919b188c8c8 100644 --- a/chromium/third_party/WebKit/Source/core/html/TextControlElement.cpp +++ b/chromium/third_party/WebKit/Source/core/html/TextControlElement.cpp @@ -42,6 +42,7 @@ #include "core/frame/LocalFrame.h" #include "core/frame/UseCounter.h" #include "core/html/HTMLBRElement.h" +#include "core/html/HTMLDivElement.h" #include "core/html/parser/HTMLParserIdioms.h" #include "core/html/shadow/ShadowElementNames.h" #include "core/layout/LayoutBlock.h" @@ -155,8 +156,8 @@ bool TextControlElement::IsPlaceholderEmpty() const { } bool TextControlElement::PlaceholderShouldBeVisible() const { - return SupportsPlaceholder() && IsEmptyValue() && IsEmptySuggestedValue() && - !IsPlaceholderEmpty(); + return SupportsPlaceholder() && InnerEditorValue().IsEmpty() && + (!IsPlaceholderEmpty() || !IsEmptySuggestedValue()); } HTMLElement* TextControlElement::PlaceholderElement() const { @@ -964,6 +965,39 @@ String TextControlElement::DirectionForFormData() const { return "ltr"; } +// TODO(crbug.com/772433): Create and use a new suggested-value element instead. +void TextControlElement::SetSuggestedValue(const String& value) { + suggested_value_ = value; + if (!suggested_value_.IsEmpty() && !InnerEditorValue().IsEmpty()) { + // Save the value that is in the editor and set the editor value to an empty + // string. This will allow the suggestion placeholder to be shown to the + // user. + value_before_set_suggested_value_ = InnerEditorValue(); + SetInnerEditorValue(""); + } else if (suggested_value_.IsEmpty() && + !value_before_set_suggested_value_.IsEmpty()) { + // Reset the value that was in the editor before showing the suggestion. + SetInnerEditorValue(value_before_set_suggested_value_); + value_before_set_suggested_value_ = ""; + } + + UpdatePlaceholderText(); + + HTMLElement* placeholder = PlaceholderElement(); + if (!placeholder) + return; + + // Change the pseudo-id to set the style for suggested values or reset the + // placeholder style depending on if there is a suggested value. + placeholder->SetShadowPseudoId(AtomicString(suggested_value_.IsEmpty() + ? "-webkit-input-placeholder" + : "-webkit-input-suggested")); +} + +const String& TextControlElement::SuggestedValue() const { + return suggested_value_; +} + HTMLElement* TextControlElement::InnerEditorElement() const { return ToHTMLElementOrDie( UserAgentShadowRoot()->getElementById(ShadowElementNames::InnerEditor())); diff --git a/chromium/third_party/WebKit/Source/core/html/TextControlElement.h b/chromium/third_party/WebKit/Source/core/html/TextControlElement.h index 536967fcbd9..c8099a5f5e6 100644 --- a/chromium/third_party/WebKit/Source/core/html/TextControlElement.h +++ b/chromium/third_party/WebKit/Source/core/html/TextControlElement.h @@ -142,10 +142,14 @@ class CORE_EXPORT TextControlElement : public HTMLFormControlElementWithState { String DirectionForFormData() const; + virtual void SetSuggestedValue(const String& value); + const String& SuggestedValue() const; + protected: TextControlElement(const QualifiedName&, Document&); bool IsPlaceholderEmpty() const; virtual void UpdatePlaceholderText() = 0; + virtual String GetPlaceholderValue() const = 0; void ParseAttribute(const AttributeModificationParams&) override; @@ -183,7 +187,7 @@ class CORE_EXPORT TextControlElement : public HTMLFormControlElementWithState { virtual bool IsEmptyValue() const = 0; // Returns true if suggested value is empty. Used to check placeholder // visibility. - virtual bool IsEmptySuggestedValue() const { return true; } + bool IsEmptySuggestedValue() const { return SuggestedValue().IsEmpty(); } // Called in dispatchFocusEvent(), after placeholder process, before calling // parent's dispatchFocusEvent(). virtual void HandleFocusEvent(Element* /* oldFocusedNode */, WebFocusType) {} @@ -203,6 +207,9 @@ class CORE_EXPORT TextControlElement : public HTMLFormControlElementWithState { unsigned cached_selection_end_; TextFieldSelectionDirection cached_selection_direction_; + String suggested_value_; + String value_before_set_suggested_value_; + FRIEND_TEST_ALL_PREFIXES(TextControlElementTest, IndexForPosition); }; diff --git a/chromium/third_party/WebKit/Source/core/html/forms/TextFieldInputType.cpp b/chromium/third_party/WebKit/Source/core/html/forms/TextFieldInputType.cpp index 5298f3e132e..f31dcbe0753 100644 --- a/chromium/third_party/WebKit/Source/core/html/forms/TextFieldInputType.cpp +++ b/chromium/third_party/WebKit/Source/core/html/forms/TextFieldInputType.cpp @@ -466,7 +466,7 @@ void TextFieldInputType::UpdatePlaceholderText() { if (!SupportsPlaceholder()) return; HTMLElement* placeholder = GetElement().PlaceholderElement(); - String placeholder_text = GetElement().StrippedPlaceholder(); + String placeholder_text = GetElement().GetPlaceholderValue(); if (placeholder_text.IsEmpty()) { if (placeholder) placeholder->remove(ASSERT_NO_EXCEPTION); @@ -531,10 +531,10 @@ void TextFieldInputType::SpinButtonStepUp() { } void TextFieldInputType::UpdateView() { - if (!GetElement().SuggestedValue().IsNull()) { - GetElement().SetInnerEditorValue(GetElement().SuggestedValue()); - GetElement().UpdatePlaceholderVisibility(); - } else if (GetElement().NeedsToUpdateViewValue()) { + // The suggested values are now shown using placeholder elements, so there is + // nothing to do here for the suggested values. + if (GetElement().SuggestedValue().IsEmpty() && + GetElement().NeedsToUpdateViewValue()) { // Update the view only if needsToUpdateViewValue is true. It protects // an unacceptable view value from being overwritten with the DOM value. // |