| /* |
| * Copyright (C) 2010 Google Inc. All rights reserved. |
| * Copyright (C) 2011 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "third_party/blink/renderer/core/html/forms/password_input_type.h" |
| |
| #include "base/memory/scoped_refptr.h" |
| #include "third_party/blink/renderer/core/dom/document.h" |
| #include "third_party/blink/renderer/core/dom/dom_token_list.h" |
| #include "third_party/blink/renderer/core/dom/node_computed_style.h" |
| #include "third_party/blink/renderer/core/editing/frame_selection.h" |
| #include "third_party/blink/renderer/core/events/keyboard_event.h" |
| #include "third_party/blink/renderer/core/frame/local_frame.h" |
| #include "third_party/blink/renderer/core/frame/web_feature.h" |
| #include "third_party/blink/renderer/core/geometry/dom_rect.h" |
| #include "third_party/blink/renderer/core/html/forms/form_controller.h" |
| #include "third_party/blink/renderer/core/html/forms/html_input_element.h" |
| #include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h" |
| #include "third_party/blink/renderer/core/input/keyboard_event_manager.h" |
| #include "third_party/blink/renderer/core/input_type_names.h" |
| #include "third_party/blink/renderer/core/layout/layout_text_control_single_line.h" |
| #include "third_party/blink/renderer/platform/wtf/assertions.h" |
| |
| namespace blink { |
| |
| void PasswordInputType::CountUsage() { |
| CountUsageIfVisible(WebFeature::kInputTypePassword); |
| if (GetElement().FastHasAttribute(html_names::kMaxlengthAttr)) |
| CountUsageIfVisible(WebFeature::kInputTypePasswordMaxLength); |
| } |
| |
| const AtomicString& PasswordInputType::FormControlType() const { |
| return input_type_names::kPassword; |
| } |
| |
| bool PasswordInputType::ShouldSaveAndRestoreFormControlState() const { |
| return false; |
| } |
| |
| FormControlState PasswordInputType::SaveFormControlState() const { |
| // Should never save/restore password fields. |
| NOTREACHED(); |
| return FormControlState(); |
| } |
| |
| void PasswordInputType::RestoreFormControlState(const FormControlState&) { |
| // Should never save/restore password fields. |
| NOTREACHED(); |
| } |
| |
| bool PasswordInputType::ShouldRespectListAttribute() { |
| return false; |
| } |
| |
| bool PasswordInputType::NeedsContainer() const { |
| return RuntimeEnabledFeatures::PasswordRevealEnabled(); |
| } |
| |
| void PasswordInputType::CreateShadowSubtree() { |
| BaseTextInputType::CreateShadowSubtree(); |
| |
| if (RuntimeEnabledFeatures::PasswordRevealEnabled()) { |
| Element* container = ContainerElement(); |
| Element* view_port = GetElement().UserAgentShadowRoot()->getElementById( |
| shadow_element_names::kIdEditingViewPort); |
| DCHECK(container); |
| DCHECK(view_port); |
| container->InsertBefore(MakeGarbageCollected<PasswordRevealButtonElement>( |
| GetElement().GetDocument()), |
| view_port->nextSibling()); |
| } |
| } |
| |
| void PasswordInputType::DidSetValueByUserEdit() { |
| if (RuntimeEnabledFeatures::PasswordRevealEnabled()) { |
| // If the last character is deleted, we hide the reveal button. |
| if (GetElement().value().IsEmpty()) { |
| should_show_reveal_button_ = false; |
| } |
| UpdatePasswordRevealButton(); |
| } |
| BaseTextInputType::DidSetValueByUserEdit(); |
| } |
| |
| void PasswordInputType::DidSetValue(const String& string, bool value_changed) { |
| if (RuntimeEnabledFeatures::PasswordRevealEnabled()) { |
| if (value_changed) { |
| // Hide the password if the value is changed by script. |
| should_show_reveal_button_ = false; |
| UpdatePasswordRevealButton(); |
| } |
| } |
| BaseTextInputType::DidSetValue(string, value_changed); |
| } |
| |
| void PasswordInputType::UpdateView() { |
| BaseTextInputType::UpdateView(); |
| |
| if (RuntimeEnabledFeatures::PasswordRevealEnabled()) |
| UpdatePasswordRevealButton(); |
| } |
| |
| void PasswordInputType::CapsLockStateMayHaveChanged() { |
| auto& document = GetElement().GetDocument(); |
| LocalFrame* frame = document.GetFrame(); |
| // Only draw the caps lock indicator if these things are true: |
| // 1) The field is a password field |
| // 2) The frame is active |
| // 3) The element is focused |
| // 4) The caps lock is on |
| const bool should_draw_caps_lock_indicator = |
| frame && frame->Selection().FrameIsFocusedAndActive() && |
| document.FocusedElement() == GetElement() && |
| KeyboardEventManager::CurrentCapsLockState(); |
| |
| if (should_draw_caps_lock_indicator != should_draw_caps_lock_indicator_) { |
| should_draw_caps_lock_indicator_ = should_draw_caps_lock_indicator; |
| if (auto* layout_object = GetElement().GetLayoutObject()) |
| layout_object->SetShouldDoFullPaintInvalidation(); |
| } |
| } |
| |
| bool PasswordInputType::ShouldDrawCapsLockIndicator() const { |
| return should_draw_caps_lock_indicator_; |
| } |
| |
| void PasswordInputType::UpdatePasswordRevealButton() { |
| Element* button = GetElement().UserAgentShadowRoot()->getElementById( |
| shadow_element_names::kIdPasswordRevealButton); |
| |
| // Update the glyph. |
| const AtomicString reveal("reveal"); |
| if (GetElement().ShouldRevealPassword()) |
| button->classList().Add(reveal); |
| else |
| button->classList().Remove(reveal); |
| |
| // Update the visibility. |
| if (should_show_reveal_button_) { |
| // Show the reveal button only when the width is enough for the reveal |
| // button plus a few characters. (The number of characters slightly varies |
| // based on the font size/family). |
| const float kRevealButtonWidthEm = 1.3; // 1.3em |
| const float kPasswordMinWidthEm = |
| 0.7; // 0.7em which is enough for ~2 chars. |
| const int kLeftMarginPx = 3; // 3px |
| const int kRightMarginPx = 3; // 3px |
| float current_width = GetElement().getBoundingClientRect()->width(); |
| float width_needed = GetElement().ComputedStyleRef().FontSize() * |
| (kRevealButtonWidthEm + kPasswordMinWidthEm) + |
| kLeftMarginPx + kRightMarginPx; |
| |
| if (current_width >= width_needed) { |
| button->RemoveInlineStyleProperty(CSSPropertyID::kDisplay); |
| } |
| } else { |
| button->SetInlineStyleProperty(CSSPropertyID::kDisplay, CSSValueID::kNone); |
| // Always obscure password when the reveal button is hidden. |
| // (ex. out of focus) |
| GetElement().SetShouldRevealPassword(false); |
| } |
| } |
| |
| void PasswordInputType::ForwardEvent(Event& event) { |
| BaseTextInputType::ForwardEvent(event); |
| |
| if (GetElement().GetLayoutObject() && |
| !GetElement().GetForceReattachLayoutTree() && |
| (event.type() == event_type_names::kBlur || |
| event.type() == event_type_names::kFocus)) |
| CapsLockStateMayHaveChanged(); |
| } |
| |
| void PasswordInputType::HandleBlurEvent() { |
| if (RuntimeEnabledFeatures::PasswordRevealEnabled()) { |
| should_show_reveal_button_ = false; |
| UpdatePasswordRevealButton(); |
| } |
| |
| BaseTextInputType::HandleBlurEvent(); |
| } |
| |
| void PasswordInputType::HandleBeforeTextInsertedEvent( |
| BeforeTextInsertedEvent& event) { |
| if (RuntimeEnabledFeatures::PasswordRevealEnabled()) { |
| // This is the only scenario we go from no reveal button to showing the |
| // reveal button: the password is empty and we have some user input. |
| if (GetElement().value().IsEmpty()) |
| should_show_reveal_button_ = true; |
| } |
| |
| TextFieldInputType::HandleBeforeTextInsertedEvent(event); |
| } |
| |
| void PasswordInputType::HandleKeydownEvent(KeyboardEvent& event) { |
| if (RuntimeEnabledFeatures::PasswordRevealEnabled()) { |
| if (should_show_reveal_button_) { |
| // Alt-F8 to reveal/obscure password |
| if (event.getModifierState("Alt") && event.key() == "F8") { |
| GetElement().SetShouldRevealPassword( |
| !GetElement().ShouldRevealPassword()); |
| UpdatePasswordRevealButton(); |
| event.SetDefaultHandled(); |
| } |
| } |
| } |
| |
| if (!event.DefaultHandled()) |
| BaseTextInputType::HandleKeydownEvent(event); |
| } |
| |
| bool PasswordInputType::SupportsInputModeAttribute() const { |
| return true; |
| } |
| |
| } // namespace blink |