INPUT_SPEECH should be implemented as a PageSupplement.
[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 "RenderLayer.h"
41 #include "RenderTextControlSingleLine.h"
42 #include "RenderView.h"
43 #include "ScriptController.h"
44 #include "ScrollbarTheme.h"
45 #include "SpeechInput.h"
46 #include "SpeechInputEvent.h"
47 #include "TextEvent.h"
48 #include "TextEventInputType.h"
49
50 namespace WebCore {
51
52 using namespace HTMLNames;
53
54 TextControlInnerElement::TextControlInnerElement(Document* document)
55     : HTMLDivElement(divTag, document)
56 {
57     setHasCustomStyleForRenderer();
58 }
59
60 PassRefPtr<TextControlInnerElement> TextControlInnerElement::create(Document* document)
61 {
62     return adoptRef(new TextControlInnerElement(document));
63 }
64
65 PassRefPtr<RenderStyle> TextControlInnerElement::customStyleForRenderer()
66 {
67     RenderTextControlSingleLine* parentRenderer = toRenderTextControlSingleLine(shadowAncestorNode()->renderer());
68     return parentRenderer->createInnerBlockStyle(parentRenderer->style());
69 }
70
71 // ----------------------------
72
73 inline TextControlInnerTextElement::TextControlInnerTextElement(Document* document)
74     : HTMLDivElement(divTag, document)
75 {
76     setHasCustomStyleForRenderer();
77 }
78
79 PassRefPtr<TextControlInnerTextElement> TextControlInnerTextElement::create(Document* document)
80 {
81     return adoptRef(new TextControlInnerTextElement(document));
82 }
83
84 void TextControlInnerTextElement::defaultEventHandler(Event* event)
85 {
86     // FIXME: In the future, we should add a way to have default event listeners.
87     // Then we would add one to the text field's inner div, and we wouldn't need this subclass.
88     // Or possibly we could just use a normal event listener.
89     if (event->isBeforeTextInsertedEvent() || event->type() == eventNames().webkitEditableContentChangedEvent) {
90         Node* shadowAncestor = shadowAncestorNode();
91         // A TextControlInnerTextElement can be its own shadow ancestor if its been detached, but kept alive by an EditCommand.
92         // In this case, an undo/redo can cause events to be sent to the TextControlInnerTextElement.  
93         // To prevent an infinite loop, we must check for this case before sending the event up the chain.
94         if (shadowAncestor && shadowAncestor != this)
95             shadowAncestor->defaultEventHandler(event);
96     }
97     if (!event->defaultHandled())
98         HTMLDivElement::defaultEventHandler(event);
99 }
100
101 RenderObject* TextControlInnerTextElement::createRenderer(RenderArena* arena, RenderStyle*)
102 {
103     bool multiLine = false;
104     Node* shadowAncestor = shadowAncestorNode();
105     if (shadowAncestor && shadowAncestor->renderer()) {
106         ASSERT(shadowAncestor->renderer()->isTextField() || shadowAncestor->renderer()->isTextArea());
107         multiLine = shadowAncestor->renderer()->isTextArea();
108     }
109     return new (arena) RenderTextControlInnerBlock(this, multiLine);
110 }
111
112 PassRefPtr<RenderStyle> TextControlInnerTextElement::customStyleForRenderer()
113 {
114     RenderTextControl* parentRenderer = toRenderTextControl(shadowAncestorNode()->renderer());
115     return parentRenderer->createInnerTextStyle(parentRenderer->style());
116 }
117
118 // ----------------------------
119
120 inline SearchFieldResultsButtonElement::SearchFieldResultsButtonElement(Document* document)
121     : HTMLDivElement(divTag, document)
122 {
123 }
124
125 PassRefPtr<SearchFieldResultsButtonElement> SearchFieldResultsButtonElement::create(Document* document)
126 {
127     return adoptRef(new SearchFieldResultsButtonElement(document));
128 }
129
130 const AtomicString& SearchFieldResultsButtonElement::shadowPseudoId() const
131 {
132     DEFINE_STATIC_LOCAL(AtomicString, resultsId, ("-webkit-search-results-button"));
133     DEFINE_STATIC_LOCAL(AtomicString, resultsDecorationId, ("-webkit-search-results-decoration"));
134     DEFINE_STATIC_LOCAL(AtomicString, decorationId, ("-webkit-search-decoration"));
135     Node* host = shadowAncestorNode();
136     if (!host)
137         return resultsId;
138     if (HTMLInputElement* input = host->toInputElement()) {
139         if (input->maxResults() < 0)
140             return decorationId;
141         if (input->maxResults() > 0)
142             return resultsId;
143         return resultsDecorationId;
144     }
145     return resultsId;
146 }
147
148 void SearchFieldResultsButtonElement::defaultEventHandler(Event* event)
149 {
150     // On mousedown, bring up a menu, if needed
151     HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowAncestorNode());
152     if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
153         input->focus();
154         input->select();
155         RenderTextControlSingleLine* renderer = toRenderTextControlSingleLine(input->renderer());
156         if (renderer->popupIsVisible())
157             renderer->hidePopup();
158         else if (input->maxResults() > 0)
159             renderer->showPopup();
160         event->setDefaultHandled();
161     }
162
163     if (!event->defaultHandled())
164         HTMLDivElement::defaultEventHandler(event);
165 }
166
167 // ----------------------------
168
169 inline SearchFieldCancelButtonElement::SearchFieldCancelButtonElement(Document* document)
170     : HTMLDivElement(divTag, document)
171     , m_capturing(false)
172 {
173 }
174
175 PassRefPtr<SearchFieldCancelButtonElement> SearchFieldCancelButtonElement::create(Document* document)
176 {
177     return adoptRef(new SearchFieldCancelButtonElement(document));
178 }
179
180 const AtomicString& SearchFieldCancelButtonElement::shadowPseudoId() const
181 {
182     DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-search-cancel-button"));
183     return pseudoId;
184 }
185
186 void SearchFieldCancelButtonElement::detach()
187 {
188     if (m_capturing) {
189         if (Frame* frame = document()->frame())
190             frame->eventHandler()->setCapturingMouseEventsNode(0);
191     }
192     HTMLDivElement::detach();
193 }
194
195
196 void SearchFieldCancelButtonElement::defaultEventHandler(Event* event)
197 {
198     // If the element is visible, on mouseup, clear the value, and set selection
199     RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowAncestorNode()));
200     if (input->disabled() || input->isReadOnlyFormControl()) {
201         if (!event->defaultHandled())
202             HTMLDivElement::defaultEventHandler(event);
203         return;
204     }
205
206     if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
207         if (renderer() && renderer()->visibleToHitTesting()) {
208             if (Frame* frame = document()->frame()) {
209                 frame->eventHandler()->setCapturingMouseEventsNode(this);
210                 m_capturing = true;
211             }
212         }
213         input->focus();
214         input->select();
215         event->setDefaultHandled();
216     }
217     if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
218         if (m_capturing) {
219             if (Frame* frame = document()->frame()) {
220                 frame->eventHandler()->setCapturingMouseEventsNode(0);
221                 m_capturing = false;
222             }
223             if (hovered()) {
224                 String oldValue = input->value();
225                 input->setValueForUser("");
226                 input->onSearch();
227                 event->setDefaultHandled();
228             }
229         }
230     }
231
232     if (!event->defaultHandled())
233         HTMLDivElement::defaultEventHandler(event);
234 }
235
236 // ----------------------------
237
238 inline SpinButtonElement::SpinButtonElement(Document* document)
239     : HTMLDivElement(divTag, document)
240     , m_capturing(false)
241     , m_upDownState(Indeterminate)
242     , m_pressStartingState(Indeterminate)
243     , m_repeatingTimer(this, &SpinButtonElement::repeatingTimerFired)
244 {
245 }
246
247 PassRefPtr<SpinButtonElement> SpinButtonElement::create(Document* document)
248 {
249     return adoptRef(new SpinButtonElement(document));
250 }
251
252 const AtomicString& SpinButtonElement::shadowPseudoId() const
253 {
254     DEFINE_STATIC_LOCAL(AtomicString, innerPseudoId, ("-webkit-inner-spin-button"));
255     return innerPseudoId;
256 }
257
258 void SpinButtonElement::detach()
259 {
260     releaseCapture();
261     HTMLDivElement::detach();
262 }
263
264 void SpinButtonElement::defaultEventHandler(Event* event)
265 {
266     if (!event->isMouseEvent()) {
267         if (!event->defaultHandled())
268             HTMLDivElement::defaultEventHandler(event);
269         return;
270     }
271
272     RenderBox* box = renderBox();
273     if (!box) {
274         if (!event->defaultHandled())
275             HTMLDivElement::defaultEventHandler(event);
276         return;
277     }
278
279     RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowAncestorNode()));
280     if (input->disabled() || input->isReadOnlyFormControl()) {
281         if (!event->defaultHandled())
282             HTMLDivElement::defaultEventHandler(event);
283         return;
284     }
285
286     MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
287     IntPoint local = roundedIntPoint(box->absoluteToLocal(mouseEvent->absoluteLocation(), false, true));
288     if (mouseEvent->type() == eventNames().mousedownEvent && mouseEvent->button() == LeftButton) {
289         if (box->borderBoxRect().contains(local)) {
290             // The following functions of HTMLInputElement may run JavaScript
291             // code which detaches this shadow node. We need to take a reference
292             // and check renderer() after such function calls.
293             RefPtr<Node> protector(this);
294             input->focus();
295             input->select();
296             if (renderer()) {
297                 ASSERT(m_upDownState != Indeterminate);
298                 input->stepUpFromRenderer(m_upDownState == Up ? 1 : -1);
299                 if (renderer())
300                     startRepeatingTimer();
301             }
302             event->setDefaultHandled();
303         }
304     } else if (mouseEvent->type() == eventNames().mouseupEvent && mouseEvent->button() == LeftButton)
305         stopRepeatingTimer();
306     else if (event->type() == eventNames().mousemoveEvent) {
307         if (box->borderBoxRect().contains(local)) {
308             if (!m_capturing) {
309                 if (Frame* frame = document()->frame()) {
310                     frame->eventHandler()->setCapturingMouseEventsNode(this);
311                     m_capturing = true;
312                 }
313             }
314             UpDownState oldUpDownState = m_upDownState;
315             m_upDownState = local.y() < box->height() / 2 ? Up : Down;
316             if (m_upDownState != oldUpDownState)
317                 renderer()->repaint();
318         } else {
319             releaseCapture();
320             m_upDownState = Indeterminate;
321         }
322     }
323
324     if (!event->defaultHandled())
325         HTMLDivElement::defaultEventHandler(event);
326 }
327
328 void SpinButtonElement::releaseCapture()
329 {
330     stopRepeatingTimer();
331     if (m_capturing) {
332         if (Frame* frame = document()->frame()) {
333             frame->eventHandler()->setCapturingMouseEventsNode(0);
334             m_capturing = false;
335         }
336     }
337 }
338
339 void SpinButtonElement::startRepeatingTimer()
340 {
341     m_pressStartingState = m_upDownState;
342     ScrollbarTheme* theme = ScrollbarTheme::theme();
343     m_repeatingTimer.start(theme->initialAutoscrollTimerDelay(), theme->autoscrollTimerDelay());
344 }
345
346 void SpinButtonElement::stopRepeatingTimer()
347 {
348     m_repeatingTimer.stop();
349 }
350
351 void SpinButtonElement::step(int amount)
352 {
353     HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowAncestorNode());
354     if (input->disabled() || input->isReadOnlyFormControl())
355         return;
356     // On Mac OS, NSStepper updates the value for the button under the mouse
357     // cursor regardless of the button pressed at the beginning. So the
358     // following check is not needed for Mac OS.
359 #if !OS(MAC_OS_X)
360     if (m_upDownState != m_pressStartingState)
361         return;
362 #endif
363     input->stepUpFromRenderer(amount);
364 }
365     
366 void SpinButtonElement::repeatingTimerFired(Timer<SpinButtonElement>*)
367 {
368     step(m_upDownState == Up ? 1 : -1);
369 }
370
371 void SpinButtonElement::setHovered(bool flag)
372 {
373     if (!flag)
374         m_upDownState = Indeterminate;
375     HTMLDivElement::setHovered(flag);
376 }
377
378
379 // ----------------------------
380
381 #if ENABLE(INPUT_SPEECH)
382
383 inline InputFieldSpeechButtonElement::InputFieldSpeechButtonElement(Document* document)
384     : HTMLDivElement(divTag, document)
385     , m_capturing(false)
386     , m_state(Idle)
387     , m_listenerId(0)
388 {
389 }
390
391 InputFieldSpeechButtonElement::~InputFieldSpeechButtonElement()
392 {
393     SpeechInput* speech = speechInput();
394     if (speech && m_listenerId)  { // Could be null when page is unloading.
395         if (m_state != Idle)
396             speech->cancelRecognition(m_listenerId);
397         speech->unregisterListener(m_listenerId);
398     }
399 }
400
401 PassRefPtr<InputFieldSpeechButtonElement> InputFieldSpeechButtonElement::create(Document* document)
402 {
403     return adoptRef(new InputFieldSpeechButtonElement(document));
404 }
405
406 void InputFieldSpeechButtonElement::defaultEventHandler(Event* event)
407 {
408     // For privacy reasons, only allow clicks directly coming from the user.
409     if (!ScriptController::processingUserGesture()) {
410         HTMLDivElement::defaultEventHandler(event);
411         return;
412     }
413
414     // The call to focus() below dispatches a focus event, and an event handler in the page might
415     // remove the input element from DOM. To make sure it remains valid until we finish our work
416     // here, we take a temporary reference.
417     RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowAncestorNode()));
418
419     if (input->disabled() || input->isReadOnlyFormControl()) {
420         if (!event->defaultHandled())
421             HTMLDivElement::defaultEventHandler(event);
422         return;
423     }
424
425     // On mouse down, select the text and set focus.
426     if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
427         if (renderer() && renderer()->visibleToHitTesting()) {
428             if (Frame* frame = document()->frame()) {
429                 frame->eventHandler()->setCapturingMouseEventsNode(this);
430                 m_capturing = true;
431             }
432         }
433         RefPtr<InputFieldSpeechButtonElement> holdRefButton(this);
434         input->focus();
435         input->select();
436         event->setDefaultHandled();
437     }
438     // On mouse up, release capture cleanly.
439     if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
440         if (m_capturing && renderer() && renderer()->visibleToHitTesting()) {
441             if (Frame* frame = document()->frame()) {
442                 frame->eventHandler()->setCapturingMouseEventsNode(0);
443                 m_capturing = false;
444             }
445         }
446     }
447
448     if (event->type() == eventNames().clickEvent && m_listenerId) {
449         switch (m_state) {
450         case Idle:
451             startSpeechInput();
452             break;
453         case Recording:
454             stopSpeechInput();
455             break;
456         case Recognizing:
457             // Nothing to do here, we will continue to wait for results.
458             break;
459         }
460         event->setDefaultHandled();
461     }
462
463     if (!event->defaultHandled())
464         HTMLDivElement::defaultEventHandler(event);
465 }
466
467 void InputFieldSpeechButtonElement::setState(SpeechInputState state)
468 {
469     if (m_state != state) {
470         m_state = state;
471         shadowAncestorNode()->renderer()->repaint();
472     }
473 }
474
475 SpeechInput* InputFieldSpeechButtonElement::speechInput()
476 {
477     return SpeechInput::from(document()->page());
478 }
479
480 void InputFieldSpeechButtonElement::didCompleteRecording(int)
481 {
482     setState(Recognizing);
483 }
484
485 void InputFieldSpeechButtonElement::didCompleteRecognition(int)
486 {
487     setState(Idle);
488 }
489
490 void InputFieldSpeechButtonElement::setRecognitionResult(int, const SpeechInputResultArray& results)
491 {
492     m_results = results;
493
494     // The call to setValue() below dispatches an event, and an event handler in the page might
495     // remove the input element from DOM. To make sure it remains valid until we finish our work
496     // here, we take a temporary reference.
497     RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowAncestorNode()));
498     if (input->disabled() || input->isReadOnlyFormControl())
499         return;
500
501     RefPtr<InputFieldSpeechButtonElement> holdRefButton(this);
502     if (document() && document()->domWindow()) {
503         // Call selectionChanged, causing the element to cache the selection,
504         // so that the text event inserts the text in this element even if
505         // focus has moved away from it.
506         input->selectionChanged(false);
507         input->dispatchEvent(TextEvent::create(document()->domWindow(), results.isEmpty() ? "" : results[0]->utterance(), TextEventInputOther));
508     }
509
510     // This event is sent after the text event so the website can perform actions using the input field content immediately.
511     // It provides alternative recognition hypotheses and notifies that the results come from speech input.
512     input->dispatchEvent(SpeechInputEvent::create(eventNames().webkitspeechchangeEvent, results));
513
514     // Check before accessing the renderer as the above event could have potentially turned off
515     // speech in the input element, hence removing this button and renderer from the hierarchy.
516     if (renderer())
517         renderer()->repaint();
518 }
519
520 void InputFieldSpeechButtonElement::attach()
521 {
522     ASSERT(!m_listenerId);
523     if (SpeechInput* input = SpeechInput::from(document()->page()))
524         m_listenerId = input->registerListener(this);
525     HTMLDivElement::attach();
526 }
527
528 void InputFieldSpeechButtonElement::detach()
529 {
530     if (m_capturing) {
531         if (Frame* frame = document()->frame())
532             frame->eventHandler()->setCapturingMouseEventsNode(0);
533     }
534
535     if (m_listenerId) {
536         if (m_state != Idle)
537             speechInput()->cancelRecognition(m_listenerId);
538         speechInput()->unregisterListener(m_listenerId);
539         m_listenerId = 0;
540     }
541
542     HTMLDivElement::detach();
543 }
544
545 void InputFieldSpeechButtonElement::startSpeechInput()
546 {
547     if (m_state != Idle)
548         return;
549
550     RefPtr<HTMLInputElement> input = static_cast<HTMLInputElement*>(shadowAncestorNode());
551     AtomicString language = input->computeInheritedLanguage();
552     String grammar = input->getAttribute(webkitgrammarAttr);
553     // FIXME: this should probably respect transforms
554     IntRect rect = pixelSnappedIntRect(renderer()->view()->frameView()->contentsToWindow(renderer()->absoluteBoundingBoxRectIgnoringTransforms()));
555     if (speechInput()->startRecognition(m_listenerId, rect, language, grammar, document()->securityOrigin()))
556         setState(Recording);
557 }
558
559 void InputFieldSpeechButtonElement::stopSpeechInput()
560 {
561     if (m_state == Recording)
562         speechInput()->stopRecording(m_listenerId);
563 }
564
565 const AtomicString& InputFieldSpeechButtonElement::shadowPseudoId() const
566 {
567     DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-input-speech-button"));
568     return pseudoId;
569 }
570
571 #endif // ENABLE(INPUT_SPEECH)
572
573 }