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