Use a 1-byte enum class for TextDirection
[WebKit-https.git] / Source / WebCore / html / shadow / TextControlInnerElements.cpp
index 322abeb..45c9d34 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006, 2008, 2010, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2006-2018 Apple Inc. All rights reserved.
  * Copyright (C) 2010 Google Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include "config.h"
 #include "TextControlInnerElements.h"
 
+#include "CSSPrimitiveValue.h"
+#include "CSSToLengthConversionData.h"
 #include "Document.h"
-#include "EventHandler.h"
 #include "EventNames.h"
 #include "Frame.h"
 #include "HTMLInputElement.h"
 #include "HTMLNames.h"
+#include "LocalizedStrings.h"
 #include "MouseEvent.h"
+#include "PlatformMouseEvent.h"
 #include "RenderSearchField.h"
 #include "RenderTextControl.h"
 #include "RenderView.h"
 #include "ScriptController.h"
+#include "ShadowRoot.h"
+#include "StyleResolver.h"
 #include "TextEvent.h"
 #include "TextEventInputType.h"
+#include <wtf/IsoMallocInlines.h>
 #include <wtf/Ref.h>
 
 namespace WebCore {
 
+WTF_MAKE_ISO_ALLOCATED_IMPL(TextControlInnerContainer);
+WTF_MAKE_ISO_ALLOCATED_IMPL(TextControlInnerElement);
+WTF_MAKE_ISO_ALLOCATED_IMPL(TextControlInnerTextElement);
+WTF_MAKE_ISO_ALLOCATED_IMPL(TextControlPlaceholderElement);
+WTF_MAKE_ISO_ALLOCATED_IMPL(SearchFieldResultsButtonElement);
+WTF_MAKE_ISO_ALLOCATED_IMPL(SearchFieldCancelButtonElement);
+
 using namespace HTMLNames;
 
 TextControlInnerContainer::TextControlInnerContainer(Document& document)
     : HTMLDivElement(divTag, document)
 {
+    setHasCustomStyleResolveCallbacks();
 }
 
-PassRefPtr<TextControlInnerContainer> TextControlInnerContainer::create(Document& document)
+Ref<TextControlInnerContainer> TextControlInnerContainer::create(Document& document)
 {
-    return adoptRef(new TextControlInnerContainer(document));
+    return adoptRef(*new TextControlInnerContainer(document));
 }
     
-RenderPtr<RenderElement> TextControlInnerContainer::createElementRenderer(PassRef<RenderStyle> style)
+RenderPtr<RenderElement> TextControlInnerContainer::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
+{
+    return createRenderer<RenderTextControlInnerContainer>(*this, WTFMove(style));
+}
+
+static inline bool isStrongPasswordTextField(const Element* element)
 {
-    return createRenderer<RenderTextControlInnerContainer>(*this, WTF::move(style));
+    return is<HTMLInputElement>(element) && downcast<HTMLInputElement>(element)->hasAutoFillStrongPasswordButton();
+}
+
+std::optional<ElementStyle> TextControlInnerContainer::resolveCustomStyle(const RenderStyle& parentStyle, const RenderStyle*)
+{
+    auto elementStyle = resolveStyle(&parentStyle);
+    if (isStrongPasswordTextField(shadowHost())) {
+        elementStyle.renderStyle->setFlexWrap(FlexWrap::Wrap);
+        elementStyle.renderStyle->setOverflowX(Overflow::Hidden);
+        elementStyle.renderStyle->setOverflowY(Overflow::Hidden);
+    }
+    return WTFMove(elementStyle);
 }
 
 TextControlInnerElement::TextControlInnerElement(Document& document)
@@ -67,18 +97,40 @@ TextControlInnerElement::TextControlInnerElement(Document& document)
     setHasCustomStyleResolveCallbacks();
 }
 
-PassRefPtr<TextControlInnerElement> TextControlInnerElement::create(Document& document)
+Ref<TextControlInnerElement> TextControlInnerElement::create(Document& document)
 {
-    return adoptRef(new TextControlInnerElement(document));
+    return adoptRef(*new TextControlInnerElement(document));
 }
 
-PassRefPtr<RenderStyle> TextControlInnerElement::customStyleForRenderer(RenderStyle&)
+std::optional<ElementStyle> TextControlInnerElement::resolveCustomStyle(const RenderStyle&, const RenderStyle* shadowHostStyle)
 {
-    RenderTextControlSingleLine& parentRenderer = downcast<RenderTextControlSingleLine>(*shadowHost()->renderer());
-    return parentRenderer.createInnerBlockStyle(&parentRenderer.style());
+    auto newStyle = RenderStyle::createPtr();
+    newStyle->inheritFrom(*shadowHostStyle);
+    newStyle->setFlexGrow(1);
+    newStyle->setMinWidth(Length { 0, Fixed }); // Needed for correct shrinking.
+    newStyle->setDisplay(DisplayType::Block);
+    newStyle->setDirection(TextDirection::LTR);
+    // We don't want the shadow DOM to be editable, so we set this block to read-only in case the input itself is editable.
+    newStyle->setUserModify(UserModify::ReadOnly);
+
+    if (isStrongPasswordTextField(shadowHost())) {
+        newStyle->setFlexShrink(0);
+        newStyle->setTextOverflow(TextOverflow::Clip);
+        newStyle->setOverflowX(Overflow::Hidden);
+        newStyle->setOverflowY(Overflow::Hidden);
+
+        // Set "flex-basis: 1em". Note that CSSPrimitiveValue::computeLengthInt() only needs the element's
+        // style to calculate em lengths. Since the element might not be in a document, just pass nullptr
+        // for the root element style and the render view.
+        RefPtr<CSSPrimitiveValue> emSize = CSSPrimitiveValue::create(1, CSSPrimitiveValue::CSS_EMS);
+        int pixels = emSize->computeLength<int>(CSSToLengthConversionData { newStyle.get(), nullptr, nullptr, 1.0, false });
+        newStyle->setFlexBasis(Length { pixels, Fixed });
+    }
+
+    return ElementStyle { WTFMove(newStyle) };
 }
 
-// ---------------------------
+// MARK: TextControlInnerTextElement
 
 inline TextControlInnerTextElement::TextControlInnerTextElement(Document& document)
     : HTMLDivElement(divTag, document)
@@ -86,18 +138,18 @@ inline TextControlInnerTextElement::TextControlInnerTextElement(Document& docume
     setHasCustomStyleResolveCallbacks();
 }
 
-PassRefPtr<TextControlInnerTextElement> TextControlInnerTextElement::create(Document& document)
+Ref<TextControlInnerTextElement> TextControlInnerTextElement::create(Document& document)
 {
-    return adoptRef(new TextControlInnerTextElement(document));
+    return adoptRef(*new TextControlInnerTextElement(document));
 }
 
-void TextControlInnerTextElement::defaultEventHandler(Event* event)
+void TextControlInnerTextElement::defaultEventHandler(Event& event)
 {
     // FIXME: In the future, we should add a way to have default event listeners.
     // Then we would add one to the text field's inner div, and we wouldn't need this subclass.
     // Or possibly we could just use a normal event listener.
-    if (event->isBeforeTextInsertedEvent() || event->type() == eventNames().webkitEditableContentChangedEvent) {
-        Element* shadowAncestor = shadowHost();
+    if (event.isBeforeTextInsertedEvent() || event.type() == eventNames().webkitEditableContentChangedEvent) {
+        RefPtr<Element> shadowAncestor = shadowHost();
         // A TextControlInnerTextElement can have no host if its been detached,
         // but kept alive by an EditCommand. In this case, an undo/redo can
         // cause events to be sent to the TextControlInnerTextElement. To
@@ -106,13 +158,13 @@ void TextControlInnerTextElement::defaultEventHandler(Event* event)
         if (shadowAncestor)
             shadowAncestor->defaultEventHandler(event);
     }
-    if (!event->defaultHandled())
+    if (!event.defaultHandled())
         HTMLDivElement::defaultEventHandler(event);
 }
 
-RenderPtr<RenderElement> TextControlInnerTextElement::createElementRenderer(PassRef<RenderStyle> style)
+RenderPtr<RenderElement> TextControlInnerTextElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
 {
-    return createRenderer<RenderTextControlInnerBlock>(*this, WTF::move(style));
+    return createRenderer<RenderTextControlInnerBlock>(*this, WTFMove(style));
 }
 
 RenderTextControlInnerBlock* TextControlInnerTextElement::renderer() const
@@ -120,42 +172,72 @@ RenderTextControlInnerBlock* TextControlInnerTextElement::renderer() const
     return downcast<RenderTextControlInnerBlock>(HTMLDivElement::renderer());
 }
 
-PassRefPtr<RenderStyle> TextControlInnerTextElement::customStyleForRenderer(RenderStyle&)
+std::optional<ElementStyle> TextControlInnerTextElement::resolveCustomStyle(const RenderStyle&, const RenderStyle* shadowHostStyle)
 {
-    RenderTextControl& parentRenderer = downcast<RenderTextControl>(*shadowHost()->renderer());
-    return parentRenderer.createInnerTextStyle(&parentRenderer.style());
+    auto style = downcast<HTMLTextFormControlElement>(*shadowHost()).createInnerTextStyle(*shadowHostStyle);
+    return ElementStyle(std::make_unique<RenderStyle>(WTFMove(style)));
 }
 
-// ----------------------------
+// MARK: TextControlPlaceholderElement
+
+inline TextControlPlaceholderElement::TextControlPlaceholderElement(Document& document)
+    : HTMLDivElement(divTag, document)
+{
+    setPseudo(AtomicString("placeholder", AtomicString::ConstructFromLiteral));
+    setHasCustomStyleResolveCallbacks();
+}
+
+Ref<TextControlPlaceholderElement> TextControlPlaceholderElement::create(Document& document)
+{
+    return adoptRef(*new TextControlPlaceholderElement(document));
+}
+
+std::optional<ElementStyle> TextControlPlaceholderElement::resolveCustomStyle(const RenderStyle& parentStyle, const RenderStyle* shadowHostStyle)
+{
+    auto style = resolveStyle(&parentStyle);
+
+    auto& controlElement = downcast<HTMLTextFormControlElement>(*containingShadowRoot()->host());
+    style.renderStyle->setDisplay(controlElement.isPlaceholderVisible() ? DisplayType::Block : DisplayType::None);
+
+    if (is<HTMLInputElement>(controlElement)) {
+        auto& inputElement = downcast<HTMLInputElement>(controlElement);
+        style.renderStyle->setTextOverflow(inputElement.shouldTruncateText(*shadowHostStyle) ? TextOverflow::Ellipsis : TextOverflow::Clip);
+    }
+    return WTFMove(style);
+}
+
+// MARK: SearchFieldResultsButtonElement
 
 inline SearchFieldResultsButtonElement::SearchFieldResultsButtonElement(Document& document)
     : HTMLDivElement(divTag, document)
 {
 }
 
-PassRefPtr<SearchFieldResultsButtonElement> SearchFieldResultsButtonElement::create(Document& document)
+Ref<SearchFieldResultsButtonElement> SearchFieldResultsButtonElement::create(Document& document)
 {
-    return adoptRef(new SearchFieldResultsButtonElement(document));
+    return adoptRef(*new SearchFieldResultsButtonElement(document));
 }
 
-void SearchFieldResultsButtonElement::defaultEventHandler(Event* event)
+void SearchFieldResultsButtonElement::defaultEventHandler(Event& event)
 {
     // On mousedown, bring up a menu, if needed
-    HTMLInputElement* input = downcast<HTMLInputElement>(shadowHost());
-    if (input && event->type() == eventNames().mousedownEvent && is<MouseEvent>(*event) && downcast<MouseEvent>(*event).button() == LeftButton) {
+    auto input = makeRefPtr(downcast<HTMLInputElement>(shadowHost()));
+    if (input && event.type() == eventNames().mousedownEvent && is<MouseEvent>(event) && downcast<MouseEvent>(event).button() == LeftButton) {
         input->focus();
         input->select();
 #if !PLATFORM(IOS)
-        RenderSearchField& renderer = downcast<RenderSearchField>(*input->renderer());
-        if (renderer.popupIsVisible())
-            renderer.hidePopup();
-        else if (input->maxResults() > 0)
-            renderer.showPopup();
+        if (auto* renderer = input->renderer()) {
+            auto& searchFieldRenderer = downcast<RenderSearchField>(*renderer);
+            if (searchFieldRenderer.popupIsVisible())
+                searchFieldRenderer.hidePopup();
+            else if (input->maxResults() > 0)
+                searchFieldRenderer.showPopup();
+        }
 #endif
-        event->setDefaultHandled();
+        event.setDefaultHandled();
     }
 
-    if (!event->defaultHandled())
+    if (!event.defaultHandled())
         HTMLDivElement::defaultEventHandler(event);
 }
 
@@ -166,73 +248,52 @@ bool SearchFieldResultsButtonElement::willRespondToMouseClickEvents()
 }
 #endif
 
-// ----------------------------
+// MARK: SearchFieldCancelButtonElement
 
 inline SearchFieldCancelButtonElement::SearchFieldCancelButtonElement(Document& document)
     : HTMLDivElement(divTag, document)
-    , m_capturing(false)
 {
     setPseudo(AtomicString("-webkit-search-cancel-button", AtomicString::ConstructFromLiteral));
-    setHasCustomStyleResolveCallbacks();
+#if !PLATFORM(IOS)
+    setAttributeWithoutSynchronization(aria_labelAttr, AXSearchFieldCancelButtonText());
+#endif
+    setAttributeWithoutSynchronization(roleAttr, AtomicString("button", AtomicString::ConstructFromLiteral));
 }
 
-PassRefPtr<SearchFieldCancelButtonElement> SearchFieldCancelButtonElement::create(Document& document)
+Ref<SearchFieldCancelButtonElement> SearchFieldCancelButtonElement::create(Document& document)
 {
-    return adoptRef(new SearchFieldCancelButtonElement(document));
+    return adoptRef(*new SearchFieldCancelButtonElement(document));
 }
 
-void SearchFieldCancelButtonElement::willDetachRenderers()
+void SearchFieldCancelButtonElement::defaultEventHandler(Event& event)
 {
-    if (m_capturing) {
-        if (Frame* frame = document().frame())
-            frame->eventHandler().setCapturingMouseEventsElement(nullptr);
-    }
-}
-
-void SearchFieldCancelButtonElement::defaultEventHandler(Event* event)
-{
-    // If the element is visible, on mouseup, clear the value, and set selection
     RefPtr<HTMLInputElement> input(downcast<HTMLInputElement>(shadowHost()));
     if (!input || input->isDisabledOrReadOnly()) {
-        if (!event->defaultHandled())
+        if (!event.defaultHandled())
             HTMLDivElement::defaultEventHandler(event);
         return;
     }
 
-    if (event->type() == eventNames().mousedownEvent && is<MouseEvent>(*event) && downcast<MouseEvent>(*event).button() == LeftButton) {
-        if (renderer() && renderer()->visibleToHitTesting()) {
-            if (Frame* frame = document().frame()) {
-                frame->eventHandler().setCapturingMouseEventsElement(this);
-                m_capturing = true;
-            }
-        }
+    if (event.type() == eventNames().mousedownEvent && is<MouseEvent>(event) && downcast<MouseEvent>(event).button() == LeftButton) {
         input->focus();
         input->select();
-        event->setDefaultHandled();
+        event.setDefaultHandled();
     }
-    if (event->type() == eventNames().mouseupEvent && is<MouseEvent>(*event) && downcast<MouseEvent>(*event).button() == LeftButton) {
-        if (m_capturing) {
-            if (Frame* frame = document().frame()) {
-                frame->eventHandler().setCapturingMouseEventsElement(nullptr);
-                m_capturing = false;
-            }
-            if (hovered()) {
-                String oldValue = input->value();
-                input->setValueForUser("");
-                input->onSearch();
-                event->setDefaultHandled();
-            }
-        }
+
+    if (event.type() == eventNames().clickEvent) {
+        input->setValueForUser(emptyString());
+        input->onSearch();
+        event.setDefaultHandled();
     }
 
-    if (!event->defaultHandled())
+    if (!event.defaultHandled())
         HTMLDivElement::defaultEventHandler(event);
 }
 
 #if !PLATFORM(IOS)
 bool SearchFieldCancelButtonElement::willRespondToMouseClickEvents()
 {
-    const HTMLInputElement* input = downcast<HTMLInputElement>(shadowHost());
+    const RefPtr<HTMLInputElement> input = downcast<HTMLInputElement>(shadowHost());
     if (input && !input->isDisabledOrReadOnly())
         return true;