DOM event handling should pass Event around by reference.
[WebKit-https.git] / Source / WebCore / html / shadow / TextControlInnerElements.cpp
1 /*
2  * Copyright (C) 2006, 2008, 2010, 2014 Apple Inc. All rights reserved.
3  * Copyright (C) 2010 Google Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "TextControlInnerElements.h"
29
30 #include "Document.h"
31 #include "EventNames.h"
32 #include "Frame.h"
33 #include "HTMLInputElement.h"
34 #include "HTMLNames.h"
35 #include "LocalizedStrings.h"
36 #include "MouseEvent.h"
37 #include "PlatformMouseEvent.h"
38 #include "RenderSearchField.h"
39 #include "RenderTextControl.h"
40 #include "RenderView.h"
41 #include "ScriptController.h"
42 #include "ShadowRoot.h"
43 #include "StyleResolver.h"
44 #include "TextEvent.h"
45 #include "TextEventInputType.h"
46 #include <wtf/Ref.h>
47
48 namespace WebCore {
49
50 using namespace HTMLNames;
51
52 TextControlInnerContainer::TextControlInnerContainer(Document& document)
53     : HTMLDivElement(divTag, document)
54 {
55 }
56
57 Ref<TextControlInnerContainer> TextControlInnerContainer::create(Document& document)
58 {
59     return adoptRef(*new TextControlInnerContainer(document));
60 }
61     
62 RenderPtr<RenderElement> TextControlInnerContainer::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
63 {
64     return createRenderer<RenderTextControlInnerContainer>(*this, WTFMove(style));
65 }
66
67 TextControlInnerElement::TextControlInnerElement(Document& document)
68     : HTMLDivElement(divTag, document)
69 {
70     setHasCustomStyleResolveCallbacks();
71 }
72
73 Ref<TextControlInnerElement> TextControlInnerElement::create(Document& document)
74 {
75     return adoptRef(*new TextControlInnerElement(document));
76 }
77
78 Optional<ElementStyle> TextControlInnerElement::resolveCustomStyle(const RenderStyle&, const RenderStyle* shadowHostStyle)
79 {
80     auto innerContainerStyle = RenderStyle::createPtr();
81     innerContainerStyle->inheritFrom(shadowHostStyle);
82
83     innerContainerStyle->setFlexGrow(1);
84     // min-width: 0; is needed for correct shrinking.
85     innerContainerStyle->setMinWidth(Length(0, Fixed));
86     innerContainerStyle->setDisplay(BLOCK);
87     innerContainerStyle->setDirection(LTR);
88
89     // 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.
90     innerContainerStyle->setUserModify(READ_ONLY);
91
92     return ElementStyle(WTFMove(innerContainerStyle));
93 }
94
95 // ---------------------------
96
97 inline TextControlInnerTextElement::TextControlInnerTextElement(Document& document)
98     : HTMLDivElement(divTag, document)
99 {
100     setHasCustomStyleResolveCallbacks();
101 }
102
103 Ref<TextControlInnerTextElement> TextControlInnerTextElement::create(Document& document)
104 {
105     return adoptRef(*new TextControlInnerTextElement(document));
106 }
107
108 void TextControlInnerTextElement::defaultEventHandler(Event& event)
109 {
110     // FIXME: In the future, we should add a way to have default event listeners.
111     // Then we would add one to the text field's inner div, and we wouldn't need this subclass.
112     // Or possibly we could just use a normal event listener.
113     if (event.isBeforeTextInsertedEvent() || event.type() == eventNames().webkitEditableContentChangedEvent) {
114         Element* shadowAncestor = shadowHost();
115         // A TextControlInnerTextElement can have no host if its been detached,
116         // but kept alive by an EditCommand. In this case, an undo/redo can
117         // cause events to be sent to the TextControlInnerTextElement. To
118         // prevent an infinite loop, we must check for this case before sending
119         // the event up the chain.
120         if (shadowAncestor)
121             shadowAncestor->defaultEventHandler(event);
122     }
123     if (!event.defaultHandled())
124         HTMLDivElement::defaultEventHandler(event);
125 }
126
127 RenderPtr<RenderElement> TextControlInnerTextElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
128 {
129     return createRenderer<RenderTextControlInnerBlock>(*this, WTFMove(style));
130 }
131
132 RenderTextControlInnerBlock* TextControlInnerTextElement::renderer() const
133 {
134     return downcast<RenderTextControlInnerBlock>(HTMLDivElement::renderer());
135 }
136
137 Optional<ElementStyle> TextControlInnerTextElement::resolveCustomStyle(const RenderStyle&, const RenderStyle* shadowHostStyle)
138 {
139     auto style = downcast<HTMLTextFormControlElement>(*shadowHost()).createInnerTextStyle(*shadowHostStyle);
140     return ElementStyle(std::make_unique<RenderStyle>(WTFMove(style)));
141 }
142
143 // ----------------------------
144
145 TextControlPlaceholderElement::TextControlPlaceholderElement(Document& document)
146     : HTMLDivElement(divTag, document)
147 {
148     setPseudo(AtomicString("placeholder", AtomicString::ConstructFromLiteral));
149     setHasCustomStyleResolveCallbacks();
150 }
151
152 Optional<ElementStyle> TextControlPlaceholderElement::resolveCustomStyle(const RenderStyle& parentStyle, const RenderStyle* shadowHostStyle)
153 {
154     auto style = resolveStyle(&parentStyle);
155
156     auto& controlElement = downcast<HTMLTextFormControlElement>(*containingShadowRoot()->host());
157     style.renderStyle->setDisplay(controlElement.isPlaceholderVisible() ? BLOCK : NONE);
158
159     if (is<HTMLInputElement>(controlElement)) {
160         auto& inputElement = downcast<HTMLInputElement>(controlElement);
161         style.renderStyle->setTextOverflow(inputElement.shouldTruncateText(*shadowHostStyle) ? TextOverflowEllipsis : TextOverflowClip);
162     }
163     return WTFMove(style);
164 }
165
166 // ----------------------------
167
168 inline SearchFieldResultsButtonElement::SearchFieldResultsButtonElement(Document& document)
169     : HTMLDivElement(divTag, document)
170 {
171 }
172
173 Ref<SearchFieldResultsButtonElement> SearchFieldResultsButtonElement::create(Document& document)
174 {
175     return adoptRef(*new SearchFieldResultsButtonElement(document));
176 }
177
178 void SearchFieldResultsButtonElement::defaultEventHandler(Event& event)
179 {
180     // On mousedown, bring up a menu, if needed
181     auto* input = downcast<HTMLInputElement>(shadowHost());
182     if (input && event.type() == eventNames().mousedownEvent && is<MouseEvent>(event) && downcast<MouseEvent>(event).button() == LeftButton) {
183         input->focus();
184         input->select();
185 #if !PLATFORM(IOS)
186         if (auto* renderer = input->renderer()) {
187             auto& searchFieldRenderer = downcast<RenderSearchField>(*renderer);
188             if (searchFieldRenderer.popupIsVisible())
189                 searchFieldRenderer.hidePopup();
190             else if (input->maxResults() > 0)
191                 searchFieldRenderer.showPopup();
192         }
193 #endif
194         event.setDefaultHandled();
195     }
196
197     if (!event.defaultHandled())
198         HTMLDivElement::defaultEventHandler(event);
199 }
200
201 #if !PLATFORM(IOS)
202 bool SearchFieldResultsButtonElement::willRespondToMouseClickEvents()
203 {
204     return true;
205 }
206 #endif
207
208 // ----------------------------
209
210 inline SearchFieldCancelButtonElement::SearchFieldCancelButtonElement(Document& document)
211     : HTMLDivElement(divTag, document)
212 {
213     setPseudo(AtomicString("-webkit-search-cancel-button", AtomicString::ConstructFromLiteral));
214 #if !PLATFORM(IOS)
215     setAttributeWithoutSynchronization(aria_labelAttr, AXSearchFieldCancelButtonText());
216 #endif
217     setAttributeWithoutSynchronization(roleAttr, AtomicString("button", AtomicString::ConstructFromLiteral));
218 }
219
220 Ref<SearchFieldCancelButtonElement> SearchFieldCancelButtonElement::create(Document& document)
221 {
222     return adoptRef(*new SearchFieldCancelButtonElement(document));
223 }
224
225 void SearchFieldCancelButtonElement::defaultEventHandler(Event& event)
226 {
227     RefPtr<HTMLInputElement> input(downcast<HTMLInputElement>(shadowHost()));
228     if (!input || input->isDisabledOrReadOnly()) {
229         if (!event.defaultHandled())
230             HTMLDivElement::defaultEventHandler(event);
231         return;
232     }
233
234     if (event.type() == eventNames().mousedownEvent && is<MouseEvent>(event) && downcast<MouseEvent>(event).button() == LeftButton) {
235         input->focus();
236         input->select();
237         event.setDefaultHandled();
238     }
239
240     if (event.type() == eventNames().clickEvent) {
241         input->setValueForUser(emptyString());
242         input->onSearch();
243         event.setDefaultHandled();
244     }
245
246     if (!event.defaultHandled())
247         HTMLDivElement::defaultEventHandler(event);
248 }
249
250 #if !PLATFORM(IOS)
251 bool SearchFieldCancelButtonElement::willRespondToMouseClickEvents()
252 {
253     const HTMLInputElement* input = downcast<HTMLInputElement>(shadowHost());
254     if (input && !input->isDisabledOrReadOnly())
255         return true;
256
257     return HTMLDivElement::willRespondToMouseClickEvents();
258 }
259 #endif
260
261 }