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