summaryrefslogtreecommitdiff
path: root/Source/WebCore/html/shadow/TextControlInnerElements.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/html/shadow/TextControlInnerElements.cpp')
-rw-r--r--Source/WebCore/html/shadow/TextControlInnerElements.cpp385
1 files changed, 88 insertions, 297 deletions
diff --git a/Source/WebCore/html/shadow/TextControlInnerElements.cpp b/Source/WebCore/html/shadow/TextControlInnerElements.cpp
index ac35831f3..584da68c7 100644
--- a/Source/WebCore/html/shadow/TextControlInnerElements.cpp
+++ b/Source/WebCore/html/shadow/TextControlInnerElements.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2008, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2006, 2008, 2010, 2014 Apple Inc. All rights reserved.
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,10 +11,10 @@
* 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 COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. 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
@@ -28,18 +28,19 @@
#include "TextControlInnerElements.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 "SpeechInput.h"
-#include "SpeechInputEvent.h"
+#include "ShadowRoot.h"
+#include "StyleResolver.h"
#include "TextEvent.h"
#include "TextEventInputType.h"
#include <wtf/Ref.h>
@@ -53,14 +54,14 @@ TextControlInnerContainer::TextControlInnerContainer(Document& document)
{
}
-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, std::move(style));
+ return createRenderer<RenderTextControlInnerContainer>(*this, WTFMove(style));
}
TextControlInnerElement::TextControlInnerElement(Document& document)
@@ -69,15 +70,26 @@ 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()
+std::optional<ElementStyle> TextControlInnerElement::resolveCustomStyle(const RenderStyle&, const RenderStyle* shadowHostStyle)
{
- RenderTextControlSingleLine* parentRenderer = toRenderTextControlSingleLine(shadowHost()->renderer());
- return parentRenderer->createInnerBlockStyle(&parentRenderer->style());
+ auto innerContainerStyle = RenderStyle::createPtr();
+ innerContainerStyle->inheritFrom(*shadowHostStyle);
+
+ innerContainerStyle->setFlexGrow(1);
+ // min-width: 0; is needed for correct shrinking.
+ innerContainerStyle->setMinWidth(Length(0, Fixed));
+ innerContainerStyle->setDisplay(BLOCK);
+ innerContainerStyle->setDirection(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.
+ innerContainerStyle->setUserModify(READ_ONLY);
+
+ return ElementStyle(WTFMove(innerContainerStyle));
}
// ---------------------------
@@ -88,17 +100,17 @@ 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) {
+ if (event.isBeforeTextInsertedEvent() || event.type() == eventNames().webkitEditableContentChangedEvent) {
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
@@ -108,256 +120,137 @@ 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, std::move(style));
+ return createRenderer<RenderTextControlInnerBlock>(*this, WTFMove(style));
}
RenderTextControlInnerBlock* TextControlInnerTextElement::renderer() const
{
- return toRenderTextControlInnerBlock(HTMLDivElement::renderer());
+ return downcast<RenderTextControlInnerBlock>(HTMLDivElement::renderer());
}
-PassRefPtr<RenderStyle> TextControlInnerTextElement::customStyleForRenderer()
+std::optional<ElementStyle> TextControlInnerTextElement::resolveCustomStyle(const RenderStyle&, const RenderStyle* shadowHostStyle)
{
- RenderTextControl* parentRenderer = toRenderTextControl(shadowHost()->renderer());
- return parentRenderer->createInnerTextStyle(&parentRenderer->style());
+ auto style = downcast<HTMLTextFormControlElement>(*shadowHost()).createInnerTextStyle(*shadowHostStyle);
+ return ElementStyle(std::make_unique<RenderStyle>(WTFMove(style)));
}
// ----------------------------
-inline SearchFieldResultsButtonElement::SearchFieldResultsButtonElement(Document& document)
+TextControlPlaceholderElement::TextControlPlaceholderElement(Document& document)
: HTMLDivElement(divTag, document)
{
+ setPseudo(AtomicString("placeholder", AtomicString::ConstructFromLiteral));
+ setHasCustomStyleResolveCallbacks();
}
-PassRefPtr<SearchFieldResultsButtonElement> SearchFieldResultsButtonElement::create(Document& document)
+std::optional<ElementStyle> TextControlPlaceholderElement::resolveCustomStyle(const RenderStyle& parentStyle, const RenderStyle* shadowHostStyle)
{
- return adoptRef(new SearchFieldResultsButtonElement(document));
-}
+ auto style = resolveStyle(&parentStyle);
-const AtomicString& SearchFieldResultsButtonElement::shadowPseudoId() const
-{
- DEFINE_STATIC_LOCAL(AtomicString, resultsId, ("-webkit-search-results-button", AtomicString::ConstructFromLiteral));
- DEFINE_STATIC_LOCAL(AtomicString, resultsDecorationId, ("-webkit-search-results-decoration", AtomicString::ConstructFromLiteral));
- DEFINE_STATIC_LOCAL(AtomicString, decorationId, ("-webkit-search-decoration", AtomicString::ConstructFromLiteral));
- Element* host = shadowHost();
- if (!host)
- return resultsId;
- if (HTMLInputElement* input = host->toInputElement()) {
- if (input->maxResults() < 0)
- return decorationId;
- if (input->maxResults() > 0)
- return resultsId;
- return resultsDecorationId;
- }
- return resultsId;
-}
+ auto& controlElement = downcast<HTMLTextFormControlElement>(*containingShadowRoot()->host());
+ style.renderStyle->setDisplay(controlElement.isPlaceholderVisible() ? BLOCK : NONE);
-void SearchFieldResultsButtonElement::defaultEventHandler(Event* event)
-{
- // On mousedown, bring up a menu, if needed
- HTMLInputElement* input = toHTMLInputElement(shadowHost());
- if (input && event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
- input->focus();
- input->select();
-#if !PLATFORM(IOS)
- RenderSearchField* renderer = toRenderSearchField(input->renderer());
- if (renderer->popupIsVisible())
- renderer->hidePopup();
- else if (input->maxResults() > 0)
- renderer->showPopup();
-#endif
- event->setDefaultHandled();
+ if (is<HTMLInputElement>(controlElement)) {
+ auto& inputElement = downcast<HTMLInputElement>(controlElement);
+ style.renderStyle->setTextOverflow(inputElement.shouldTruncateText(*shadowHostStyle) ? TextOverflowEllipsis : TextOverflowClip);
}
-
- if (!event->defaultHandled())
- HTMLDivElement::defaultEventHandler(event);
-}
-
-#if !PLATFORM(IOS)
-bool SearchFieldResultsButtonElement::willRespondToMouseClickEvents()
-{
- return true;
+ return WTFMove(style);
}
-#endif
// ----------------------------
-inline SearchFieldCancelButtonElement::SearchFieldCancelButtonElement(Document& document)
+inline SearchFieldResultsButtonElement::SearchFieldResultsButtonElement(Document& document)
: HTMLDivElement(divTag, document)
- , m_capturing(false)
{
- setHasCustomStyleResolveCallbacks();
}
-PassRefPtr<SearchFieldCancelButtonElement> SearchFieldCancelButtonElement::create(Document& document)
+Ref<SearchFieldResultsButtonElement> SearchFieldResultsButtonElement::create(Document& document)
{
- return adoptRef(new SearchFieldCancelButtonElement(document));
+ return adoptRef(*new SearchFieldResultsButtonElement(document));
}
-const AtomicString& SearchFieldCancelButtonElement::shadowPseudoId() const
+void SearchFieldResultsButtonElement::defaultEventHandler(Event& event)
{
- DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-search-cancel-button", AtomicString::ConstructFromLiteral));
- return pseudoId;
-}
-
-void SearchFieldCancelButtonElement::willDetachRenderers()
-{
- 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(toHTMLInputElement(shadowHost()));
- if (!input || input->isDisabledOrReadOnly()) {
- if (!event->defaultHandled())
- HTMLDivElement::defaultEventHandler(event);
- return;
- }
-
- if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
- if (renderer() && renderer()->visibleToHitTesting()) {
- if (Frame* frame = document().frame()) {
- frame->eventHandler().setCapturingMouseEventsElement(this);
- m_capturing = true;
- }
- }
+ // On mousedown, bring up a menu, if needed
+ auto* input = downcast<HTMLInputElement>(shadowHost());
+ if (input && event.type() == eventNames().mousedownEvent && is<MouseEvent>(event) && downcast<MouseEvent>(event).button() == LeftButton) {
input->focus();
input->select();
- event->setDefaultHandled();
- }
- if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<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 !PLATFORM(IOS)
+ 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();
}
- if (!event->defaultHandled())
+ if (!event.defaultHandled())
HTMLDivElement::defaultEventHandler(event);
}
#if !PLATFORM(IOS)
-bool SearchFieldCancelButtonElement::willRespondToMouseClickEvents()
+bool SearchFieldResultsButtonElement::willRespondToMouseClickEvents()
{
- const HTMLInputElement* input = toHTMLInputElement(shadowHost());
- if (input && !input->isDisabledOrReadOnly())
- return true;
-
- return HTMLDivElement::willRespondToMouseClickEvents();
+ return true;
}
#endif
// ----------------------------
-#if ENABLE(INPUT_SPEECH)
-
-inline InputFieldSpeechButtonElement::InputFieldSpeechButtonElement(Document& document)
+inline SearchFieldCancelButtonElement::SearchFieldCancelButtonElement(Document& document)
: HTMLDivElement(divTag, document)
- , m_capturing(false)
- , m_state(Idle)
- , m_listenerId(0)
-{
- setHasCustomStyleResolveCallbacks();
-}
-
-InputFieldSpeechButtonElement::~InputFieldSpeechButtonElement()
{
- SpeechInput* speech = speechInput();
- if (speech && m_listenerId) { // Could be null when page is unloading.
- if (m_state != Idle)
- speech->cancelRecognition(m_listenerId);
- speech->unregisterListener(m_listenerId);
- }
+ setPseudo(AtomicString("-webkit-search-cancel-button", AtomicString::ConstructFromLiteral));
+#if !PLATFORM(IOS)
+ setAttributeWithoutSynchronization(aria_labelAttr, AXSearchFieldCancelButtonText());
+#endif
+ setAttributeWithoutSynchronization(roleAttr, AtomicString("button", AtomicString::ConstructFromLiteral));
}
-PassRefPtr<InputFieldSpeechButtonElement> InputFieldSpeechButtonElement::create(Document& document)
+Ref<SearchFieldCancelButtonElement> SearchFieldCancelButtonElement::create(Document& document)
{
- return adoptRef(new InputFieldSpeechButtonElement(document));
+ return adoptRef(*new SearchFieldCancelButtonElement(document));
}
-void InputFieldSpeechButtonElement::defaultEventHandler(Event* event)
+void SearchFieldCancelButtonElement::defaultEventHandler(Event& event)
{
- // For privacy reasons, only allow clicks directly coming from the user.
- if (!ScriptController::processingUserGesture()) {
- HTMLDivElement::defaultEventHandler(event);
- return;
- }
-
- // The call to focus() below dispatches a focus event, and an event handler in the page might
- // remove the input element from DOM. To make sure it remains valid until we finish our work
- // here, we take a temporary reference.
- RefPtr<HTMLInputElement> input(toHTMLInputElement(shadowHost()));
-
+ RefPtr<HTMLInputElement> input(downcast<HTMLInputElement>(shadowHost()));
if (!input || input->isDisabledOrReadOnly()) {
- if (!event->defaultHandled())
+ if (!event.defaultHandled())
HTMLDivElement::defaultEventHandler(event);
return;
}
- // On mouse down, select the text and set focus.
- if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
- if (renderer() && renderer()->visibleToHitTesting()) {
- if (Frame* frame = document().frame()) {
- frame->eventHandler().setCapturingMouseEventsElement(this);
- m_capturing = true;
- }
- }
- Ref<InputFieldSpeechButtonElement> protect(*this);
+ if (event.type() == eventNames().mousedownEvent && is<MouseEvent>(event) && downcast<MouseEvent>(event).button() == LeftButton) {
input->focus();
input->select();
- event->setDefaultHandled();
- }
- // On mouse up, release capture cleanly.
- if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
- if (m_capturing && renderer() && renderer()->visibleToHitTesting()) {
- if (Frame* frame = document().frame()) {
- frame->eventHandler().setCapturingMouseEventsElement(nullptr);
- m_capturing = false;
- }
- }
+ event.setDefaultHandled();
}
- if (event->type() == eventNames().clickEvent && m_listenerId) {
- switch (m_state) {
- case Idle:
- startSpeechInput();
- break;
- case Recording:
- stopSpeechInput();
- break;
- case Recognizing:
- // Nothing to do here, we will continue to wait for results.
- break;
- }
- 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 InputFieldSpeechButtonElement::willRespondToMouseClickEvents()
+bool SearchFieldCancelButtonElement::willRespondToMouseClickEvents()
{
- const HTMLInputElement* input = toHTMLInputElement(shadowHost());
+ const HTMLInputElement* input = downcast<HTMLInputElement>(shadowHost());
if (input && !input->isDisabledOrReadOnly())
return true;
@@ -365,106 +258,4 @@ bool InputFieldSpeechButtonElement::willRespondToMouseClickEvents()
}
#endif
-void InputFieldSpeechButtonElement::setState(SpeechInputState state)
-{
- if (m_state != state) {
- m_state = state;
- shadowHost()->renderer()->repaint();
- }
-}
-
-SpeechInput* InputFieldSpeechButtonElement::speechInput()
-{
- return SpeechInput::from(document().page());
-}
-
-void InputFieldSpeechButtonElement::didCompleteRecording(int)
-{
- setState(Recognizing);
-}
-
-void InputFieldSpeechButtonElement::didCompleteRecognition(int)
-{
- setState(Idle);
-}
-
-void InputFieldSpeechButtonElement::setRecognitionResult(int, const SpeechInputResultArray& results)
-{
- m_results = results;
-
- // The call to setValue() below dispatches an event, and an event handler in the page might
- // remove the input element from DOM. To make sure it remains valid until we finish our work
- // here, we take a temporary reference.
- RefPtr<HTMLInputElement> input(toHTMLInputElement(shadowHost()));
- if (!input || input->isDisabledOrReadOnly())
- return;
-
- Ref<InputFieldSpeechButtonElement> protect(*this);
- if (document().domWindow()) {
- // Call selectionChanged, causing the element to cache the selection,
- // so that the text event inserts the text in this element even if
- // focus has moved away from it.
- input->selectionChanged(false);
- input->dispatchEvent(TextEvent::create(document().domWindow(), results.isEmpty() ? "" : results[0]->utterance(), TextEventInputOther));
- }
-
- // This event is sent after the text event so the website can perform actions using the input field content immediately.
- // It provides alternative recognition hypotheses and notifies that the results come from speech input.
- input->dispatchEvent(SpeechInputEvent::create(eventNames().webkitspeechchangeEvent, results));
-
- // Check before accessing the renderer as the above event could have potentially turned off
- // speech in the input element, hence removing this button and renderer from the hierarchy.
- if (renderer())
- renderer()->repaint();
-}
-
-void InputFieldSpeechButtonElement::willAttachRenderers()
-{
- ASSERT(!m_listenerId);
- if (SpeechInput* input = SpeechInput::from(document().page()))
- m_listenerId = input->registerListener(this);
-}
-
-void InputFieldSpeechButtonElement::willDetachRenderers()
-{
- if (m_capturing) {
- if (Frame* frame = document().frame())
- frame->eventHandler().setCapturingMouseEventsElement(nullptr);
- }
-
- if (m_listenerId) {
- if (m_state != Idle)
- speechInput()->cancelRecognition(m_listenerId);
- speechInput()->unregisterListener(m_listenerId);
- m_listenerId = 0;
- }
-}
-
-void InputFieldSpeechButtonElement::startSpeechInput()
-{
- if (m_state != Idle)
- return;
-
- RefPtr<HTMLInputElement> input = toHTMLInputElement(shadowHost());
- AtomicString language = input->computeInheritedLanguage();
- String grammar = input->getAttribute(webkitgrammarAttr);
- IntRect rect = document().view()->contentsToRootView(pixelSnappedBoundingBox());
- if (speechInput()->startRecognition(m_listenerId, rect, language, grammar, document().securityOrigin()))
- setState(Recording);
-}
-
-void InputFieldSpeechButtonElement::stopSpeechInput()
-{
- if (m_state == Recording)
- speechInput()->stopRecording(m_listenerId);
-}
-
-const AtomicString& InputFieldSpeechButtonElement::shadowPseudoId() const
-{
- DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-input-speech-button", AtomicString::ConstructFromLiteral));
- return pseudoId;
-}
-
-#endif // ENABLE(INPUT_SPEECH)
-
}