Home | History | Annotate | Download | only in shadow
      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