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