1/*
2 * Copyright (C) 2011 Ericsson AB
3 * 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 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * 3. Neither the name of Ericsson nor the names of its contributors
16 * may be used to endorse or promote products derived from this
17 * software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "config.h"
33
34#if ENABLE(DEVICE_ELEMENT)
35
36#include "HTMLDeviceElement.h"
37
38#include "Chrome.h"
39#include "DeviceManagerProxy.h"
40#include "Document.h"
41#include "Frame.h"
42#include "HTMLNames.h"
43#include "KeyboardEvent.h"
44#include "LocalizedStrings.h"
45#include "Page.h"
46#include "PlatformString.h"
47#include "RenderButton.h"
48#include <wtf/HashMap.h>
49#include <wtf/text/AtomicStringHash.h>
50
51namespace WebCore {
52
53using namespace HTMLNames;
54
55typedef HashMap<String, DeviceType, CaseFoldingHash> DeviceTypeMap;
56
57static PassOwnPtr<DeviceTypeMap> createDeviceTypeMap()
58{
59 OwnPtr<DeviceTypeMap> map = adoptPtr(new DeviceTypeMap);
60 map->add(DeviceTypeNames::media(), MediaDeviceType);
61 map->add(DeviceTypeNames::audio(), AudioDeviceType);
62 map->add(DeviceTypeNames::video(), VideoDeviceType);
63 return map.release();
64}
65
66HTMLDeviceElement::HTMLDeviceElement(const QualifiedName& tagName, Document* doc)
67 : HTMLElement(tagName, doc)
68 , m_data(0)
69 , m_deviceList(0)
70{
71 ASSERT(hasTagName(deviceTag));
72}
73
74HTMLDeviceElement::~HTMLDeviceElement()
75{
76}
77
78PassRefPtr<HTMLDeviceElement> HTMLDeviceElement::create(Document* document)
79{
80 return adoptRef(new HTMLDeviceElement(deviceTag, document));
81}
82
83PassRefPtr<HTMLDeviceElement> HTMLDeviceElement::create(const QualifiedName& tagName, Document* document)
84{
85 return adoptRef(new HTMLDeviceElement(tagName, document));
86}
87
88String HTMLDeviceElement::type() const
89{
90 return getAttribute(typeAttr);
91}
92
93void HTMLDeviceElement::setType(const String& type)
94{
95 setAttribute(typeAttr, type);
96}
97
98RenderObject* HTMLDeviceElement::createRenderer(RenderArena* arena, RenderStyle*)
99{
100 return new (arena) RenderButton(this);
101}
102
103void HTMLDeviceElement::attach()
104{
105 ASSERT(!attached());
106
107 HTMLElement::attach();
108
109 if (renderer())
110 static_cast<RenderButton*>(renderer())->setText(deviceElementButtonLabel());
111}
112
113void HTMLDeviceElement::parseMappedAttribute(Attribute* attr)
114{
115 if (attr->name() == typeAttr)
116 updateDeviceType();
117 else
118 HTMLElement::parseMappedAttribute(attr);
119}
120
121void HTMLDeviceElement::updateDeviceType()
122{
123 static const DeviceTypeMap* deviceTypeMap = createDeviceTypeMap().leakPtr();
124 const AtomicString& typeString = fastGetAttribute(typeAttr);
125
126 DeviceTypeMap::const_iterator result = deviceTypeMap->find(typeString);
127 m_deviceType = result != deviceTypeMap->end() ? result->second : UnknownDeviceType;
128}
129
130void HTMLDeviceElement::defaultEventHandler(Event* evt)
131{
132 if (evt->type() == eventNames().DOMActivateEvent) {
133 runDialog();
134 evt->setDefaultHandled();
135 return;
136 }
137
138 if (evt->isKeyboardEvent()) {
139 KeyboardEvent* keyEvent = static_cast<KeyboardEvent*>(evt);
140
141 if (evt->type() == eventNames().keypressEvent) {
142 switch (keyEvent->charCode()) {
143 case '\r':
144 dispatchSimulatedClick(evt);
145 case ' ':
146 evt->setDefaultHandled();
147 return;
148 }
149 } else if (keyEvent->keyIdentifier() == "U+0020") {
150 if (evt->type() == eventNames().keydownEvent)
151 setActive(true, true);
152 else { // keyupEvent
153 if (active())
154 dispatchSimulatedClick(evt);
155 evt->setDefaultHandled();
156 }
157 return;
158 }
159 }
160 HTMLElement::defaultEventHandler(evt);
161}
162
163void HTMLDeviceElement::runDialog()
164{
165 Frame* frame = document()->frame();
166 if (!frame || !frame->loader()->isProcessingUserGesture())
167 return;
168
169 Page* page = frame->page();
170 if (!page)
171 return;
172 if (Chrome* chrome = page->chrome())
173 chrome->runDeviceDialog(m_deviceList.get(), this);
174}
175
176void HTMLDeviceElement::selectionsMade(PassRefPtr<DeviceList> prpNewDeviceList)
177{
178 RefPtr<DeviceList> newDeviceList = prpNewDeviceList;
179 bool hasSelections = newDeviceList->selectedEntries().size();
180 bool hasChanged = m_deviceList ? !newDeviceList->selectionsMatch(m_deviceList.get()) : hasSelections;
181 m_deviceList = newDeviceList;
182
183 if (hasChanged) {
184 m_data = hasSelections ? deviceManagerProxy().createDeviceData(m_deviceList.get()) : 0;
185 dispatchEvent(Event::create(eventNames().changeEvent, true, false));
186 }
187}
188
189bool HTMLDeviceElement::supportsFocus() const
190{
191 return true;
192}
193
194bool HTMLDeviceElement::isFocusable() const
195{
196 return renderer() && renderer()->isBox() && !toRenderBox(renderer())->size().isEmpty()
197 && HTMLElement::isFocusable();
198}
199
200bool HTMLDeviceElement::isKeyboardFocusable(KeyboardEvent* event) const
201{
202 return isFocusable() && document()->frame()
203 && document()->frame()->eventHandler()->tabsToAllControls(event);
204}
205
206bool HTMLDeviceElement::isMouseFocusable() const
207{
208#if PLATFORM(GTK) || PLATFORM(QT)
209 return HTMLElement::isMouseFocusable();
210#else
211 return false;
212#endif
213}
214
215namespace DeviceTypeNames {
216
217const AtomicString& media()
218{
219 DEFINE_STATIC_LOCAL(AtomicString, name, ("media"));
220 return name;
221}
222
223const AtomicString& audio()
224{
225 DEFINE_STATIC_LOCAL(AtomicString, name, ("audio"));
226 return name;
227}
228
229const AtomicString& video()
230{
231 DEFINE_STATIC_LOCAL(AtomicString, name, ("video"));
232 return name;
233}
234
235} // namespace WebCore::DeviceTypeNames
236
237} // namespace WebCore
238
239#endif