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 45using namespace WTF::Unicode; 46 47namespace blink { 48 49using namespace HTMLNames; 50 51inline PickerIndicatorElement::PickerIndicatorElement(Document& document, PickerIndicatorOwner& pickerIndicatorOwner) 52 : HTMLDivElement(document) 53 , m_pickerIndicatorOwner(&pickerIndicatorOwner) 54 , m_isInOpenPopup(false) 55{ 56} 57 58PassRefPtrWillBeRawPtr<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 66PickerIndicatorElement::~PickerIndicatorElement() 67{ 68 closePopup(); 69 ASSERT(!m_chooser); 70} 71 72RenderObject* PickerIndicatorElement::createRenderer(RenderStyle*) 73{ 74 return new RenderDetailsMarker(this); 75} 76 77void 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 99bool PickerIndicatorElement::willRespondToMouseClickEvents() 100{ 101 if (renderer() && m_pickerIndicatorOwner && !m_pickerIndicatorOwner->isPickerIndicatorOwnerDisabledOrReadOnly()) 102 return true; 103 104 return HTMLDivElement::willRespondToMouseClickEvents(); 105} 106 107void PickerIndicatorElement::didChooseValue(const String& value) 108{ 109 if (!m_pickerIndicatorOwner) 110 return; 111 m_pickerIndicatorOwner->pickerIndicatorChooseValue(value); 112} 113 114void PickerIndicatorElement::didChooseValue(double value) 115{ 116 if (m_pickerIndicatorOwner) 117 m_pickerIndicatorOwner->pickerIndicatorChooseValue(value); 118} 119 120void PickerIndicatorElement::didEndChooser() 121{ 122 m_chooser.clear(); 123} 124 125void 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 145Element& PickerIndicatorElement::ownerElement() const 146{ 147 ASSERT(m_pickerIndicatorOwner); 148 return m_pickerIndicatorOwner->pickerOwnerElement(); 149} 150 151void PickerIndicatorElement::closePopup() 152{ 153 if (!m_chooser) 154 return; 155 m_chooser->endChooser(); 156} 157 158void PickerIndicatorElement::detach(const AttachContext& context) 159{ 160 closePopup(); 161 HTMLDivElement::detach(context); 162} 163 164AXObject* PickerIndicatorElement::popupRootAXObject() const 165{ 166 return m_chooser ? m_chooser->rootAXObject() : 0; 167} 168 169bool PickerIndicatorElement::isPickerIndicatorElement() const 170{ 171 return true; 172} 173 174Node::InsertionNotificationRequest PickerIndicatorElement::insertedInto(ContainerNode* insertionPoint) 175{ 176 HTMLDivElement::insertedInto(insertionPoint); 177 return InsertionShouldCallDidNotifySubtreeInsertions; 178} 179 180void 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 194void PickerIndicatorElement::trace(Visitor* visitor) 195{ 196 visitor->trace(m_pickerIndicatorOwner); 197 HTMLDivElement::trace(visitor); 198} 199 200} 201 202#endif 203