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