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