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 "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