Rename HTMLFormControlElement::readOnly to isReadOnly
[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 "RenderTextControl.h"
42 #include "RenderView.h"
43 #include "ScriptController.h"
44 #include "SpeechInput.h"
45 #include "SpeechInputEvent.h"
46 #include "StyleInheritedData.h"
47 #include "TextEvent.h"
48 #include "TextEventInputType.h"
49
50 namespace WebCore {
51
52 using namespace HTMLNames;
53
54 TextControlInnerContainer::TextControlInnerContainer(Document* document)
55     : HTMLDivElement(divTag, document)
56 {
57 }
58
59 PassRefPtr<TextControlInnerContainer> TextControlInnerContainer::create(Document* document)
60 {
61     return adoptRef(new TextControlInnerContainer(document));
62 }
63     
64 RenderObject* TextControlInnerContainer::createRenderer(RenderArena* arena, RenderStyle*)
65 {
66     return new (arena) RenderTextControlInnerContainer(this);
67 }
68
69 TextControlInnerElement::TextControlInnerElement(Document* document)
70     : HTMLDivElement(divTag, document)
71 {
72     setHasCustomStyleCallbacks();
73 }
74
75 PassRefPtr<TextControlInnerElement> TextControlInnerElement::create(Document* document)
76 {
77     return adoptRef(new TextControlInnerElement(document));
78 }
79
80 PassRefPtr<RenderStyle> TextControlInnerElement::customStyleForRenderer()
81 {
82     RenderTextControlSingleLine* parentRenderer = toRenderTextControlSingleLine(shadowHost()->renderer());
83     return parentRenderer->createInnerBlockStyle(parentRenderer->style());
84 }
85
86 // ---------------------------
87
88 inline TextControlInnerTextElement::TextControlInnerTextElement(Document* document)
89     : HTMLDivElement(divTag, document)
90 {
91     setHasCustomStyleCallbacks();
92 }
93
94 PassRefPtr<TextControlInnerTextElement> TextControlInnerTextElement::create(Document* document)
95 {
96     return adoptRef(new TextControlInnerTextElement(document));
97 }
98
99 void TextControlInnerTextElement::defaultEventHandler(Event* event)
100 {
101     // FIXME: In the future, we should add a way to have default event listeners.
102     // Then we would add one to the text field's inner div, and we wouldn't need this subclass.
103     // Or possibly we could just use a normal event listener.
104     if (event->isBeforeTextInsertedEvent() || event->type() == eventNames().webkitEditableContentChangedEvent) {
105         Element* shadowAncestor = shadowHost();
106         // A TextControlInnerTextElement can have no host if its been detached,
107         // but kept alive by an EditCommand. In this case, an undo/redo can
108         // cause events to be sent to the TextControlInnerTextElement. To
109         // prevent an infinite loop, we must check for this case before sending
110         // the event up the chain.
111         if (shadowAncestor)
112             shadowAncestor->defaultEventHandler(event);
113     }
114     if (!event->defaultHandled())
115         HTMLDivElement::defaultEventHandler(event);
116 }
117
118 RenderObject* TextControlInnerTextElement::createRenderer(RenderArena* arena, RenderStyle*)
119 {
120     return new (arena) RenderTextControlInnerBlock(this);
121 }
122
123 PassRefPtr<RenderStyle> TextControlInnerTextElement::customStyleForRenderer()
124 {
125     RenderTextControl* parentRenderer = toRenderTextControl(shadowHost()->renderer());
126     return parentRenderer->createInnerTextStyle(parentRenderer->style());
127 }
128
129 // ----------------------------
130
131 inline SearchFieldResultsButtonElement::SearchFieldResultsButtonElement(Document* document)
132     : HTMLDivElement(divTag, document)
133 {
134 }
135
136 PassRefPtr<SearchFieldResultsButtonElement> SearchFieldResultsButtonElement::create(Document* document)
137 {
138     return adoptRef(new SearchFieldResultsButtonElement(document));
139 }
140
141 const AtomicString& SearchFieldResultsButtonElement::shadowPseudoId() const
142 {
143     DEFINE_STATIC_LOCAL(AtomicString, resultsId, ("-webkit-search-results-button", AtomicString::ConstructFromLiteral));
144     DEFINE_STATIC_LOCAL(AtomicString, resultsDecorationId, ("-webkit-search-results-decoration", AtomicString::ConstructFromLiteral));
145     DEFINE_STATIC_LOCAL(AtomicString, decorationId, ("-webkit-search-decoration", AtomicString::ConstructFromLiteral));
146     Element* host = shadowHost();
147     if (!host)
148         return resultsId;
149     if (HTMLInputElement* input = host->toInputElement()) {
150         if (input->maxResults() < 0)
151             return decorationId;
152         if (input->maxResults() > 0)
153             return resultsId;
154         return resultsDecorationId;
155     }
156     return resultsId;
157 }
158
159 void SearchFieldResultsButtonElement::defaultEventHandler(Event* event)
160 {
161     // On mousedown, bring up a menu, if needed
162     HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowHost());
163     if (input && event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
164         input->focus();
165         input->select();
166         RenderSearchField* renderer = toRenderSearchField(input->renderer());
167         if (renderer->popupIsVisible())
168             renderer->hidePopup();
169         else if (input->maxResults() > 0)
170             renderer->showPopup();
171         event->setDefaultHandled();
172     }
173
174     if (!event->defaultHandled())
175         HTMLDivElement::defaultEventHandler(event);
176 }
177
178 bool SearchFieldResultsButtonElement::willRespondToMouseClickEvents()
179 {
180     return true;
181 }
182
183 // ----------------------------
184
185 inline SearchFieldCancelButtonElement::SearchFieldCancelButtonElement(Document* document)
186     : HTMLDivElement(divTag, document)
187     , m_capturing(false)
188 {
189 }
190
191 PassRefPtr<SearchFieldCancelButtonElement> SearchFieldCancelButtonElement::create(Document* document)
192 {
193     return adoptRef(new SearchFieldCancelButtonElement(document));
194 }
195
196 const AtomicString& SearchFieldCancelButtonElement::shadowPseudoId() const
197 {
198     DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-search-cancel-button", AtomicString::ConstructFromLiteral));
199     return pseudoId;
200 }
201
202 void SearchFieldCancelButtonElement::detach()
203 {
204     if (m_capturing) {
205         if (Frame* frame = document()->frame())
206             frame->eventHandler()->setCapturingMouseEventsNode(0);
207     }
208     HTMLDivElement::detach();
209 }
210
211
212 void SearchFieldCancelButtonElement::defaultEventHandler(Event* event)
213 {
214     // If the element is visible, on mouseup, clear the value, and set selection
215     RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowHost()));
216     if (!input || input->isDisabledOrReadOnly()) {
217         if (!event->defaultHandled())
218             HTMLDivElement::defaultEventHandler(event);
219         return;
220     }
221
222     if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
223         if (renderer() && renderer()->visibleToHitTesting()) {
224             if (Frame* frame = document()->frame()) {
225                 frame->eventHandler()->setCapturingMouseEventsNode(this);
226                 m_capturing = true;
227             }
228         }
229         input->focus();
230         input->select();
231         event->setDefaultHandled();
232     }
233     if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
234         if (m_capturing) {
235             if (Frame* frame = document()->frame()) {
236                 frame->eventHandler()->setCapturingMouseEventsNode(0);
237                 m_capturing = false;
238             }
239             if (hovered()) {
240                 String oldValue = input->value();
241                 input->setValueForUser("");
242                 input->onSearch();
243                 event->setDefaultHandled();
244             }
245         }
246     }
247
248     if (!event->defaultHandled())
249         HTMLDivElement::defaultEventHandler(event);
250 }
251
252 bool SearchFieldCancelButtonElement::willRespondToMouseClickEvents()
253 {
254     const HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowHost());
255     if (input && !input->isDisabledOrReadOnly())
256         return true;
257
258     return HTMLDivElement::willRespondToMouseClickEvents();
259 }
260
261 // ----------------------------
262
263 #if ENABLE(INPUT_SPEECH)
264
265 inline InputFieldSpeechButtonElement::InputFieldSpeechButtonElement(Document* document)
266     : HTMLDivElement(divTag, document)
267     , m_capturing(false)
268     , m_state(Idle)
269     , m_listenerId(0)
270 {
271 }
272
273 InputFieldSpeechButtonElement::~InputFieldSpeechButtonElement()
274 {
275     SpeechInput* speech = speechInput();
276     if (speech && m_listenerId)  { // Could be null when page is unloading.
277         if (m_state != Idle)
278             speech->cancelRecognition(m_listenerId);
279         speech->unregisterListener(m_listenerId);
280     }
281 }
282
283 PassRefPtr<InputFieldSpeechButtonElement> InputFieldSpeechButtonElement::create(Document* document)
284 {
285     return adoptRef(new InputFieldSpeechButtonElement(document));
286 }
287
288 void InputFieldSpeechButtonElement::defaultEventHandler(Event* event)
289 {
290     // For privacy reasons, only allow clicks directly coming from the user.
291     if (!ScriptController::processingUserGesture()) {
292         HTMLDivElement::defaultEventHandler(event);
293         return;
294     }
295
296     // The call to focus() below dispatches a focus event, and an event handler in the page might
297     // remove the input element from DOM. To make sure it remains valid until we finish our work
298     // here, we take a temporary reference.
299     RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowHost()));
300
301     if (!input || input->isDisabledOrReadOnly()) {
302         if (!event->defaultHandled())
303             HTMLDivElement::defaultEventHandler(event);
304         return;
305     }
306
307     // On mouse down, select the text and set focus.
308     if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
309         if (renderer() && renderer()->visibleToHitTesting()) {
310             if (Frame* frame = document()->frame()) {
311                 frame->eventHandler()->setCapturingMouseEventsNode(this);
312                 m_capturing = true;
313             }
314         }
315         RefPtr<InputFieldSpeechButtonElement> holdRefButton(this);
316         input->focus();
317         input->select();
318         event->setDefaultHandled();
319     }
320     // On mouse up, release capture cleanly.
321     if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
322         if (m_capturing && renderer() && renderer()->visibleToHitTesting()) {
323             if (Frame* frame = document()->frame()) {
324                 frame->eventHandler()->setCapturingMouseEventsNode(0);
325                 m_capturing = false;
326             }
327         }
328     }
329
330     if (event->type() == eventNames().clickEvent && m_listenerId) {
331         switch (m_state) {
332         case Idle:
333             startSpeechInput();
334             break;
335         case Recording:
336             stopSpeechInput();
337             break;
338         case Recognizing:
339             // Nothing to do here, we will continue to wait for results.
340             break;
341         }
342         event->setDefaultHandled();
343     }
344
345     if (!event->defaultHandled())
346         HTMLDivElement::defaultEventHandler(event);
347 }
348
349 bool InputFieldSpeechButtonElement::willRespondToMouseClickEvents()
350 {
351     const HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowHost());
352     if (input && !input->isDisabledOrReadOnly())
353         return true;
354
355     return HTMLDivElement::willRespondToMouseClickEvents();
356 }
357
358 void InputFieldSpeechButtonElement::setState(SpeechInputState state)
359 {
360     if (m_state != state) {
361         m_state = state;
362         shadowHost()->renderer()->repaint();
363     }
364 }
365
366 SpeechInput* InputFieldSpeechButtonElement::speechInput()
367 {
368     return SpeechInput::from(document()->page());
369 }
370
371 void InputFieldSpeechButtonElement::didCompleteRecording(int)
372 {
373     setState(Recognizing);
374 }
375
376 void InputFieldSpeechButtonElement::didCompleteRecognition(int)
377 {
378     setState(Idle);
379 }
380
381 void InputFieldSpeechButtonElement::setRecognitionResult(int, const SpeechInputResultArray& results)
382 {
383     m_results = results;
384
385     // The call to setValue() below dispatches an event, and an event handler in the page might
386     // remove the input element from DOM. To make sure it remains valid until we finish our work
387     // here, we take a temporary reference.
388     RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowHost()));
389     if (!input || input->isDisabledOrReadOnly())
390         return;
391
392     RefPtr<InputFieldSpeechButtonElement> holdRefButton(this);
393     if (document() && document()->domWindow()) {
394         // Call selectionChanged, causing the element to cache the selection,
395         // so that the text event inserts the text in this element even if
396         // focus has moved away from it.
397         input->selectionChanged(false);
398         input->dispatchEvent(TextEvent::create(document()->domWindow(), results.isEmpty() ? "" : results[0]->utterance(), TextEventInputOther));
399     }
400
401     // This event is sent after the text event so the website can perform actions using the input field content immediately.
402     // It provides alternative recognition hypotheses and notifies that the results come from speech input.
403     input->dispatchEvent(SpeechInputEvent::create(eventNames().webkitspeechchangeEvent, results));
404
405     // Check before accessing the renderer as the above event could have potentially turned off
406     // speech in the input element, hence removing this button and renderer from the hierarchy.
407     if (renderer())
408         renderer()->repaint();
409 }
410
411 void InputFieldSpeechButtonElement::attach()
412 {
413     ASSERT(!m_listenerId);
414     if (SpeechInput* input = SpeechInput::from(document()->page()))
415         m_listenerId = input->registerListener(this);
416     HTMLDivElement::attach();
417 }
418
419 void InputFieldSpeechButtonElement::detach()
420 {
421     if (m_capturing) {
422         if (Frame* frame = document()->frame())
423             frame->eventHandler()->setCapturingMouseEventsNode(0);
424     }
425
426     if (m_listenerId) {
427         if (m_state != Idle)
428             speechInput()->cancelRecognition(m_listenerId);
429         speechInput()->unregisterListener(m_listenerId);
430         m_listenerId = 0;
431     }
432
433     HTMLDivElement::detach();
434 }
435
436 void InputFieldSpeechButtonElement::startSpeechInput()
437 {
438     if (m_state != Idle)
439         return;
440
441     RefPtr<HTMLInputElement> input = static_cast<HTMLInputElement*>(shadowHost());
442     AtomicString language = input->computeInheritedLanguage();
443     String grammar = input->getAttribute(webkitgrammarAttr);
444     IntRect rect = document()->view()->contentsToRootView(pixelSnappedBoundingBox());
445     if (speechInput()->startRecognition(m_listenerId, rect, language, grammar, document()->securityOrigin()))
446         setState(Recording);
447 }
448
449 void InputFieldSpeechButtonElement::stopSpeechInput()
450 {
451     if (m_state == Recording)
452         speechInput()->stopRecording(m_listenerId);
453 }
454
455 const AtomicString& InputFieldSpeechButtonElement::shadowPseudoId() const
456 {
457     DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-input-speech-button", AtomicString::ConstructFromLiteral));
458     return pseudoId;
459 }
460
461 #endif // ENABLE(INPUT_SPEECH)
462
463 }