Home | History | Annotate | Download | only in shadow
      1 /*
      2  * Copyright (C) 2012 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
     33 #include "core/html/shadow/PickerIndicatorElement.h"
     34 
     35 #include "core/events/Event.h"
     36 #include "core/events/KeyboardEvent.h"
     37 #include "core/frame/Settings.h"
     38 #include "core/html/shadow/ShadowElementNames.h"
     39 #include "core/page/Chrome.h"
     40 #include "core/page/Page.h"
     41 #include "core/rendering/RenderDetailsMarker.h"
     42 #include "platform/LayoutTestSupport.h"
     43 #include "wtf/TemporaryChange.h"
     44 
     45 using namespace WTF::Unicode;
     46 
     47 namespace blink {
     48 
     49 using namespace HTMLNames;
     50 
     51 inline PickerIndicatorElement::PickerIndicatorElement(Document& document, PickerIndicatorOwner& pickerIndicatorOwner)
     52     : HTMLDivElement(document)
     53     , m_pickerIndicatorOwner(&pickerIndicatorOwner)
     54     , m_isInOpenPopup(false)
     55 {
     56 }
     57 
     58 PassRefPtrWillBeRawPtr<PickerIndicatorElement> PickerIndicatorElement::create(Document& document, PickerIndicatorOwner& pickerIndicatorOwner)
     59 {
     60     RefPtrWillBeRawPtr<PickerIndicatorElement> element = adoptRefWillBeNoop(new PickerIndicatorElement(document, pickerIndicatorOwner));
     61     element->setShadowPseudoId(AtomicString("-webkit-calendar-picker-indicator", AtomicString::ConstructFromLiteral));
     62     element->setAttribute(idAttr, ShadowElementNames::pickerIndicator());
     63     return element.release();
     64 }
     65 
     66 PickerIndicatorElement::~PickerIndicatorElement()
     67 {
     68     closePopup();
     69     ASSERT(!m_chooser);
     70 }
     71 
     72 RenderObject* PickerIndicatorElement::createRenderer(RenderStyle*)
     73 {
     74     return new RenderDetailsMarker(this);
     75 }
     76 
     77 void PickerIndicatorElement::defaultEventHandler(Event* event)
     78 {
     79     if (!renderer())
     80         return;
     81     if (!m_pickerIndicatorOwner || m_pickerIndicatorOwner->isPickerIndicatorOwnerDisabledOrReadOnly())
     82         return;
     83 
     84     if (event->type() == EventTypeNames::click) {
     85         openPopup();
     86         event->setDefaultHandled();
     87     } else if (event->type() == EventTypeNames::keypress && event->isKeyboardEvent()) {
     88         int charCode = toKeyboardEvent(event)->charCode();
     89         if (charCode == ' ' || charCode == '\r') {
     90             openPopup();
     91             event->setDefaultHandled();
     92         }
     93     }
     94 
     95     if (!event->defaultHandled())
     96         HTMLDivElement::defaultEventHandler(event);
     97 }
     98 
     99 bool PickerIndicatorElement::willRespondToMouseClickEvents()
    100 {
    101     if (renderer() && m_pickerIndicatorOwner && !m_pickerIndicatorOwner->isPickerIndicatorOwnerDisabledOrReadOnly())
    102         return true;
    103 
    104     return HTMLDivElement::willRespondToMouseClickEvents();
    105 }
    106 
    107 void PickerIndicatorElement::didChooseValue(const String& value)
    108 {
    109     if (!m_pickerIndicatorOwner)
    110         return;
    111     m_pickerIndicatorOwner->pickerIndicatorChooseValue(value);
    112 }
    113 
    114 void PickerIndicatorElement::didChooseValue(double value)
    115 {
    116     if (m_pickerIndicatorOwner)
    117         m_pickerIndicatorOwner->pickerIndicatorChooseValue(value);
    118 }
    119 
    120 void PickerIndicatorElement::didEndChooser()
    121 {
    122     m_chooser.clear();
    123 }
    124 
    125 void PickerIndicatorElement::openPopup()
    126 {
    127     // The m_isInOpenPopup flag is unnecessary in production.
    128     // MockPagePopupDriver allows to execute JavaScript code in
    129     // DateTimeChooserImpl constructor. It might create another DateTimeChooser.
    130     if (m_isInOpenPopup)
    131         return;
    132     TemporaryChange<bool> reentrancyProtector(m_isInOpenPopup, true);
    133     if (m_chooser)
    134         return;
    135     if (!document().page())
    136         return;
    137     if (!m_pickerIndicatorOwner)
    138         return;
    139     DateTimeChooserParameters parameters;
    140     if (!m_pickerIndicatorOwner->setupDateTimeChooserParameters(parameters))
    141         return;
    142     m_chooser = document().page()->chrome().openDateTimeChooser(this, parameters);
    143 }
    144 
    145 Element& PickerIndicatorElement::ownerElement() const
    146 {
    147     ASSERT(m_pickerIndicatorOwner);
    148     return m_pickerIndicatorOwner->pickerOwnerElement();
    149 }
    150 
    151 void PickerIndicatorElement::closePopup()
    152 {
    153     if (!m_chooser)
    154         return;
    155     m_chooser->endChooser();
    156 }
    157 
    158 void PickerIndicatorElement::detach(const AttachContext& context)
    159 {
    160     closePopup();
    161     HTMLDivElement::detach(context);
    162 }
    163 
    164 AXObject* PickerIndicatorElement::popupRootAXObject() const
    165 {
    166     return m_chooser ? m_chooser->rootAXObject() : 0;
    167 }
    168 
    169 bool PickerIndicatorElement::isPickerIndicatorElement() const
    170 {
    171     return true;
    172 }
    173 
    174 Node::InsertionNotificationRequest PickerIndicatorElement::insertedInto(ContainerNode* insertionPoint)
    175 {
    176     HTMLDivElement::insertedInto(insertionPoint);
    177     return InsertionShouldCallDidNotifySubtreeInsertions;
    178 }
    179 
    180 void PickerIndicatorElement::didNotifySubtreeInsertionsToDocument()
    181 {
    182     if (!document().settings() || !document().settings()->accessibilityEnabled())
    183         return;
    184     // Don't make this focusable if we are in layout tests in order to avoid to
    185     // break existing tests.
    186     // FIXME: We should have a way to disable accessibility in layout tests.
    187     if (LayoutTestSupport::isRunningLayoutTest())
    188         return;
    189     setAttribute(tabindexAttr, "0");
    190     setAttribute(aria_haspopupAttr, "true");
    191     setAttribute(roleAttr, "button");
    192 }
    193 
    194 void PickerIndicatorElement::trace(Visitor* visitor)
    195 {
    196     visitor->trace(m_pickerIndicatorOwner);
    197     HTMLDivElement::trace(visitor);
    198 }
    199 
    200 }
    201 
    202 #endif
    203