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