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