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 50 TextControlInnerElement::TextControlInnerElement(Document* document, HTMLElement* shadowParent) 51 : HTMLDivElement(divTag, document) 52 { 53 setShadowHost(shadowParent); 54 } 55 56 PassRefPtr<TextControlInnerElement> TextControlInnerElement::create(HTMLElement* shadowParent) 57 { 58 return adoptRef(new TextControlInnerElement(shadowParent->document(), shadowParent)); 59 } 60 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 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 99 inline TextControlInnerTextElement::TextControlInnerTextElement(Document* document, HTMLElement* shadowParent) 100 : TextControlInnerElement(document, shadowParent) 101 { 102 } 103 104 PassRefPtr<TextControlInnerTextElement> TextControlInnerTextElement::create(Document* document, HTMLElement* shadowParent) 105 { 106 return adoptRef(new TextControlInnerTextElement(document, shadowParent)); 107 } 108 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 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 139 inline SearchFieldResultsButtonElement::SearchFieldResultsButtonElement(Document* document) 140 : TextControlInnerElement(document) 141 { 142 } 143 144 PassRefPtr<SearchFieldResultsButtonElement> SearchFieldResultsButtonElement::create(Document* document) 145 { 146 return adoptRef(new SearchFieldResultsButtonElement(document)); 147 } 148 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 170 inline SearchFieldCancelButtonElement::SearchFieldCancelButtonElement(Document* document) 171 : TextControlInnerElement(document) 172 , m_capturing(false) 173 { 174 } 175 176 PassRefPtr<SearchFieldCancelButtonElement> SearchFieldCancelButtonElement::create(Document* document) 177 { 178 return adoptRef(new SearchFieldCancelButtonElement(document)); 179 } 180 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 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 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 236 PassRefPtr<SpinButtonElement> SpinButtonElement::create(HTMLElement* shadowParent) 237 { 238 return adoptRef(new SpinButtonElement(shadowParent)); 239 } 240 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 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 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 328 void SpinButtonElement::stopRepeatingTimer() 329 { 330 m_repeatingTimer.stop(); 331 } 332 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 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 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 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 378 PassRefPtr<InputFieldSpeechButtonElement> InputFieldSpeechButtonElement::create(HTMLElement* shadowParent) 379 { 380 return adoptRef(new InputFieldSpeechButtonElement(shadowParent)); 381 } 382 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 449 void InputFieldSpeechButtonElement::setState(SpeechInputState state) 450 { 451 if (m_state != state) { 452 m_state = state; 453 shadowAncestorNode()->renderer()->repaint(); 454 } 455 } 456 457 SpeechInput* InputFieldSpeechButtonElement::speechInput() 458 { 459 return document()->page() ? document()->page()->speechInput() : 0; 460 } 461 462 void InputFieldSpeechButtonElement::didCompleteRecording(int) 463 { 464 setState(Recognizing); 465 } 466 467 void InputFieldSpeechButtonElement::didCompleteRecognition(int) 468 { 469 setState(Idle); 470 } 471 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 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