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