Add WTF::move()
[WebKit-https.git] / Source / WebCore / html / shadow / TextControlInnerElements.cpp
1 /*
2  * Copyright (C) 2006, 2008, 2010 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 "EventHandler.h"
32 #include "EventNames.h"
33 #include "Frame.h"
34 #include "HTMLInputElement.h"
35 #include "HTMLNames.h"
36 #include "MouseEvent.h"
37 #include "RenderSearchField.h"
38 #include "RenderTextControl.h"
39 #include "RenderView.h"
40 #include "ScriptController.h"
41 #include "SpeechInput.h"
42 #include "SpeechInputEvent.h"
43 #include "TextEvent.h"
44 #include "TextEventInputType.h"
45 #include <wtf/Ref.h>
46
47 namespace WebCore {
48
49 using namespace HTMLNames;
50
51 TextControlInnerContainer::TextControlInnerContainer(Document& document)
52     : HTMLDivElement(divTag, document)
53 {
54 }
55
56 PassRefPtr<TextControlInnerContainer> TextControlInnerContainer::create(Document& document)
57 {
58     return adoptRef(new TextControlInnerContainer(document));
59 }
60     
61 RenderPtr<RenderElement> TextControlInnerContainer::createElementRenderer(PassRef<RenderStyle> style)
62 {
63     return createRenderer<RenderTextControlInnerContainer>(*this, WTF::move(style));
64 }
65
66 TextControlInnerElement::TextControlInnerElement(Document& document)
67     : HTMLDivElement(divTag, document)
68 {
69     setHasCustomStyleResolveCallbacks();
70 }
71
72 PassRefPtr<TextControlInnerElement> TextControlInnerElement::create(Document& document)
73 {
74     return adoptRef(new TextControlInnerElement(document));
75 }
76
77 PassRefPtr<RenderStyle> TextControlInnerElement::customStyleForRenderer(RenderStyle&)
78 {
79     RenderTextControlSingleLine* parentRenderer = toRenderTextControlSingleLine(shadowHost()->renderer());
80     return parentRenderer->createInnerBlockStyle(&parentRenderer->style());
81 }
82
83 // ---------------------------
84
85 inline TextControlInnerTextElement::TextControlInnerTextElement(Document& document)
86     : HTMLDivElement(divTag, document)
87 {
88     setHasCustomStyleResolveCallbacks();
89 }
90
91 PassRefPtr<TextControlInnerTextElement> TextControlInnerTextElement::create(Document& document)
92 {
93     return adoptRef(new TextControlInnerTextElement(document));
94 }
95
96 void TextControlInnerTextElement::defaultEventHandler(Event* event)
97 {
98     // FIXME: In the future, we should add a way to have default event listeners.
99     // Then we would add one to the text field's inner div, and we wouldn't need this subclass.
100     // Or possibly we could just use a normal event listener.
101     if (event->isBeforeTextInsertedEvent() || event->type() == eventNames().webkitEditableContentChangedEvent) {
102         Element* shadowAncestor = shadowHost();
103         // A TextControlInnerTextElement can have no host if its been detached,
104         // but kept alive by an EditCommand. In this case, an undo/redo can
105         // cause events to be sent to the TextControlInnerTextElement. To
106         // prevent an infinite loop, we must check for this case before sending
107         // the event up the chain.
108         if (shadowAncestor)
109             shadowAncestor->defaultEventHandler(event);
110     }
111     if (!event->defaultHandled())
112         HTMLDivElement::defaultEventHandler(event);
113 }
114
115 RenderPtr<RenderElement> TextControlInnerTextElement::createElementRenderer(PassRef<RenderStyle> style)
116 {
117     return createRenderer<RenderTextControlInnerBlock>(*this, WTF::move(style));
118 }
119
120 RenderTextControlInnerBlock* TextControlInnerTextElement::renderer() const
121 {
122     return toRenderTextControlInnerBlock(HTMLDivElement::renderer());
123 }
124
125 PassRefPtr<RenderStyle> TextControlInnerTextElement::customStyleForRenderer(RenderStyle&)
126 {
127     RenderTextControl* parentRenderer = toRenderTextControl(shadowHost()->renderer());
128     return parentRenderer->createInnerTextStyle(&parentRenderer->style());
129 }
130
131 // ----------------------------
132
133 inline SearchFieldResultsButtonElement::SearchFieldResultsButtonElement(Document& document)
134     : HTMLDivElement(divTag, document)
135 {
136 }
137
138 PassRefPtr<SearchFieldResultsButtonElement> SearchFieldResultsButtonElement::create(Document& document)
139 {
140     return adoptRef(new SearchFieldResultsButtonElement(document));
141 }
142
143 const AtomicString& SearchFieldResultsButtonElement::shadowPseudoId() const
144 {
145     DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, resultsId, ("-webkit-search-results-button", AtomicString::ConstructFromLiteral));
146     DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, resultsDecorationId, ("-webkit-search-results-decoration", AtomicString::ConstructFromLiteral));
147     DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, decorationId, ("-webkit-search-decoration", AtomicString::ConstructFromLiteral));
148     Element* host = shadowHost();
149     if (!host)
150         return resultsId;
151     if (HTMLInputElement* input = host->toInputElement()) {
152         if (input->maxResults() < 0)
153             return decorationId;
154         if (input->maxResults() > 0)
155             return resultsId;
156         return resultsDecorationId;
157     }
158     return resultsId;
159 }
160
161 void SearchFieldResultsButtonElement::defaultEventHandler(Event* event)
162 {
163     // On mousedown, bring up a menu, if needed
164     HTMLInputElement* input = toHTMLInputElement(shadowHost());
165     if (input && event->type() == eventNames().mousedownEvent && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton) {
166         input->focus();
167         input->select();
168 #if !PLATFORM(IOS)
169         RenderSearchField* renderer = toRenderSearchField(input->renderer());
170         if (renderer->popupIsVisible())
171             renderer->hidePopup();
172         else if (input->maxResults() > 0)
173             renderer->showPopup();
174 #endif
175         event->setDefaultHandled();
176     }
177
178     if (!event->defaultHandled())
179         HTMLDivElement::defaultEventHandler(event);
180 }
181
182 #if !PLATFORM(IOS)
183 bool SearchFieldResultsButtonElement::willRespondToMouseClickEvents()
184 {
185     return true;
186 }
187 #endif
188
189 // ----------------------------
190
191 inline SearchFieldCancelButtonElement::SearchFieldCancelButtonElement(Document& document)
192     : HTMLDivElement(divTag, document)
193     , m_capturing(false)
194 {
195     setHasCustomStyleResolveCallbacks();
196 }
197
198 PassRefPtr<SearchFieldCancelButtonElement> SearchFieldCancelButtonElement::create(Document& document)
199 {
200     return adoptRef(new SearchFieldCancelButtonElement(document));
201 }
202
203 const AtomicString& SearchFieldCancelButtonElement::shadowPseudoId() const
204 {
205     DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-search-cancel-button", AtomicString::ConstructFromLiteral));
206     return pseudoId;
207 }
208
209 void SearchFieldCancelButtonElement::willDetachRenderers()
210 {
211     if (m_capturing) {
212         if (Frame* frame = document().frame())
213             frame->eventHandler().setCapturingMouseEventsElement(nullptr);
214     }
215 }
216
217 void SearchFieldCancelButtonElement::defaultEventHandler(Event* event)
218 {
219     // If the element is visible, on mouseup, clear the value, and set selection
220     RefPtr<HTMLInputElement> input(toHTMLInputElement(shadowHost()));
221     if (!input || input->isDisabledOrReadOnly()) {
222         if (!event->defaultHandled())
223             HTMLDivElement::defaultEventHandler(event);
224         return;
225     }
226
227     if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton) {
228         if (renderer() && renderer()->visibleToHitTesting()) {
229             if (Frame* frame = document().frame()) {
230                 frame->eventHandler().setCapturingMouseEventsElement(this);
231                 m_capturing = true;
232             }
233         }
234         input->focus();
235         input->select();
236         event->setDefaultHandled();
237     }
238     if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton) {
239         if (m_capturing) {
240             if (Frame* frame = document().frame()) {
241                 frame->eventHandler().setCapturingMouseEventsElement(nullptr);
242                 m_capturing = false;
243             }
244             if (hovered()) {
245                 String oldValue = input->value();
246                 input->setValueForUser("");
247                 input->onSearch();
248                 event->setDefaultHandled();
249             }
250         }
251     }
252
253     if (!event->defaultHandled())
254         HTMLDivElement::defaultEventHandler(event);
255 }
256
257 #if !PLATFORM(IOS)
258 bool SearchFieldCancelButtonElement::willRespondToMouseClickEvents()
259 {
260     const HTMLInputElement* input = toHTMLInputElement(shadowHost());
261     if (input && !input->isDisabledOrReadOnly())
262         return true;
263
264     return HTMLDivElement::willRespondToMouseClickEvents();
265 }
266 #endif
267
268 // ----------------------------
269
270 #if ENABLE(INPUT_SPEECH)
271
272 inline InputFieldSpeechButtonElement::InputFieldSpeechButtonElement(Document& document)
273     : HTMLDivElement(divTag, document)
274     , m_capturing(false)
275     , m_state(Idle)
276     , m_listenerId(0)
277 {
278     setHasCustomStyleResolveCallbacks();
279 }
280
281 InputFieldSpeechButtonElement::~InputFieldSpeechButtonElement()
282 {
283     SpeechInput* speech = speechInput();
284     if (speech && m_listenerId)  { // Could be null when page is unloading.
285         if (m_state != Idle)
286             speech->cancelRecognition(m_listenerId);
287         speech->unregisterListener(m_listenerId);
288     }
289 }
290
291 PassRefPtr<InputFieldSpeechButtonElement> InputFieldSpeechButtonElement::create(Document& document)
292 {
293     return adoptRef(new InputFieldSpeechButtonElement(document));
294 }
295
296 void InputFieldSpeechButtonElement::defaultEventHandler(Event* event)
297 {
298     // For privacy reasons, only allow clicks directly coming from the user.
299     if (!ScriptController::processingUserGesture()) {
300         HTMLDivElement::defaultEventHandler(event);
301         return;
302     }
303
304     // The call to focus() below dispatches a focus event, and an event handler in the page might
305     // remove the input element from DOM. To make sure it remains valid until we finish our work
306     // here, we take a temporary reference.
307     RefPtr<HTMLInputElement> input(toHTMLInputElement(shadowHost()));
308
309     if (!input || input->isDisabledOrReadOnly()) {
310         if (!event->defaultHandled())
311             HTMLDivElement::defaultEventHandler(event);
312         return;
313     }
314
315     // On mouse down, select the text and set focus.
316     if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton) {
317         if (renderer() && renderer()->visibleToHitTesting()) {
318             if (Frame* frame = document().frame()) {
319                 frame->eventHandler().setCapturingMouseEventsElement(this);
320                 m_capturing = true;
321             }
322         }
323         Ref<InputFieldSpeechButtonElement> protect(*this);
324         input->focus();
325         input->select();
326         event->setDefaultHandled();
327     }
328     // On mouse up, release capture cleanly.
329     if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && toMouseEvent(event)->button() == LeftButton) {
330         if (m_capturing && renderer() && renderer()->visibleToHitTesting()) {
331             if (Frame* frame = document().frame()) {
332                 frame->eventHandler().setCapturingMouseEventsElement(nullptr);
333                 m_capturing = false;
334             }
335         }
336     }
337
338     if (event->type() == eventNames().clickEvent && m_listenerId) {
339         switch (m_state) {
340         case Idle:
341             startSpeechInput();
342             break;
343         case Recording:
344             stopSpeechInput();
345             break;
346         case Recognizing:
347             // Nothing to do here, we will continue to wait for results.
348             break;
349         }
350         event->setDefaultHandled();
351     }
352
353     if (!event->defaultHandled())
354         HTMLDivElement::defaultEventHandler(event);
355 }
356
357 #if !PLATFORM(IOS)
358 bool InputFieldSpeechButtonElement::willRespondToMouseClickEvents()
359 {
360     const HTMLInputElement* input = toHTMLInputElement(shadowHost());
361     if (input && !input->isDisabledOrReadOnly())
362         return true;
363
364     return HTMLDivElement::willRespondToMouseClickEvents();
365 }
366 #endif
367
368 void InputFieldSpeechButtonElement::setState(SpeechInputState state)
369 {
370     if (m_state != state) {
371         m_state = state;
372         shadowHost()->renderer()->repaint();
373     }
374 }
375
376 SpeechInput* InputFieldSpeechButtonElement::speechInput()
377 {
378     return SpeechInput::from(document().page());
379 }
380
381 void InputFieldSpeechButtonElement::didCompleteRecording(int)
382 {
383     setState(Recognizing);
384 }
385
386 void InputFieldSpeechButtonElement::didCompleteRecognition(int)
387 {
388     setState(Idle);
389 }
390
391 void InputFieldSpeechButtonElement::setRecognitionResult(int, const SpeechInputResultArray& results)
392 {
393     m_results = results;
394
395     // The call to setValue() below dispatches an event, and an event handler in the page might
396     // remove the input element from DOM. To make sure it remains valid until we finish our work
397     // here, we take a temporary reference.
398     RefPtr<HTMLInputElement> input(toHTMLInputElement(shadowHost()));
399     if (!input || input->isDisabledOrReadOnly())
400         return;
401
402     Ref<InputFieldSpeechButtonElement> protect(*this);
403     if (document().domWindow()) {
404         // Call selectionChanged, causing the element to cache the selection,
405         // so that the text event inserts the text in this element even if
406         // focus has moved away from it.
407         input->selectionChanged(false);
408         input->dispatchEvent(TextEvent::create(document().domWindow(), results.isEmpty() ? "" : results[0]->utterance(), TextEventInputOther));
409     }
410
411     // This event is sent after the text event so the website can perform actions using the input field content immediately.
412     // It provides alternative recognition hypotheses and notifies that the results come from speech input.
413     input->dispatchEvent(SpeechInputEvent::create(eventNames().webkitspeechchangeEvent, results));
414
415     // Check before accessing the renderer as the above event could have potentially turned off
416     // speech in the input element, hence removing this button and renderer from the hierarchy.
417     if (renderer())
418         renderer()->repaint();
419 }
420
421 void InputFieldSpeechButtonElement::willAttachRenderers()
422 {
423     ASSERT(!m_listenerId);
424     if (SpeechInput* input = SpeechInput::from(document().page()))
425         m_listenerId = input->registerListener(this);
426 }
427
428 void InputFieldSpeechButtonElement::willDetachRenderers()
429 {
430     if (m_capturing) {
431         if (Frame* frame = document().frame())
432             frame->eventHandler().setCapturingMouseEventsElement(nullptr);
433     }
434
435     if (m_listenerId) {
436         if (m_state != Idle)
437             speechInput()->cancelRecognition(m_listenerId);
438         speechInput()->unregisterListener(m_listenerId);
439         m_listenerId = 0;
440     }
441 }
442
443 void InputFieldSpeechButtonElement::startSpeechInput()
444 {
445     if (m_state != Idle)
446         return;
447
448     RefPtr<HTMLInputElement> input = toHTMLInputElement(shadowHost());
449     AtomicString language = input->computeInheritedLanguage();
450     String grammar = input->getAttribute(webkitgrammarAttr);
451     IntRect rect = document().view()->contentsToRootView(pixelSnappedBoundingBox());
452     if (speechInput()->startRecognition(m_listenerId, rect, language, grammar, document().securityOrigin()))
453         setState(Recording);
454 }
455
456 void InputFieldSpeechButtonElement::stopSpeechInput()
457 {
458     if (m_state == Recording)
459         speechInput()->stopRecording(m_listenerId);
460 }
461
462 const AtomicString& InputFieldSpeechButtonElement::shadowPseudoId() const
463 {
464     DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-input-speech-button", AtomicString::ConstructFromLiteral));
465     return pseudoId;
466 }
467
468 #endif // ENABLE(INPUT_SPEECH)
469
470 }