[CSS] Change Element::isReadOnlyFormControl to shouldMatchReadWriteSelector or HTMLTe...
[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 bool SearchFieldResultsButtonElement::willRespondToMouseClickEvents()
169 {
170     return true;
171 }
172
173 // ----------------------------
174
175 inline SearchFieldCancelButtonElement::SearchFieldCancelButtonElement(Document* document)
176     : HTMLDivElement(divTag, document)
177     , m_capturing(false)
178 {
179 }
180
181 PassRefPtr<SearchFieldCancelButtonElement> SearchFieldCancelButtonElement::create(Document* document)
182 {
183     return adoptRef(new SearchFieldCancelButtonElement(document));
184 }
185
186 const AtomicString& SearchFieldCancelButtonElement::shadowPseudoId() const
187 {
188     DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-search-cancel-button"));
189     return pseudoId;
190 }
191
192 void SearchFieldCancelButtonElement::detach()
193 {
194     if (m_capturing) {
195         if (Frame* frame = document()->frame())
196             frame->eventHandler()->setCapturingMouseEventsNode(0);
197     }
198     HTMLDivElement::detach();
199 }
200
201
202 void SearchFieldCancelButtonElement::defaultEventHandler(Event* event)
203 {
204     // If the element is visible, on mouseup, clear the value, and set selection
205     RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowHost()));
206     if (input->disabled() || input->readOnly()) {
207         if (!event->defaultHandled())
208             HTMLDivElement::defaultEventHandler(event);
209         return;
210     }
211
212     if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
213         if (renderer() && renderer()->visibleToHitTesting()) {
214             if (Frame* frame = document()->frame()) {
215                 frame->eventHandler()->setCapturingMouseEventsNode(this);
216                 m_capturing = true;
217             }
218         }
219         input->focus();
220         input->select();
221         event->setDefaultHandled();
222     }
223     if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
224         if (m_capturing) {
225             if (Frame* frame = document()->frame()) {
226                 frame->eventHandler()->setCapturingMouseEventsNode(0);
227                 m_capturing = false;
228             }
229             if (hovered()) {
230                 String oldValue = input->value();
231                 input->setValueForUser("");
232                 input->onSearch();
233                 event->setDefaultHandled();
234             }
235         }
236     }
237
238     if (!event->defaultHandled())
239         HTMLDivElement::defaultEventHandler(event);
240 }
241
242 bool SearchFieldCancelButtonElement::willRespondToMouseClickEvents()
243 {
244     const HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowHost());
245     if (!input->disabled() && !input->readOnly())
246         return true;
247
248     return HTMLDivElement::willRespondToMouseClickEvents();
249 }
250
251 // ----------------------------
252
253 inline SpinButtonElement::SpinButtonElement(Document* document, StepActionHandler& stepActionHandler)
254     : HTMLDivElement(divTag, document)
255     , m_stepActionHandler(&stepActionHandler)
256     , m_capturing(false)
257     , m_upDownState(Indeterminate)
258     , m_pressStartingState(Indeterminate)
259     , m_repeatingTimer(this, &SpinButtonElement::repeatingTimerFired)
260 {
261 }
262
263 PassRefPtr<SpinButtonElement> SpinButtonElement::create(Document* document, StepActionHandler& stepActionHandler)
264 {
265     return adoptRef(new SpinButtonElement(document, stepActionHandler));
266 }
267
268 const AtomicString& SpinButtonElement::shadowPseudoId() const
269 {
270     DEFINE_STATIC_LOCAL(AtomicString, innerPseudoId, ("-webkit-inner-spin-button"));
271     return innerPseudoId;
272 }
273
274 void SpinButtonElement::detach()
275 {
276     releaseCapture();
277     HTMLDivElement::detach();
278 }
279
280 void SpinButtonElement::defaultEventHandler(Event* event)
281 {
282     if (!event->isMouseEvent()) {
283         if (!event->defaultHandled())
284             HTMLDivElement::defaultEventHandler(event);
285         return;
286     }
287
288     RenderBox* box = renderBox();
289     if (!box) {
290         if (!event->defaultHandled())
291             HTMLDivElement::defaultEventHandler(event);
292         return;
293     }
294
295     RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowHost()));
296     if (input->disabled() || input->readOnly()) {
297         if (!event->defaultHandled())
298             HTMLDivElement::defaultEventHandler(event);
299         return;
300     }
301
302     MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
303     IntPoint local = roundedIntPoint(box->absoluteToLocal(mouseEvent->absoluteLocation(), false, true));
304     if (mouseEvent->type() == eventNames().mousedownEvent && mouseEvent->button() == LeftButton) {
305         if (box->pixelSnappedBorderBoxRect().contains(local)) {
306             // The following functions of HTMLInputElement may run JavaScript
307             // code which detaches this shadow node. We need to take a reference
308             // and check renderer() after such function calls.
309             RefPtr<Node> protector(this);
310             input->focus();
311             input->select();
312             if (renderer()) {
313                 if (m_upDownState != Indeterminate) {
314                     doStepAction(m_upDownState == Up ? 1 : -1);
315                     if (renderer())
316                         startRepeatingTimer();
317                 }
318             }
319             event->setDefaultHandled();
320         }
321     } else if (mouseEvent->type() == eventNames().mouseupEvent && mouseEvent->button() == LeftButton)
322         stopRepeatingTimer();
323     else if (event->type() == eventNames().mousemoveEvent) {
324         if (box->pixelSnappedBorderBoxRect().contains(local)) {
325             if (!m_capturing) {
326                 if (Frame* frame = document()->frame()) {
327                     frame->eventHandler()->setCapturingMouseEventsNode(this);
328                     m_capturing = true;
329                 }
330             }
331             UpDownState oldUpDownState = m_upDownState;
332             m_upDownState = local.y() < box->height() / 2 ? Up : Down;
333             if (m_upDownState != oldUpDownState)
334                 renderer()->repaint();
335         } else {
336             releaseCapture();
337             m_upDownState = Indeterminate;
338         }
339     }
340
341     if (!event->defaultHandled())
342         HTMLDivElement::defaultEventHandler(event);
343 }
344
345 bool SpinButtonElement::willRespondToMouseMoveEvents()
346 {
347     const HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowHost());
348     if (renderBox() && !input->disabled() && !input->readOnly())
349         return true;
350
351     return HTMLDivElement::willRespondToMouseMoveEvents();
352 }
353
354 bool SpinButtonElement::willRespondToMouseClickEvents()
355 {
356     const HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowHost());
357     if (renderBox() && !input->disabled() && !input->readOnly())
358         return true;
359
360     return HTMLDivElement::willRespondToMouseClickEvents();
361 }
362
363 void SpinButtonElement::doStepAction(int amount)
364 {
365     if (!m_stepActionHandler)
366         return;
367
368     if (amount > 0)
369         m_stepActionHandler->spinButtonStepUp();
370     else if (amount < 0)
371         m_stepActionHandler->spinButtonStepDown();
372 }
373
374 void SpinButtonElement::releaseCapture()
375 {
376     stopRepeatingTimer();
377     if (m_capturing) {
378         if (Frame* frame = document()->frame()) {
379             frame->eventHandler()->setCapturingMouseEventsNode(0);
380             m_capturing = false;
381         }
382     }
383 }
384
385 void SpinButtonElement::startRepeatingTimer()
386 {
387     m_pressStartingState = m_upDownState;
388     ScrollbarTheme* theme = ScrollbarTheme::theme();
389     m_repeatingTimer.start(theme->initialAutoscrollTimerDelay(), theme->autoscrollTimerDelay());
390 }
391
392 void SpinButtonElement::stopRepeatingTimer()
393 {
394     m_repeatingTimer.stop();
395 }
396
397 void SpinButtonElement::step(int amount)
398 {
399     HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowHost());
400     if (input->disabled() || input->readOnly())
401         return;
402     // On Mac OS, NSStepper updates the value for the button under the mouse
403     // cursor regardless of the button pressed at the beginning. So the
404     // following check is not needed for Mac OS.
405 #if !OS(MAC_OS_X)
406     if (m_upDownState != m_pressStartingState)
407         return;
408 #endif
409     doStepAction(amount);
410 }
411     
412 void SpinButtonElement::repeatingTimerFired(Timer<SpinButtonElement>*)
413 {
414     if (m_upDownState != Indeterminate)
415         step(m_upDownState == Up ? 1 : -1);
416 }
417
418 void SpinButtonElement::setHovered(bool flag)
419 {
420     if (!flag)
421         m_upDownState = Indeterminate;
422     HTMLDivElement::setHovered(flag);
423 }
424
425
426 // ----------------------------
427
428 #if ENABLE(INPUT_SPEECH)
429
430 inline InputFieldSpeechButtonElement::InputFieldSpeechButtonElement(Document* document)
431     : HTMLDivElement(divTag, document)
432     , m_capturing(false)
433     , m_state(Idle)
434     , m_listenerId(0)
435 {
436 }
437
438 InputFieldSpeechButtonElement::~InputFieldSpeechButtonElement()
439 {
440     SpeechInput* speech = speechInput();
441     if (speech && m_listenerId)  { // Could be null when page is unloading.
442         if (m_state != Idle)
443             speech->cancelRecognition(m_listenerId);
444         speech->unregisterListener(m_listenerId);
445     }
446 }
447
448 PassRefPtr<InputFieldSpeechButtonElement> InputFieldSpeechButtonElement::create(Document* document)
449 {
450     return adoptRef(new InputFieldSpeechButtonElement(document));
451 }
452
453 void InputFieldSpeechButtonElement::defaultEventHandler(Event* event)
454 {
455     // For privacy reasons, only allow clicks directly coming from the user.
456     if (!ScriptController::processingUserGesture()) {
457         HTMLDivElement::defaultEventHandler(event);
458         return;
459     }
460
461     // The call to focus() below dispatches a focus event, and an event handler in the page might
462     // remove the input element from DOM. To make sure it remains valid until we finish our work
463     // here, we take a temporary reference.
464     RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowHost()));
465
466     if (input->disabled() || input->readOnly()) {
467         if (!event->defaultHandled())
468             HTMLDivElement::defaultEventHandler(event);
469         return;
470     }
471
472     // On mouse down, select the text and set focus.
473     if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
474         if (renderer() && renderer()->visibleToHitTesting()) {
475             if (Frame* frame = document()->frame()) {
476                 frame->eventHandler()->setCapturingMouseEventsNode(this);
477                 m_capturing = true;
478             }
479         }
480         RefPtr<InputFieldSpeechButtonElement> holdRefButton(this);
481         input->focus();
482         input->select();
483         event->setDefaultHandled();
484     }
485     // On mouse up, release capture cleanly.
486     if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
487         if (m_capturing && renderer() && renderer()->visibleToHitTesting()) {
488             if (Frame* frame = document()->frame()) {
489                 frame->eventHandler()->setCapturingMouseEventsNode(0);
490                 m_capturing = false;
491             }
492         }
493     }
494
495     if (event->type() == eventNames().clickEvent && m_listenerId) {
496         switch (m_state) {
497         case Idle:
498             startSpeechInput();
499             break;
500         case Recording:
501             stopSpeechInput();
502             break;
503         case Recognizing:
504             // Nothing to do here, we will continue to wait for results.
505             break;
506         }
507         event->setDefaultHandled();
508     }
509
510     if (!event->defaultHandled())
511         HTMLDivElement::defaultEventHandler(event);
512 }
513
514 bool InputFieldSpeechButtonElement::willRespondToMouseClickEvents()
515 {
516     const HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowHost());
517     if (!input->disabled() && !input->readOnly())
518         return true;
519
520     return HTMLDivElement::willRespondToMouseClickEvents();
521 }
522
523 void InputFieldSpeechButtonElement::setState(SpeechInputState state)
524 {
525     if (m_state != state) {
526         m_state = state;
527         shadowHost()->renderer()->repaint();
528     }
529 }
530
531 SpeechInput* InputFieldSpeechButtonElement::speechInput()
532 {
533     return SpeechInput::from(document()->page());
534 }
535
536 void InputFieldSpeechButtonElement::didCompleteRecording(int)
537 {
538     setState(Recognizing);
539 }
540
541 void InputFieldSpeechButtonElement::didCompleteRecognition(int)
542 {
543     setState(Idle);
544 }
545
546 void InputFieldSpeechButtonElement::setRecognitionResult(int, const SpeechInputResultArray& results)
547 {
548     m_results = results;
549
550     // The call to setValue() below dispatches an event, and an event handler in the page might
551     // remove the input element from DOM. To make sure it remains valid until we finish our work
552     // here, we take a temporary reference.
553     RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowHost()));
554     if (input->disabled() || input->readOnly())
555         return;
556
557     RefPtr<InputFieldSpeechButtonElement> holdRefButton(this);
558     if (document() && document()->domWindow()) {
559         // Call selectionChanged, causing the element to cache the selection,
560         // so that the text event inserts the text in this element even if
561         // focus has moved away from it.
562         input->selectionChanged(false);
563         input->dispatchEvent(TextEvent::create(document()->domWindow(), results.isEmpty() ? "" : results[0]->utterance(), TextEventInputOther));
564     }
565
566     // This event is sent after the text event so the website can perform actions using the input field content immediately.
567     // It provides alternative recognition hypotheses and notifies that the results come from speech input.
568     input->dispatchEvent(SpeechInputEvent::create(eventNames().webkitspeechchangeEvent, results));
569
570     // Check before accessing the renderer as the above event could have potentially turned off
571     // speech in the input element, hence removing this button and renderer from the hierarchy.
572     if (renderer())
573         renderer()->repaint();
574 }
575
576 void InputFieldSpeechButtonElement::attach()
577 {
578     ASSERT(!m_listenerId);
579     if (SpeechInput* input = SpeechInput::from(document()->page()))
580         m_listenerId = input->registerListener(this);
581     HTMLDivElement::attach();
582 }
583
584 void InputFieldSpeechButtonElement::detach()
585 {
586     if (m_capturing) {
587         if (Frame* frame = document()->frame())
588             frame->eventHandler()->setCapturingMouseEventsNode(0);
589     }
590
591     if (m_listenerId) {
592         if (m_state != Idle)
593             speechInput()->cancelRecognition(m_listenerId);
594         speechInput()->unregisterListener(m_listenerId);
595         m_listenerId = 0;
596     }
597
598     HTMLDivElement::detach();
599 }
600
601 void InputFieldSpeechButtonElement::startSpeechInput()
602 {
603     if (m_state != Idle)
604         return;
605
606     RefPtr<HTMLInputElement> input = static_cast<HTMLInputElement*>(shadowHost());
607     AtomicString language = input->computeInheritedLanguage();
608     String grammar = input->getAttribute(webkitgrammarAttr);
609     // FIXME: this should probably respect transforms
610     IntRect rect = pixelSnappedIntRect(renderer()->view()->frameView()->contentsToWindow(renderer()->absoluteBoundingBoxRectIgnoringTransforms()));
611     if (speechInput()->startRecognition(m_listenerId, rect, language, grammar, document()->securityOrigin()))
612         setState(Recording);
613 }
614
615 void InputFieldSpeechButtonElement::stopSpeechInput()
616 {
617     if (m_state == Recording)
618         speechInput()->stopRecording(m_listenerId);
619 }
620
621 const AtomicString& InputFieldSpeechButtonElement::shadowPseudoId() const
622 {
623     DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-input-speech-button"));
624     return pseudoId;
625 }
626
627 #endif // ENABLE(INPUT_SPEECH)
628
629 }