diff options
Diffstat (limited to 'Source/WebCore/html/shadow/TextControlInnerElements.cpp')
-rw-r--r-- | Source/WebCore/html/shadow/TextControlInnerElements.cpp | 385 |
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) - } |