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 "RenderLayer.h"
41 #include "RenderTextControlSingleLine.h"
42 #include "ScrollbarTheme.h"
43 #include "SpeechInput.h"
44 #include "SpeechInputEvent.h"
45
46 namespace WebCore {
47
48 using namespace HTMLNames;
49
TextControlInnerElement(Document * document,HTMLElement * shadowParent)50 TextControlInnerElement::TextControlInnerElement(Document* document, HTMLElement* shadowParent)
51 : HTMLDivElement(divTag, document)
52 {
53 setShadowHost(shadowParent);
54 }
55
create(HTMLElement * shadowParent)56 PassRefPtr<TextControlInnerElement> TextControlInnerElement::create(HTMLElement* shadowParent)
57 {
58 return adoptRef(new TextControlInnerElement(shadowParent->document(), shadowParent));
59 }
60
attachInnerElement(Node * parent,PassRefPtr<RenderStyle> style,RenderArena * arena)61 void TextControlInnerElement::attachInnerElement(Node* parent, PassRefPtr<RenderStyle> style, RenderArena* arena)
62 {
63 // When adding these elements, create the renderer & style first before adding to the DOM.
64 // Otherwise, the render tree will create some anonymous blocks that will mess up our layout.
65
66 // Create the renderer with the specified style
67 RenderObject* renderer = createRenderer(arena, style.get());
68 if (renderer) {
69 setRenderer(renderer);
70 renderer->setStyle(style);
71 }
72
73 // Set these explicitly since this normally happens during an attach()
74 setAttached();
75 setInDocument();
76
77 // For elements not yet in shadow DOM, add the node to the DOM normally.
78 if (!isShadowRoot()) {
79 // FIXME: This code seems very wrong. Why are we magically adding |this| to the DOM here?
80 // We shouldn't be calling parser API methods outside of the parser!
81 parent->deprecatedParserAddChild(this);
82 }
83
84 // Add the renderer to the render tree
85 if (renderer)
86 parent->renderer()->addChild(renderer);
87 }
88
detach()89 void TextControlInnerElement::detach()
90 {
91 HTMLDivElement::detach();
92 // FIXME: Remove once shadow DOM uses Element::setShadowRoot().
93 if (shadowHost())
94 setShadowHost(0);
95 }
96
97 // ----------------------------
98
TextControlInnerTextElement(Document * document,HTMLElement * shadowParent)99 inline TextControlInnerTextElement::TextControlInnerTextElement(Document* document, HTMLElement* shadowParent)
100 : TextControlInnerElement(document, shadowParent)
101 {
102 }
103
create(Document * document,HTMLElement * shadowParent)104 PassRefPtr<TextControlInnerTextElement> TextControlInnerTextElement::create(Document* document, HTMLElement* shadowParent)
105 {
106 return adoptRef(new TextControlInnerTextElement(document, shadowParent));
107 }
108
defaultEventHandler(Event * event)109 void TextControlInnerTextElement::defaultEventHandler(Event* event)
110 {
111 // FIXME: In the future, we should add a way to have default event listeners.
112 // Then we would add one to the text field's inner div, and we wouldn't need this subclass.
113 // Or possibly we could just use a normal event listener.
114 if (event->isBeforeTextInsertedEvent() || event->type() == eventNames().webkitEditableContentChangedEvent) {
115 Node* shadowAncestor = shadowAncestorNode();
116 // A TextControlInnerTextElement can be its own shadow ancestor if its been detached, but kept alive by an EditCommand.
117 // In this case, an undo/redo can cause events to be sent to the TextControlInnerTextElement.
118 // To prevent an infinite loop, we must check for this case before sending the event up the chain.
119 if (shadowAncestor && shadowAncestor != this)
120 shadowAncestor->defaultEventHandler(event);
121 }
122 if (!event->defaultHandled())
123 HTMLDivElement::defaultEventHandler(event);
124 }
125
createRenderer(RenderArena * arena,RenderStyle *)126 RenderObject* TextControlInnerTextElement::createRenderer(RenderArena* arena, RenderStyle*)
127 {
128 bool multiLine = false;
129 Node* shadowAncestor = shadowAncestorNode();
130 if (shadowAncestor && shadowAncestor->renderer()) {
131 ASSERT(shadowAncestor->renderer()->isTextField() || shadowAncestor->renderer()->isTextArea());
132 multiLine = shadowAncestor->renderer()->isTextArea();
133 }
134 return new (arena) RenderTextControlInnerBlock(this, multiLine);
135 }
136
137 // ----------------------------
138
SearchFieldResultsButtonElement(Document * document)139 inline SearchFieldResultsButtonElement::SearchFieldResultsButtonElement(Document* document)
140 : TextControlInnerElement(document)
141 {
142 }
143
create(Document * document)144 PassRefPtr<SearchFieldResultsButtonElement> SearchFieldResultsButtonElement::create(Document* document)
145 {
146 return adoptRef(new SearchFieldResultsButtonElement(document));
147 }
148
defaultEventHandler(Event * event)149 void SearchFieldResultsButtonElement::defaultEventHandler(Event* event)
150 {
151 // On mousedown, bring up a menu, if needed
152 HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowAncestorNode());
153 if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
154 input->focus();
155 input->select();
156 RenderTextControlSingleLine* renderer = toRenderTextControlSingleLine(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
SearchFieldCancelButtonElement(Document * document)170 inline SearchFieldCancelButtonElement::SearchFieldCancelButtonElement(Document* document)
171 : TextControlInnerElement(document)
172 , m_capturing(false)
173 {
174 }
175
create(Document * document)176 PassRefPtr<SearchFieldCancelButtonElement> SearchFieldCancelButtonElement::create(Document* document)
177 {
178 return adoptRef(new SearchFieldCancelButtonElement(document));
179 }
180
detach()181 void SearchFieldCancelButtonElement::detach()
182 {
183 if (m_capturing) {
184 if (Frame* frame = document()->frame())
185 frame->eventHandler()->setCapturingMouseEventsNode(0);
186 }
187 TextControlInnerElement::detach();
188 }
189
190
defaultEventHandler(Event * event)191 void SearchFieldCancelButtonElement::defaultEventHandler(Event* event)
192 {
193 // If the element is visible, on mouseup, clear the value, and set selection
194 RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowAncestorNode()));
195 if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
196 if (renderer() && renderer()->visibleToHitTesting()) {
197 if (Frame* frame = document()->frame()) {
198 frame->eventHandler()->setCapturingMouseEventsNode(this);
199 m_capturing = true;
200 }
201 }
202 input->focus();
203 input->select();
204 event->setDefaultHandled();
205 }
206 if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
207 if (m_capturing) {
208 if (Frame* frame = document()->frame()) {
209 frame->eventHandler()->setCapturingMouseEventsNode(0);
210 m_capturing = false;
211 }
212 if (hovered()) {
213 String oldValue = input->value();
214 input->setValueForUser("");
215 input->onSearch();
216 event->setDefaultHandled();
217 }
218 }
219 }
220
221 if (!event->defaultHandled())
222 HTMLDivElement::defaultEventHandler(event);
223 }
224
225 // ----------------------------
226
SpinButtonElement(HTMLElement * shadowParent)227 inline SpinButtonElement::SpinButtonElement(HTMLElement* shadowParent)
228 : TextControlInnerElement(shadowParent->document(), shadowParent)
229 , m_capturing(false)
230 , m_upDownState(Indeterminate)
231 , m_pressStartingState(Indeterminate)
232 , m_repeatingTimer(this, &SpinButtonElement::repeatingTimerFired)
233 {
234 }
235
create(HTMLElement * shadowParent)236 PassRefPtr<SpinButtonElement> SpinButtonElement::create(HTMLElement* shadowParent)
237 {
238 return adoptRef(new SpinButtonElement(shadowParent));
239 }
240
detach()241 void SpinButtonElement::detach()
242 {
243 stopRepeatingTimer();
244 if (m_capturing) {
245 if (Frame* frame = document()->frame()) {
246 frame->eventHandler()->setCapturingMouseEventsNode(0);
247 m_capturing = false;
248 }
249 }
250 TextControlInnerElement::detach();
251 }
252
defaultEventHandler(Event * event)253 void SpinButtonElement::defaultEventHandler(Event* event)
254 {
255 if (!event->isMouseEvent()) {
256 if (!event->defaultHandled())
257 HTMLDivElement::defaultEventHandler(event);
258 return;
259 }
260
261 RenderBox* box = renderBox();
262 if (!box) {
263 if (!event->defaultHandled())
264 HTMLDivElement::defaultEventHandler(event);
265 return;
266 }
267
268 RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowAncestorNode()));
269 if (input->disabled() || input->isReadOnlyFormControl()) {
270 if (!event->defaultHandled())
271 HTMLDivElement::defaultEventHandler(event);
272 return;
273 }
274
275 MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
276 IntPoint local = roundedIntPoint(box->absoluteToLocal(mouseEvent->absoluteLocation(), false, true));
277 if (mouseEvent->type() == eventNames().mousedownEvent && mouseEvent->button() == LeftButton) {
278 if (box->borderBoxRect().contains(local)) {
279 // The following functions of HTMLInputElement may run JavaScript
280 // code which detaches this shadow node. We need to take a reference
281 // and check renderer() after such function calls.
282 RefPtr<Node> protector(this);
283 input->focus();
284 input->select();
285 if (renderer()) {
286 input->stepUpFromRenderer(m_upDownState == Up ? 1 : -1);
287 if (renderer())
288 startRepeatingTimer();
289 }
290 event->setDefaultHandled();
291 }
292 } else if (mouseEvent->type() == eventNames().mouseupEvent && mouseEvent->button() == LeftButton)
293 stopRepeatingTimer();
294 else if (event->type() == eventNames().mousemoveEvent) {
295 if (box->borderBoxRect().contains(local)) {
296 if (!m_capturing) {
297 if (Frame* frame = document()->frame()) {
298 frame->eventHandler()->setCapturingMouseEventsNode(this);
299 m_capturing = true;
300 }
301 }
302 UpDownState oldUpDownState = m_upDownState;
303 m_upDownState = local.y() < box->height() / 2 ? Up : Down;
304 if (m_upDownState != oldUpDownState)
305 renderer()->repaint();
306 } else {
307 if (m_capturing) {
308 stopRepeatingTimer();
309 if (Frame* frame = document()->frame()) {
310 frame->eventHandler()->setCapturingMouseEventsNode(0);
311 m_capturing = false;
312 }
313 }
314 }
315 }
316
317 if (!event->defaultHandled())
318 HTMLDivElement::defaultEventHandler(event);
319 }
320
startRepeatingTimer()321 void SpinButtonElement::startRepeatingTimer()
322 {
323 m_pressStartingState = m_upDownState;
324 ScrollbarTheme* theme = ScrollbarTheme::nativeTheme();
325 m_repeatingTimer.start(theme->initialAutoscrollTimerDelay(), theme->autoscrollTimerDelay());
326 }
327
stopRepeatingTimer()328 void SpinButtonElement::stopRepeatingTimer()
329 {
330 m_repeatingTimer.stop();
331 }
332
repeatingTimerFired(Timer<SpinButtonElement> *)333 void SpinButtonElement::repeatingTimerFired(Timer<SpinButtonElement>*)
334 {
335 HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowAncestorNode());
336 if (input->disabled() || input->isReadOnlyFormControl())
337 return;
338 // On Mac OS, NSStepper updates the value for the button under the mouse
339 // cursor regardless of the button pressed at the beginning. So the
340 // following check is not needed for Mac OS.
341 #if !OS(MAC_OS_X)
342 if (m_upDownState != m_pressStartingState)
343 return;
344 #endif
345 input->stepUpFromRenderer(m_upDownState == Up ? 1 : -1);
346 }
347
setHovered(bool flag)348 void SpinButtonElement::setHovered(bool flag)
349 {
350 if (!hovered() && flag)
351 m_upDownState = Indeterminate;
352 TextControlInnerElement::setHovered(flag);
353 }
354
355
356 // ----------------------------
357
358 #if ENABLE(INPUT_SPEECH)
359
InputFieldSpeechButtonElement(HTMLElement * shadowParent)360 inline InputFieldSpeechButtonElement::InputFieldSpeechButtonElement(HTMLElement* shadowParent)
361 : TextControlInnerElement(shadowParent->document(), shadowParent)
362 , m_capturing(false)
363 , m_state(Idle)
364 , m_listenerId(document()->page()->speechInput()->registerListener(this))
365 {
366 }
367
~InputFieldSpeechButtonElement()368 InputFieldSpeechButtonElement::~InputFieldSpeechButtonElement()
369 {
370 SpeechInput* speech = speechInput();
371 if (speech && m_listenerId) { // Could be null when page is unloading.
372 if (m_state != Idle)
373 speech->cancelRecognition(m_listenerId);
374 speech->unregisterListener(m_listenerId);
375 }
376 }
377
create(HTMLElement * shadowParent)378 PassRefPtr<InputFieldSpeechButtonElement> InputFieldSpeechButtonElement::create(HTMLElement* shadowParent)
379 {
380 return adoptRef(new InputFieldSpeechButtonElement(shadowParent));
381 }
382
defaultEventHandler(Event * event)383 void InputFieldSpeechButtonElement::defaultEventHandler(Event* event)
384 {
385 // For privacy reasons, only allow clicks directly coming from the user.
386 if (!event->fromUserGesture()) {
387 HTMLDivElement::defaultEventHandler(event);
388 return;
389 }
390
391 // The call to focus() below dispatches a focus event, and an event handler in the page might
392 // remove the input element from DOM. To make sure it remains valid until we finish our work
393 // here, we take a temporary reference.
394 RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowAncestorNode()));
395
396 if (input->disabled() || input->isReadOnlyFormControl()) {
397 if (!event->defaultHandled())
398 HTMLDivElement::defaultEventHandler(event);
399 return;
400 }
401
402 // On mouse down, select the text and set focus.
403 if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
404 if (renderer() && renderer()->visibleToHitTesting()) {
405 if (Frame* frame = document()->frame()) {
406 frame->eventHandler()->setCapturingMouseEventsNode(this);
407 m_capturing = true;
408 }
409 }
410 RefPtr<InputFieldSpeechButtonElement> holdRefButton(this);
411 input->focus();
412 input->select();
413 event->setDefaultHandled();
414 }
415 // On mouse up, release capture cleanly.
416 if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
417 if (m_capturing && renderer() && renderer()->visibleToHitTesting()) {
418 if (Frame* frame = document()->frame()) {
419 frame->eventHandler()->setCapturingMouseEventsNode(0);
420 m_capturing = false;
421 }
422 }
423 }
424
425 if (event->type() == eventNames().clickEvent) {
426 switch (m_state) {
427 case Idle: {
428 AtomicString language = input->computeInheritedLanguage();
429 String grammar = input->getAttribute(webkitgrammarAttr);
430 IntRect rect = input->renderer()->absoluteBoundingBoxRect();
431 if (speechInput()->startRecognition(m_listenerId, rect, language, grammar, document()->securityOrigin()))
432 setState(Recording);
433 }
434 break;
435 case Recording:
436 speechInput()->stopRecording(m_listenerId);
437 break;
438 case Recognizing:
439 // Nothing to do here, we will continue to wait for results.
440 break;
441 }
442 event->setDefaultHandled();
443 }
444
445 if (!event->defaultHandled())
446 HTMLDivElement::defaultEventHandler(event);
447 }
448
setState(SpeechInputState state)449 void InputFieldSpeechButtonElement::setState(SpeechInputState state)
450 {
451 if (m_state != state) {
452 m_state = state;
453 shadowAncestorNode()->renderer()->repaint();
454 }
455 }
456
speechInput()457 SpeechInput* InputFieldSpeechButtonElement::speechInput()
458 {
459 return document()->page() ? document()->page()->speechInput() : 0;
460 }
461
didCompleteRecording(int)462 void InputFieldSpeechButtonElement::didCompleteRecording(int)
463 {
464 setState(Recognizing);
465 }
466
didCompleteRecognition(int)467 void InputFieldSpeechButtonElement::didCompleteRecognition(int)
468 {
469 setState(Idle);
470 }
471
setRecognitionResult(int,const SpeechInputResultArray & results)472 void InputFieldSpeechButtonElement::setRecognitionResult(int, const SpeechInputResultArray& results)
473 {
474 m_results = results;
475
476 // The call to setValue() below dispatches an event, and an event handler in the page might
477 // remove the input element from DOM. To make sure it remains valid until we finish our work
478 // here, we take a temporary reference.
479 RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowAncestorNode()));
480 if (input->disabled() || input->isReadOnlyFormControl())
481 return;
482
483 RefPtr<InputFieldSpeechButtonElement> holdRefButton(this);
484 input->setValue(results.isEmpty() ? "" : results[0]->utterance());
485 input->dispatchEvent(SpeechInputEvent::create(eventNames().webkitspeechchangeEvent, results));
486
487 // Check before accessing the renderer as the above event could have potentially turned off
488 // speech in the input element, hence removing this button and renderer from the hierarchy.
489 if (renderer())
490 renderer()->repaint();
491 }
492
detach()493 void InputFieldSpeechButtonElement::detach()
494 {
495 if (m_capturing) {
496 if (Frame* frame = document()->frame())
497 frame->eventHandler()->setCapturingMouseEventsNode(0);
498 }
499
500 if (m_listenerId) {
501 if (m_state != Idle)
502 speechInput()->cancelRecognition(m_listenerId);
503 speechInput()->unregisterListener(m_listenerId);
504 m_listenerId = 0;
505 }
506
507 TextControlInnerElement::detach();
508 }
509
510 #endif // ENABLE(INPUT_SPEECH)
511
512 }
513