text: /*
   * 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:
   * 1.  Redistributions of source code must retain the above copyright
   *     notice, this list of conditions and the following disclaimer.
   * 2.  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.
   *
   * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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/date_time_edit_element.h"
  
  #include "third_party/blink/renderer/core/css/style_change_reason.h"
  #include "third_party/blink/renderer/core/dom/document.h"
  #include "third_party/blink/renderer/core/dom/text.h"
  #include "third_party/blink/renderer/core/events/mouse_event.h"
  #include "third_party/blink/renderer/core/frame/use_counter_impl.h"
  #include "third_party/blink/renderer/core/html/forms/date_time_field_elements.h"
  #include "third_party/blink/renderer/core/html/forms/date_time_fields_state.h"
  #include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h"
  #include "third_party/blink/renderer/core/html_names.h"
  #include "third_party/blink/renderer/core/style/computed_style.h"
  #include "third_party/blink/renderer/platform/bindings/exception_state.h"
  #include "third_party/blink/renderer/platform/fonts/font_cache.h"
  #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
  #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
  #include "third_party/blink/renderer/platform/text/date_time_format.h"
  #include "third_party/blink/renderer/platform/text/platform_locale.h"
  #include "third_party/blink/renderer/platform/wtf/date_math.h"
  
  namespace blink {
  
  class DateTimeEditBuilder : private DateTimeFormat::TokenHandler {
   public:
    // The argument objects must be alive until this object dies.
    DateTimeEditBuilder(DateTimeEditElement&,
                        const DateTimeEditElement::LayoutParameters&,
                        const DateComponents&);
  
    bool Build(const String&);
  
   private:
    bool NeedMillisecondField() const;
    bool ShouldAMPMFieldDisabled() const;
    bool ShouldDayOfMonthFieldDisabled() const;
    bool ShouldHourFieldDisabled() const;
    bool ShouldMillisecondFieldDisabled() const;
    bool ShouldMinuteFieldDisabled() const;
    bool ShouldSecondFieldDisabled() const;
    bool ShouldYearFieldDisabled() const;
    inline const StepRange& GetStepRange() const {
      return parameters_.step_range;
    }
    DateTimeNumericFieldElement::Step CreateStep(double ms_per_field_unit,
                                                 double ms_per_field_size) const;
  
    // DateTimeFormat::TokenHandler functions.
    void VisitField(DateTimeFormat::FieldType, int) final;
    void VisitLiteral(const String&) final;
  
    DateTimeEditElement& EditElement() const;
  
    DateTimeEditElement* edit_element_;
    const DateComponents date_value_;
    const DateTimeEditElement::LayoutParameters& parameters_;
    DateTimeNumericFieldElement::Range day_range_;
    DateTimeNumericFieldElement::Range hour23_range_;
    DateTimeNumericFieldElement::Range minute_range_;
    DateTimeNumericFieldElement::Range second_range_;
    DateTimeNumericFieldElement::Range millisecond_range_;
  };
  
  DateTimeEditBuilder::DateTimeEditBuilder(
      DateTimeEditElement& element,
      const DateTimeEditElement::LayoutParameters& layout_parameters,
      const DateComponents& date_value)
      : edit_element_(&element),
        date_value_(date_value),
        parameters_(layout_parameters),
        day_range_(1, 31),
        hour23_range_(0, 23),
        minute_range_(0, 59),
        second_range_(0, 59),
        millisecond_range_(0, 999) {
    if (date_value_.GetType() == DateComponents::kDate ||
        date_value_.GetType() == DateComponents::kDateTimeLocal) {
      if (parameters_.minimum.GetType() != DateComponents::kInvalid &&
          parameters_.maximum.GetType() != DateComponents::kInvalid &&
          parameters_.minimum.FullYear() == parameters_.maximum.FullYear() &&
          parameters_.minimum.Month() == parameters_.maximum.Month() &&
          parameters_.minimum.MonthDay() <= parameters_.maximum.MonthDay()) {
        day_range_.minimum = parameters_.minimum.MonthDay();
        day_range_.maximum = parameters_.maximum.MonthDay();
      }
    }
  
    if (date_value_.GetType() == DateComponents::kTime ||
        day_range_.IsSingleton()) {
      if (parameters_.minimum.GetType() != DateComponents::kInvalid &&
          parameters_.maximum.GetType() != DateComponents::kInvalid &&
          parameters_.minimum.Hour() <= parameters_.maximum.Hour()) {
        hour23_range_.minimum = parameters_.minimum.Hour();
        hour23_range_.maximum = parameters_.maximum.Hour();
      }
    }
  
    if (hour23_range_.IsSingleton() &&
        parameters_.minimum.Minute() <= parameters_.maximum.Minute()) {
      minute_range_.minimum = parameters_.minimum.Minute();
      minute_range_.maximum = parameters_.maximum.Minute();
    }
    if (minute_range_.IsSingleton() &&
        parameters_.minimum.Second() <= parameters_.maximum.Second()) {
      second_range_.minimum = parameters_.minimum.Second();
      second_range_.maximum = parameters_.maximum.Second();
    }
    if (second_range_.IsSingleton() &&
        parameters_.minimum.Millisecond() <= parameters_.maximum.Millisecond()) {
      millisecond_range_.minimum = parameters_.minimum.Millisecond();
      millisecond_range_.maximum = parameters_.maximum.Millisecond();
    }
  }
  
  bool DateTimeEditBuilder::Build(const String& format_string) {
    EditElement().ResetFields();
  
    // Mute UseCounter when constructing the DateTime object, to avoid counting
    // attributes on elements inside the user-agent shadow DOM.
    UseCounterMuteScope scope(EditElement());
    return DateTimeFormat::Parse(format_string, *this);
  }
  
  bool DateTimeEditBuilder::NeedMillisecondField() const {
    return date_value_.Millisecond() ||
           !GetStepRange()
                .Minimum()
                .Remainder(static_cast<int>(kMsPerSecond))
                .IsZero() ||
           !GetStepRange()
                .Step()
                .Remainder(static_cast<int>(kMsPerSecond))
                .IsZero();
  }
  
  void DateTimeEditBuilder::VisitField(DateTimeFormat::FieldType field_type,
                                       int count) {
    const int kCountForAbbreviatedMonth = 3;
    const int kCountForFullMonth = 4;
    const int kCountForNarrowMonth = 5;
    Document& document = EditElement().GetDocument();
  
    switch (field_type) {
      case DateTimeFormat::kFieldTypeDayOfMonth: {
        DateTimeFieldElement* field =
            MakeGarbageCollected<DateTimeDayFieldElement>(
                document, EditElement(), parameters_.placeholder_for_day,
                day_range_);
        EditElement().AddField(field);
        if (ShouldDayOfMonthFieldDisabled()) {
          field->SetValueAsDate(date_value_);
          field->SetDisabled();
        }
        return;
      }
  
      case DateTimeFormat::kFieldTypeHour11: {
        DateTimeNumericFieldElement::Step step =
            CreateStep(kMsPerHour, kMsPerHour * 12);
        DateTimeFieldElement* field =
            MakeGarbageCollected<DateTimeHour11FieldElement>(
                document, EditElement(), hour23_range_, step);
        EditElement().AddField(field);
        if (ShouldHourFieldDisabled()) {
          field->SetValueAsDate(date_value_);
          field->SetDisabled();
        }
        return;
      }
  
      case DateTimeFormat::kFieldTypeHour12: {
        DateTimeNumericFieldElement::Step step =
            CreateStep(kMsPerHour, kMsPerHour * 12);
        DateTimeFieldElement* field =
            MakeGarbageCollected<DateTimeHour12FieldElement>(
                document, EditElement(), hour23_range_, step);
        EditElement().AddField(field);
        if (ShouldHourFieldDisabled()) {
          field->SetValueAsDate(date_value_);
          field->SetDisabled();
        }
        return;
      }
  
      case DateTimeFormat::kFieldTypeHour23: {
        DateTimeNumericFieldElement::Step step =
            CreateStep(kMsPerHour, kMsPerDay);
        DateTimeFieldElement* field =
            MakeGarbageCollected<DateTimeHour23FieldElement>(
                document, EditElement(), hour23_range_, step);
        EditElement().AddField(field);
        if (ShouldHourFieldDisabled()) {
          field->SetValueAsDate(date_value_);
          field->SetDisabled();
        }
        return;
      }
  
      case DateTimeFormat::kFieldTypeHour24: {
        DateTimeNumericFieldElement::Step step =
            CreateStep(kMsPerHour, kMsPerDay);
        DateTimeFieldElement* field =
            MakeGarbageCollected<DateTimeHour24FieldElement>(
                document, EditElement(), hour23_range_, step);
        EditElement().AddField(field);
        if (ShouldHourFieldDisabled()) {
          field->SetValueAsDate(date_value_);
          field->SetDisabled();
        }
        return;
      }
  
      case DateTimeFormat::kFieldTypeMinute: {
        DateTimeNumericFieldElement::Step step =
            CreateStep(kMsPerMinute, kMsPerHour);
        DateTimeNumericFieldElement* field =
            MakeGarbageCollected<DateTimeMinuteFieldElement>(
                document, EditElement(), minute_range_, step);
        EditElement().AddField(field);
        if (ShouldMinuteFieldDisabled()) {
          field->SetValueAsDate(date_value_);
          field->SetDisabled();
        }
        return;
      }
  
      case DateTimeFormat::kFieldTypeMonth:  // Fallthrough.
      case DateTimeFormat::kFieldTypeMonthStandAlone: {
        int min_month = 0, max_month = 11;
        if (parameters_.minimum.GetType() != DateComponents::kInvalid &&
            parameters_.maximum.GetType() != DateComponents::kInvalid &&
            parameters_.minimum.FullYear() == parameters_.maximum.FullYear() &&
            parameters_.minimum.Month() <= parameters_.maximum.Month()) {
          min_month = parameters_.minimum.Month();
          max_month = parameters_.maximum.Month();
        }
        DateTimeFieldElement* field;
        switch (count) {
          case kCountForNarrowMonth:  // Fallthrough.
          case kCountForAbbreviatedMonth:
            field = MakeGarbageCollected<DateTimeSymbolicMonthFieldElement>(
                document, EditElement(),
                field_type == DateTimeFormat::kFieldTypeMonth
                    ? parameters_.locale.ShortMonthLabels()
                    : parameters_.locale.ShortStandAloneMonthLabels(),
                min_month, max_month);
            break;
          case kCountForFullMonth:
            field = MakeGarbageCollected<DateTimeSymbolicMonthFieldElement>(
                document, EditElement(),
                field_type == DateTimeFormat::kFieldTypeMonth
                    ? parameters_.locale.MonthLabels()
                    : parameters_.locale.StandAloneMonthLabels(),
                min_month, max_month);
            break;
          default:
            field = MakeGarbageCollected<DateTimeMonthFieldElement>(
                document, EditElement(), parameters_.placeholder_for_month,
                DateTimeNumericFieldElement::Range(min_month + 1, max_month + 1));
            break;
        }
        EditElement().AddField(field);
        if (min_month == max_month && min_month == date_value_.Month() &&
            date_value_.GetType() != DateComponents::kMonth) {
          field->SetValueAsDate(date_value_);
          field->SetDisabled();
        }
        return;
      }
  
      // TODO(crbug.com/1261272): We don't support UI for
      // kFieldTypePeriodAmPmNoonMidnight and kFieldTypePeriodFlexible. Apply
      // the normal am/pm UI instead.
      case DateTimeFormat::kFieldTypePeriod:
      case DateTimeFormat::kFieldTypePeriodAmPmNoonMidnight:
      case DateTimeFormat::kFieldTypePeriodFlexible: {
        DateTimeFieldElement* field =
            MakeGarbageCollected<DateTimeAMPMFieldElement>(
                document, EditElement(), parameters_.locale.TimeAMPMLabels());
        EditElement().AddField(field);
        if (ShouldAMPMFieldDisabled()) {
          field->SetValueAsDate(date_value_);
          field->SetDisabled();
        }
        return;
      }
  
      case DateTimeFormat::kFieldTypeSecond: {
        DateTimeNumericFieldElement::Step step =
            CreateStep(kMsPerSecond, kMsPerMinute);
        DateTimeNumericFieldElement* field =
            MakeGarbageCollected<DateTimeSecondFieldElement>(
                document, EditElement(), second_range_, step);
        EditElement().AddField(field);
        if (ShouldSecondFieldDisabled()) {
          field->SetValueAsDate(date_value_);
          field->SetDisabled();
        }
  
        if (NeedMillisecondField()) {
          VisitLiteral(parameters_.locale.LocalizedDecimalSeparator());
          VisitField(DateTimeFormat::kFieldTypeFractionalSecond, 3);
        }
        return;
      }
  
      case DateTimeFormat::kFieldTypeFractionalSecond: {
        DateTimeNumericFieldElement::Step step = CreateStep(1, kMsPerSecond);
        DateTimeNumericFieldElement* field =
            MakeGarbageCollected<DateTimeMillisecondFieldElement>(
                document, EditElement(), millisecond_range_, step);
        EditElement().AddField(field);
        if (ShouldMillisecondFieldDisabled()) {
          field->SetValueAsDate(date_value_);
          field->SetDisabled();
        }
        return;
      }
  
      case DateTimeFormat::kFieldTypeWeekOfYear: {
        DateTimeNumericFieldElement::Range range(
            DateComponents::kMinimumWeekNumber,
            DateComponents::kMaximumWeekNumber);
        if (parameters_.minimum.GetType() != DateComponents::kInvalid &&
            parameters_.maximum.GetType() != DateComponents::kInvalid &&
            parameters_.minimum.FullYear() == parameters_.maximum.FullYear() &&
            parameters_.minimum.Week() <= parameters_.maximum.Week()) {
          range.minimum = parameters_.minimum.Week();
          range.maximum = parameters_.maximum.Week();
        }
        EditElement().AddField(MakeGarbageCollected<DateTimeWeekFieldElement>(
            document, EditElement(), range));
        return;
      }
  
      case DateTimeFormat::kFieldTypeYear: {
        DateTimeYearFieldElement::Parameters year_params;
        if (parameters_.minimum.GetType() == DateComponents::kInvalid) {
          year_params.minimum_year = DateComponents::MinimumYear();
          year_params.min_is_specified = false;
        } else {
          year_params.minimum_year = parameters_.minimum.FullYear();
          year_params.min_is_specified = true;
        }
        if (parameters_.maximum.GetType() == DateComponents::kInvalid) {
          year_params.maximum_year = DateComponents::MaximumYear();
          year_params.max_is_specified = false;
        } else {
          year_params.maximum_year = parameters_.maximum.FullYear();
          year_params.max_is_specified = true;
        }
        if (year_params.minimum_year > year_params.maximum_year) {
          std::swap(year_params.minimum_year, year_params.maximum_year);
          std::swap(year_params.min_is_specified, year_params.max_is_specified);
        }
        year_params.placeholder = parameters_.placeholder_for_year;
        DateTimeFieldElement* field =
            MakeGarbageCollected<DateTimeYearFieldElement>(
                document, EditElement(), year_params);
        EditElement().AddField(field);
        if (ShouldYearFieldDisabled()) {
          field->SetValueAsDate(date_value_);
          field->SetDisabled();
        }
        return;
      }
  
      default:
        return;
    }
  }
  
  bool DateTimeEditBuilder::ShouldAMPMFieldDisabled() const {
    return ShouldHourFieldDisabled() ||
           (hour23_range_.minimum < 12 && hour23_range_.maximum < 12 &&
            date_value_.Hour() < 12) ||
           (hour23_range_.minimum >= 12 && hour23_range_.maximum >= 12 &&
            date_value_.Hour() >= 12);
  }
  
  bool DateTimeEditBuilder::ShouldDayOfMonthFieldDisabled() const {
    return day_range_.IsSingleton() &&
           day_range_.minimum == date_value_.MonthDay() &&
           date_value_.GetType() != DateComponents::kDate;
  }
  
  bool DateTimeEditBuilder::ShouldHourFieldDisabled() const {
    if (hour23_range_.IsSingleton() &&
        hour23_range_.minimum == date_value_.Hour() &&
        !(ShouldMinuteFieldDisabled() && ShouldSecondFieldDisabled() &&
          ShouldMillisecondFieldDisabled()))
      return true;
  
    if (date_value_.GetType() == DateComponents::kTime)
      return false;
    DCHECK_EQ(date_value_.GetType(), DateComponents::kDateTimeLocal);
  
    if (ShouldDayOfMonthFieldDisabled()) {
      DCHECK_EQ(parameters_.minimum.FullYear(), parameters_.maximum.FullYear());
      DCHECK_EQ(parameters_.minimum.Month(), parameters_.maximum.Month());
      return false;
    }
  
    const Decimal decimal_ms_per_day(static_cast<int>(kMsPerDay));
    Decimal hour_part_of_minimum =
        (GetStepRange().StepBase().Abs().Remainder(decimal_ms_per_day) /
         static_cast<int>(kMsPerHour))
            .Floor();
    return hour_part_of_minimum == date_value_.Hour() &&
           GetStepRange().Step().Remainder(decimal_ms_per_day).IsZero();
  }
  
  bool DateTimeEditBuilder::ShouldMillisecondFieldDisabled() const {
    if (millisecond_range_.IsSingleton() &&
        millisecond_range_.minimum == date_value_.Millisecond())
      return true;
  
    const Decimal decimal_ms_per_second(static_cast<int>(kMsPerSecond));
    return GetStepRange().StepBase().Abs().Remainder(decimal_ms_per_second) ==
               date_value_.Millisecond() &&
           GetStepRange().Step().Remainder(decimal_ms_per_second).IsZero();
  }
  
  bool DateTimeEditBuilder::ShouldMinuteFieldDisabled() const {
    if (minute_range_.IsSingleton() &&
        minute_range_.minimum == date_value_.Minute())
      return true;
  
    const Decimal decimal_ms_per_hour(static_cast<int>(kMsPerHour));
    Decimal minute_part_of_minimum =
        (GetStepRange().StepBase().Abs().Remainder(decimal_ms_per_hour) /
         static_cast<int>(kMsPerMinute))
            .Floor();
    return minute_part_of_minimum == date_value_.Minute() &&
           GetStepRange().Step().Remainder(decimal_ms_per_hour).IsZero();
  }
  
  bool DateTimeEditBuilder::ShouldSecondFieldDisabled() const {
    if (second_range_.IsSingleton() &&
        second_range_.minimum == date_value_.Second())
      return true;
  
    const Decimal decimal_ms_per_minute(static_cast<int>(kMsPerMinute));
    Decimal second_part_of_minimum =
        (GetStepRange().StepBase().Abs().Remainder(decimal_ms_per_minute) /
         static_cast<int>(kMsPerSecond))
            .Floor();
    return second_part_of_minimum == date_value_.Second() &&
           GetStepRange().Step().Remainder(decimal_ms_per_minute).IsZero();
  }
  
  bool DateTimeEditBuilder::ShouldYearFieldDisabled() const {
    return parameters_.minimum.GetType() != DateComponents::kInvalid &&
           parameters_.maximum.GetType() != DateComponents::kInvalid &&
           parameters_.minimum.FullYear() == parameters_.maximum.FullYear() &&
           parameters_.minimum.FullYear() == date_value_.FullYear();
  }
  
  void DateTimeEditBuilder::VisitLiteral(const String& text) {
    DEFINE_STATIC_LOCAL(AtomicString, text_pseudo_id,
                        ("-webkit-datetime-edit-text"));
    DCHECK_GT(text.length(), 0u);
    auto* element =
        MakeGarbageCollected<HTMLDivElement>(EditElement().GetDocument());
    element->SetShadowPseudoId(text_pseudo_id);
    if (parameters_.locale.IsRTL() && text.length()) {
      WTF::unicode::CharDirection dir = WTF::unicode::Direction(text[0]);
      if (dir == WTF::unicode::kSegmentSeparator ||
          dir == WTF::unicode::kWhiteSpaceNeutral ||
          dir == WTF::unicode::kOtherNeutral) {
        element->AppendChild(Text::Create(
            EditElement().GetDocument(), String(&kRightToLeftMarkCharacter, 1u)));
      }
    }
    element->AppendChild(Text::Create(EditElement().GetDocument(), text));
    EditElement().FieldsWrapperElement()->AppendChild(element);
  }
  
  DateTimeEditElement& DateTimeEditBuilder::EditElement() const {
    return *edit_element_;
  }
  
  DateTimeNumericFieldElement::Step DateTimeEditBuilder::CreateStep(
      double ms_per_field_unit,
      double ms_per_field_size) const {
    const Decimal ms_per_field_unit_decimal(static_cast<int>(ms_per_field_unit));
    const Decimal ms_per_field_size_decimal(static_cast<int>(ms_per_field_size));
    Decimal step_milliseconds = GetStepRange().Step();
    DCHECK(!ms_per_field_unit_decimal.IsZero());
    DCHECK(!ms_per_field_size_decimal.IsZero());
    DCHECK(!step_milliseconds.IsZero());
  
    DateTimeNumericFieldElement::Step step(1, 0);
  
    if (step_milliseconds.Remainder(ms_per_field_size_decimal).IsZero())
      step_milliseconds = ms_per_field_size_decimal;
  
    if (ms_per_field_size_decimal.Remainder(step_milliseconds).IsZero() &&
        step_milliseconds.Remainder(ms_per_field_unit_decimal).IsZero()) {
      step.step = static_cast<int>(
          (step_milliseconds / ms_per_field_unit_decimal).ToDouble());
      step.step_base = static_cast<int>(
          (GetStepRange().StepBase() / ms_per_field_unit_decimal)
              .Floor()
              .Remainder(ms_per_field_size_decimal / ms_per_field_unit_decimal)
              .ToDouble());
    }
    return step;
  }
  
  // ----------------------------
  
  DateTimeEditElement::EditControlOwner::~EditControlOwner() = default;
  
  DateTimeEditElement::DateTimeEditElement(Document& document,
                                           EditControlOwner& edit_control_owner)
      : HTMLDivElement(document), edit_control_owner_(&edit_control_owner) {
    SetHasCustomStyleCallbacks();
    SetShadowPseudoId(AtomicString("-webkit-datetime-edit"));
    setAttribute(html_names::kIdAttr, shadow_element_names::kIdDateTimeEdit);
  }
  
  DateTimeEditElement::~DateTimeEditElement() = default;
  
  void DateTimeEditElement::Trace(Visitor* visitor) const {
    visitor->Trace(fields_);
    visitor->Trace(edit_control_owner_);
    HTMLDivElement::Trace(visitor);
  }
  
  inline Element* DateTimeEditElement::FieldsWrapperElement() const {
    CHECK(!firstChild() || IsA<Element>(firstChild()));
    return To<Element>(firstChild());
  }
  
  void DateTimeEditElement::AddField(DateTimeFieldElement* field) {
    if (fields_.size() >= kMaximumNumberOfFields)
      return;
    fields_.push_back(field);
    FieldsWrapperElement()->AppendChild(field);
  }
  
  bool DateTimeEditElement::AnyEditableFieldsHaveValues() const {
    for (const auto& field : fields_) {
      if (!field->IsDisabled() && field->HasValue())
        return true;
    }
    return false;
  }
  
  void DateTimeEditElement::BlurByOwner() {
    if (DateTimeFieldElement* field = FocusedField())
      field->blur();
  }
  
  scoped_refptr<ComputedStyle> DateTimeEditElement::CustomStyleForLayoutObject(
      const StyleRecalcContext& style_recalc_context) {
    // TODO(crbug.com/1181868): This is a kind of layout. We might want to
    // introduce new LayoutObject.
    scoped_refptr<ComputedStyle> style =
        OriginalStyleForLayoutObject(style_recalc_context);
    float width = 0;
    for (Node* child = FieldsWrapperElement()->firstChild(); child;
         child = child->nextSibling()) {
      auto* child_element = DynamicTo<Element>(child);
      if (!child_element)
        continue;
      if (child_element->IsDateTimeFieldElement()) {
        // We need to pass the ComputedStyle of this element because child
        // elements can't resolve inherited style at this timing.
        width += static_cast<DateTimeFieldElement*>(child_element)
                     ->MaximumWidth(*style);
      } else {
        // ::-webkit-datetime-edit-text case. It has no
        // border/padding/margin in html.css.
        width += DateTimeFieldElement::ComputeTextWidth(
            *style, child_element->textContent());
      }
    }
    style->SetWidth(Length::Fixed(ceilf(width)));
    style->SetCustomStyleCallbackDependsOnFont();
    return style;
  }
  
  void DateTimeEditElement::DidBlurFromField(mojom::blink::FocusType focus_type) {
    if (edit_control_owner_)
      edit_control_owner_->DidBlurFromControl(focus_type);
  }
  
  void DateTimeEditElement::DidFocusOnField(mojom::blink::FocusType focus_type) {
    if (edit_control_owner_)
      edit_control_owner_->DidFocusOnControl(focus_type);
  }
  
  void DateTimeEditElement::DisabledStateChanged() {
    UpdateUIState();
  }
  
  DateTimeFieldElement* DateTimeEditElement::FieldAt(
      wtf_size_t field_index) const {
    return field_index < fields_.size() ? fields_[field_index].Get() : nullptr;
  }
  
  wtf_size_t DateTimeEditElement::FieldIndexOf(
      const DateTimeFieldElement& field) const {
    for (wtf_size_t field_index = 0; field_index < fields_.size();
         ++field_index) {
      if (fields_[field_index] == &field)
        return field_index;
    }
    return kInvalidFieldIndex;
  }
  
  void DateTimeEditElement::FocusIfNoFocus() {
    if (FocusedFieldIndex() != kInvalidFieldIndex)
      return;
    FocusOnNextFocusableField(0);
  }
  
  void DateTimeEditElement::FocusByOwner(Element* old_focused_element) {
    if (old_focused_element && old_focused_element->IsDateTimeFieldElement()) {
      DateTimeFieldElement* old_focused_field =
          static_cast<DateTimeFieldElement*>(old_focused_element);
      wtf_size_t index = FieldIndexOf(*old_focused_field);
      GetDocument().UpdateStyleAndLayoutTreeForNode(old_focused_field);
      if (index != kInvalidFieldIndex && old_focused_field->IsFocusable()) {
        old_focused_field->focus();
        return;
      }
    }
    FocusOnNextFocusableField(0);
  }
  
  DateTimeFieldElement* DateTimeEditElement::FocusedField() const {
    return FieldAt(FocusedFieldIndex());
  }
  
  wtf_size_t DateTimeEditElement::FocusedFieldIndex() const {
    Element* const focused_field_element = GetDocument().FocusedElement();
    for (wtf_size_t field_index = 0; field_index < fields_.size();
         ++field_index) {
      if (fields_[field_index] == focused_field_element)
        return field_index;
    }
    return kInvalidFieldIndex;
  }
  
  void DateTimeEditElement::FieldValueChanged() {
    if (edit_control_owner_)
      edit_control_owner_->EditControlValueChanged();
  }
  
  bool DateTimeEditElement::FocusOnNextFocusableField(wtf_size_t start_index) {
    GetDocument().UpdateStyleAndLayoutTree();
    for (wtf_size_t field_index = start_index; field_index < fields_.size();
         ++field_index) {
      if (fields_[field_index]->IsFocusable()) {
        fields_[field_index]->focus();
        return true;
      }
    }
    return false;
  }
  
  bool DateTimeEditElement::FocusOnNextField(const DateTimeFieldElement& field) {
    const wtf_size_t start_field_index = FieldIndexOf(field);
    if (start_field_index == kInvalidFieldIndex)
      return false;
    return FocusOnNextFocusableField(start_field_index + 1);
  }
  
  bool DateTimeEditElement::FocusOnPreviousField(
      const DateTimeFieldElement& field) {
    const wtf_size_t start_field_index = FieldIndexOf(field);
    if (start_field_index == kInvalidFieldIndex)
      return false;
    GetDocument().UpdateStyleAndLayoutTree();
    wtf_size_t field_index = start_field_index;
    while (field_index > 0) {
      --field_index;
      if (fields_[field_index]->IsFocusable()) {
        fields_[field_index]->focus();
        return true;
      }
    }
    return false;
  }
  
  void DateTimeEditElement::HandleAmPmRollover(
      DateTimeFieldElement::FieldRolloverType rollover_type) {
    auto* ampm_field = GetField(DateTimeField::kAMPM);
    if (!ampm_field)
      return;
    DateTimeFieldsState date_time_fields_state = ValueAsDateTimeFieldsState();
    ampm_field->PopulateDateTimeFieldsState(date_time_fields_state);
    bool was_am =
        (date_time_fields_state.Ampm() == DateTimeFieldsState::kAMPMValueAM);
    date_time_fields_state.SetAMPM(DateTimeFieldsState::kAMPMValuePM);
    if (rollover_type != DateTimeFieldElement::FieldRolloverType::kToPm &&
        !was_am)
      date_time_fields_state.SetAMPM(DateTimeFieldsState::kAMPMValueAM);
    ampm_field->SetValueAsDateTimeFieldsState(date_time_fields_state);
    // No need to call EditControlValueChanged since change that triggered
    // rollover will do so.
  }
  
  bool DateTimeEditElement::IsDateTimeEditElement() const {
    return true;
  }
  
  bool DateTimeEditElement::IsDisabled() const {
    return edit_control_owner_ &&
           edit_control_owner_->IsEditControlOwnerDisabled();
  }
  
  bool DateTimeEditElement::IsFieldOwnerDisabled() const {
    return IsDisabled();
  }
  
  bool DateTimeEditElement::IsFieldOwnerReadOnly() const {
    return IsReadOnly();
  }
  
  bool DateTimeEditElement::IsReadOnly() const {
    return edit_control_owner_ &&
           edit_control_owner_->IsEditControlOwnerReadOnly();
  }
  
  void DateTimeEditElement::GetLayout(const LayoutParameters& layout_parameters,
                                      const DateComponents& date_value) {
    // TODO(tkent): We assume this function never dispatches events. However this
    // can dispatch 'blur' event in Node::removeChild().
  
    DEFINE_STATIC_LOCAL(AtomicString, fields_wrapper_pseudo_id,
                        ("-webkit-datetime-edit-fields-wrapper"));
    if (!HasChildren()) {
      auto* element = MakeGarbageCollected<HTMLDivElement>(GetDocument());
      element->SetShadowPseudoId(fields_wrapper_pseudo_id);
      AppendChild(element);
    }
    Element* fields_wrapper = FieldsWrapperElement();
  
    wtf_size_t focused_field_index = FocusedFieldIndex();
    DateTimeFieldElement* const focused_field = FieldAt(focused_field_index);
    const AtomicString focused_field_id =
        focused_field ? focused_field->ShadowPseudoId() : g_null_atom;
  
    DateTimeEditBuilder builder(*this, layout_parameters, date_value);
    Node* last_child_to_be_removed = fields_wrapper->lastChild();
    if (!builder.Build(layout_parameters.date_time_format) || fields_.IsEmpty()) {
      last_child_to_be_removed = fields_wrapper->lastChild();
      builder.Build(layout_parameters.fallback_date_time_format);
    }
  
    if (focused_field_index != kInvalidFieldIndex) {
      for (wtf_size_t field_index = 0; field_index < fields_.size();
           ++field_index) {
        if (fields_[field_index]->ShadowPseudoId() == focused_field_id) {
          focused_field_index = field_index;
          break;
        }
      }
      if (DateTimeFieldElement* field =
              FieldAt(std::min(focused_field_index, fields_.size() - 1)))
        field->focus();
    }
  
    if (last_child_to_be_removed) {
      for (Node* child_node = fields_wrapper->firstChild(); child_node;
           child_node = fields_wrapper->firstChild()) {
        fields_wrapper->RemoveChild(child_node);
        if (child_node == last_child_to_be_removed)
          break;
      }
      SetNeedsStyleRecalc(
          kSubtreeStyleChange,
          StyleChangeReasonForTracing::Create(style_change_reason::kControl));
    }
  }
  
  AtomicString DateTimeEditElement::LocaleIdentifier() const {
    return edit_control_owner_ ? edit_control_owner_->LocaleIdentifier()
                               : g_null_atom;
  }
  
  void DateTimeEditElement::FieldDidChangeValueByKeyboard() {
    if (edit_control_owner_)
      edit_control_owner_->EditControlDidChangeValueByKeyboard();
  }
  
  void DateTimeEditElement::ReadOnlyStateChanged() {
    UpdateUIState();
  }
  
  void DateTimeEditElement::ResetFields() {
    for (const auto& field : fields_)
      field->RemoveEventHandler();
    fields_.Shrink(0);
  }
  
  void DateTimeEditElement::DefaultEventHandler(Event& event) {
    // In case of control owner forward event to control, e.g. DOM
    // dispatchEvent method.
    if (DateTimeFieldElement* field = FocusedField()) {
      field->DefaultEventHandler(event);
      if (event.DefaultHandled())
        return;
    }
  
    HTMLDivElement::DefaultEventHandler(event);
  }
  
  void DateTimeEditElement::SetValueAsDate(
      const LayoutParameters& layout_parameters,
      const DateComponents& date) {
    GetLayout(layout_parameters, date);
    for (const auto& field : fields_)
      field->SetValueAsDate(date);
  }
  
  void DateTimeEditElement::SetValueAsDateTimeFieldsState(
      const DateTimeFieldsState& date_time_fields_state) {
    for (const auto& field : fields_)
      field->SetValueAsDateTimeFieldsState(date_time_fields_state);
  }
  
  void DateTimeEditElement::SetEmptyValue(
      const LayoutParameters& layout_parameters,
      const DateComponents& date_for_read_only_field) {
    GetLayout(layout_parameters, date_for_read_only_field);
    for (const auto& field : fields_)
      field->SetEmptyValue(DateTimeFieldElement::kDispatchNoEvent);
  }
  
  DateTimeFieldElement* DateTimeEditElement::GetField(DateTimeField type) const {
    auto* it = std::find_if(
        fields_.begin(), fields_.end(),
        [&type](const auto& field) { return field->Type() == type; });
    if (it == fields_.end())
      return nullptr;
    return *it;
  }
  
  bool DateTimeEditElement::HasField(DateTimeField type) const {
    for (const auto& field : fields_) {
      if (field->Type() == type)
        return true;
    }
  
    return false;
  }
  
  bool DateTimeEditElement::IsFirstFieldAMPM() const {
    const auto* first_field = FieldAt(0);
    return first_field && first_field->Type() == DateTimeField::kAMPM;
  }
  
  bool DateTimeEditElement::HasFocusedField() {
    return FocusedFieldIndex() != kInvalidFieldIndex;
  }
  
  void PopulateOnlyYearMonthDay(const DateComponents& date,
                                DateTimeFieldsState& date_time_fields_state) {
    date_time_fields_state.SetYear(date.FullYear());
    date_time_fields_state.SetMonth(date.Month() + 1);
    date_time_fields_state.SetDayOfMonth(date.MonthDay());
  }
  
  void DateTimeEditElement::SetOnlyYearMonthDay(const DateComponents& date) {
    DCHECK_EQ(date.GetType(), DateComponents::kDate);
  
    if (!edit_control_owner_)
      return;
  
    DateTimeFieldsState date_time_fields_state = ValueAsDateTimeFieldsState();
    PopulateOnlyYearMonthDay(date, date_time_fields_state);
    SetValueAsDateTimeFieldsState(date_time_fields_state);
    edit_control_owner_->EditControlValueChanged();
  }
  
  void PopulateOnlyTime(const DateComponents& date,
                        DateTimeFieldsState& date_time_fields_state) {
    date_time_fields_state.SetHour(date.Hour() % 12 ? date.Hour() % 12 : 12);
    date_time_fields_state.SetMinute(date.Minute());
    date_time_fields_state.SetSecond(date.Second());
    date_time_fields_state.SetMillisecond(date.Millisecond());
    date_time_fields_state.SetAMPM(date.Hour() >= 12
                                       ? DateTimeFieldsState::kAMPMValuePM
                                       : DateTimeFieldsState::kAMPMValueAM);
  }
  
  void DateTimeEditElement::SetOnlyTime(const DateComponents& date) {
    DCHECK_EQ(date.GetType(), DateComponents::kTime);
  
    if (!edit_control_owner_)
      return;
  
    DateTimeFieldsState date_time_fields_state = ValueAsDateTimeFieldsState();
    PopulateOnlyTime(date, date_time_fields_state);
    SetValueAsDateTimeFieldsState(date_time_fields_state);
    edit_control_owner_->EditControlValueChanged();
  }
  
  void PopulateDateTimeLocal(const DateComponents& date,
                             DateTimeFieldsState& date_time_fields_state) {
    PopulateOnlyYearMonthDay(date, date_time_fields_state);
    PopulateOnlyTime(date, date_time_fields_state);
  }
  
  void DateTimeEditElement::SetDateTimeLocal(const DateComponents& date) {
    DCHECK_EQ(date.GetType(), DateComponents::kDateTimeLocal);
  
    if (!edit_control_owner_)
      return;
  
    DateTimeFieldsState date_time_fields_state = ValueAsDateTimeFieldsState();
    PopulateDateTimeLocal(date, date_time_fields_state);
    SetValueAsDateTimeFieldsState(date_time_fields_state);
    edit_control_owner_->EditControlValueChanged();
  }
  
  void DateTimeEditElement::StepDown() {
    if (DateTimeFieldElement* const field = FocusedField())
      field->StepDown();
  }
  
  void DateTimeEditElement::StepUp() {
    if (DateTimeFieldElement* const field = FocusedField())
      field->StepUp();
  }
  
  void DateTimeEditElement::UpdateUIState() {
    if (IsDisabled()) {
      if (DateTimeFieldElement* field = FocusedField())
        field->blur();
    }
  }
  
  String DateTimeEditElement::Value() const {
    if (!edit_control_owner_)
      return g_empty_string;
    return edit_control_owner_->FormatDateTimeFieldsState(
        ValueAsDateTimeFieldsState());
  }
  
  DateTimeFieldsState DateTimeEditElement::ValueAsDateTimeFieldsState() const {
    DateTimeFieldsState date_time_fields_state;
    for (const auto& field : fields_)
      field->PopulateDateTimeFieldsState(date_time_fields_state);
    return date_time_fields_state;
  }
  
  }  // namespace blink
  