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