| /* |
| * Copyright (C) 2012 Google 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/picker_indicator_element.h" |
| |
| #include "third_party/blink/public/strings/grit/blink_strings.h" |
| #include "third_party/blink/renderer/core/dom/events/event.h" |
| #include "third_party/blink/renderer/core/events/keyboard_event.h" |
| #include "third_party/blink/renderer/core/frame/settings.h" |
| #include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h" |
| #include "third_party/blink/renderer/core/layout/layout_details_marker.h" |
| #include "third_party/blink/renderer/core/page/chrome_client.h" |
| #include "third_party/blink/renderer/core/page/page.h" |
| #include "third_party/blink/renderer/platform/text/platform_locale.h" |
| #include "third_party/blink/renderer/platform/web_test_support.h" |
| #include "ui/base/ui_base_features.h" |
| |
| namespace blink { |
| |
| PickerIndicatorElement::PickerIndicatorElement( |
| Document& document, |
| PickerIndicatorOwner& picker_indicator_owner) |
| : HTMLDivElement(document), |
| picker_indicator_owner_(&picker_indicator_owner) { |
| SetShadowPseudoId(AtomicString("-webkit-calendar-picker-indicator")); |
| setAttribute(html_names::kIdAttr, shadow_element_names::kIdPickerIndicator); |
| } |
| |
| PickerIndicatorElement::~PickerIndicatorElement() { |
| DCHECK(!chooser_); |
| } |
| |
| LayoutObject* PickerIndicatorElement::CreateLayoutObject( |
| const ComputedStyle& style, |
| LegacyLayout legacy) { |
| if (features::IsFormControlsRefreshEnabled()) |
| return HTMLDivElement::CreateLayoutObject(style, legacy); |
| |
| UseCounter::Count(GetDocument(), WebFeature::kLegacyLayoutByDetailsMarker); |
| return new LayoutDetailsMarker(this); |
| } |
| |
| void PickerIndicatorElement::DefaultEventHandler(Event& event) { |
| if (!GetLayoutObject()) |
| return; |
| if (!picker_indicator_owner_ || |
| picker_indicator_owner_->IsPickerIndicatorOwnerDisabledOrReadOnly()) |
| return; |
| |
| auto* keyboard_event = DynamicTo<KeyboardEvent>(event); |
| if (event.type() == event_type_names::kClick) { |
| OpenPopup(); |
| event.SetDefaultHandled(); |
| } else if (event.type() == event_type_names::kKeypress && keyboard_event) { |
| int char_code = keyboard_event->charCode(); |
| if (char_code == ' ' || char_code == '\r') { |
| OpenPopup(); |
| event.SetDefaultHandled(); |
| } |
| } |
| |
| if (!event.DefaultHandled()) |
| HTMLDivElement::DefaultEventHandler(event); |
| } |
| |
| bool PickerIndicatorElement::WillRespondToMouseClickEvents() { |
| if (GetLayoutObject() && picker_indicator_owner_ && |
| !picker_indicator_owner_->IsPickerIndicatorOwnerDisabledOrReadOnly()) |
| return true; |
| |
| return HTMLDivElement::WillRespondToMouseClickEvents(); |
| } |
| |
| void PickerIndicatorElement::DidChooseValue(const String& value) { |
| if (!picker_indicator_owner_) |
| return; |
| picker_indicator_owner_->PickerIndicatorChooseValue(value); |
| } |
| |
| void PickerIndicatorElement::DidChooseValue(double value) { |
| if (picker_indicator_owner_) |
| picker_indicator_owner_->PickerIndicatorChooseValue(value); |
| } |
| |
| void PickerIndicatorElement::DidEndChooser() { |
| chooser_.Clear(); |
| picker_indicator_owner_->DidEndChooser(); |
| if (::features::IsFormControlsRefreshEnabled() && |
| OwnerElement().GetLayoutObject()) { |
| // Invalidate paint to ensure that the focus ring is shown. |
| OwnerElement().GetLayoutObject()->SetShouldDoFullPaintInvalidation(); |
| } |
| } |
| |
| void PickerIndicatorElement::OpenPopup() { |
| if (HasOpenedPopup()) |
| return; |
| if (!GetDocument().GetPage()) |
| return; |
| if (!picker_indicator_owner_) |
| return; |
| DateTimeChooserParameters parameters; |
| if (!picker_indicator_owner_->SetupDateTimeChooserParameters(parameters)) |
| return; |
| chooser_ = GetDocument().GetPage()->GetChromeClient().OpenDateTimeChooser( |
| GetDocument().GetFrame(), this, parameters); |
| if (::features::IsFormControlsRefreshEnabled() && |
| OwnerElement().GetLayoutObject()) { |
| // Invalidate paint to ensure that the focus ring is removed. |
| OwnerElement().GetLayoutObject()->SetShouldDoFullPaintInvalidation(); |
| } |
| } |
| |
| Element& PickerIndicatorElement::OwnerElement() const { |
| DCHECK(picker_indicator_owner_); |
| return picker_indicator_owner_->PickerOwnerElement(); |
| } |
| |
| void PickerIndicatorElement::ClosePopup() { |
| if (!chooser_) |
| return; |
| chooser_->EndChooser(); |
| } |
| |
| bool PickerIndicatorElement::HasOpenedPopup() const { |
| return chooser_; |
| } |
| |
| void PickerIndicatorElement::DetachLayoutTree(bool performing_reattach) { |
| ClosePopup(); |
| HTMLDivElement::DetachLayoutTree(performing_reattach); |
| } |
| |
| AXObject* PickerIndicatorElement::PopupRootAXObject() const { |
| return chooser_ ? chooser_->RootAXObject() : nullptr; |
| } |
| |
| bool PickerIndicatorElement::IsPickerIndicatorElement() const { |
| return true; |
| } |
| |
| Node::InsertionNotificationRequest PickerIndicatorElement::InsertedInto( |
| ContainerNode& insertion_point) { |
| HTMLDivElement::InsertedInto(insertion_point); |
| return kInsertionShouldCallDidNotifySubtreeInsertions; |
| } |
| |
| void PickerIndicatorElement::DidNotifySubtreeInsertionsToDocument() { |
| if (!GetDocument().ExistingAXObjectCache()) |
| return; |
| // Don't make this focusable if we are in web tests in order to avoid |
| // breaking existing tests. |
| // TODO(crbug.com/1054048): We should have a way to disable accessibility in |
| // web tests. Once we do have it, this early return should be removed. |
| if (WebTestSupport::IsRunningWebTest()) |
| return; |
| setAttribute(html_names::kTabindexAttr, "0"); |
| setAttribute(html_names::kAriaHaspopupAttr, "menu"); |
| setAttribute(html_names::kRoleAttr, "button"); |
| setAttribute( |
| html_names::kAriaLabelAttr, |
| AtomicString( |
| this->picker_indicator_owner_->AriaRoleForPickerIndicator())); |
| } |
| |
| void PickerIndicatorElement::Trace(Visitor* visitor) const { |
| visitor->Trace(picker_indicator_owner_); |
| visitor->Trace(chooser_); |
| HTMLDivElement::Trace(visitor); |
| DateTimeChooserClient::Trace(visitor); |
| } |
| |
| } // namespace blink |