Merge from Chromium at DEPS revision r198571
This commit was generated by merge_to_master.py.
Change-Id: I3a7f89ea6b8c017335bd52739166aed708cad1e5
diff --git a/Source/core/html/shadow/ClearButtonElement.cpp b/Source/core/html/shadow/ClearButtonElement.cpp
new file mode 100644
index 0000000..3558ace
--- /dev/null
+++ b/Source/core/html/shadow/ClearButtonElement.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "core/html/shadow/ClearButtonElement.h"
+
+#include "core/dom/MouseEvent.h"
+#include "core/page/EventHandler.h"
+#include "core/page/Frame.h"
+#include "core/rendering/RenderView.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+inline ClearButtonElement::ClearButtonElement(Document* document, ClearButtonOwner& clearButtonOwner)
+ : HTMLDivElement(divTag, document)
+ , m_clearButtonOwner(&clearButtonOwner)
+ , m_capturing(false)
+{
+ setPseudo(AtomicString("-webkit-clear-button", AtomicString::ConstructFromLiteral));
+}
+
+PassRefPtr<ClearButtonElement> ClearButtonElement::create(Document* document, ClearButtonOwner& clearButtonOwner)
+{
+ return adoptRef(new ClearButtonElement(document, clearButtonOwner));
+}
+
+void ClearButtonElement::detach()
+{
+ if (m_capturing) {
+ if (Frame* frame = document()->frame())
+ frame->eventHandler()->setCapturingMouseEventsNode(0);
+ }
+ HTMLDivElement::detach();
+}
+
+void ClearButtonElement::releaseCapture()
+{
+ if (!m_capturing)
+ return;
+
+ if (Frame* frame = document()->frame()) {
+ frame->eventHandler()->setCapturingMouseEventsNode(0);
+ m_capturing = false;
+ }
+}
+
+void ClearButtonElement::defaultEventHandler(Event* event)
+{
+ if (!m_clearButtonOwner) {
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+ return;
+ }
+
+ if (!m_clearButtonOwner->shouldClearButtonRespondToMouseEvents()) {
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+ return;
+ }
+
+ if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
+ if (renderer() && renderer()->visibleToHitTesting()) {
+ if (Frame* frame = document()->frame()) {
+ frame->eventHandler()->setCapturingMouseEventsNode(this);
+ m_capturing = true;
+ }
+ }
+ m_clearButtonOwner->focusAndSelectClearButtonOwner();
+ event->setDefaultHandled();
+ }
+ if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
+ if (m_capturing) {
+ if (Frame* frame = document()->frame()) {
+ frame->eventHandler()->setCapturingMouseEventsNode(0);
+ m_capturing = false;
+ }
+ if (hovered()) {
+ m_clearButtonOwner->clearValue();
+ event->setDefaultHandled();
+ }
+ }
+ }
+
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+}
+
+}
diff --git a/Source/core/html/shadow/ClearButtonElement.h b/Source/core/html/shadow/ClearButtonElement.h
new file mode 100644
index 0000000..bb5439b
--- /dev/null
+++ b/Source/core/html/shadow/ClearButtonElement.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2013 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef ClearButtonElement_h
+#define ClearButtonElement_h
+
+#include "core/html/HTMLDivElement.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class ClearButtonElement FINAL : public HTMLDivElement {
+public:
+ class ClearButtonOwner {
+ public:
+ virtual ~ClearButtonOwner() { }
+ virtual void focusAndSelectClearButtonOwner() = 0;
+ virtual bool shouldClearButtonRespondToMouseEvents() = 0;
+ virtual void clearValue() = 0;
+ };
+
+ static PassRefPtr<ClearButtonElement> create(Document*, ClearButtonOwner&);
+ void releaseCapture();
+ void removeClearButtonOwner() { m_clearButtonOwner = 0; }
+
+private:
+ ClearButtonElement(Document*, ClearButtonOwner&);
+ virtual void detach();
+ virtual bool isMouseFocusable() const { return false; }
+ virtual void defaultEventHandler(Event*);
+
+ ClearButtonOwner* m_clearButtonOwner;
+ bool m_capturing;
+};
+
+} // namespace
+
+#endif // ClearButtonElement_h
diff --git a/Source/core/html/shadow/ContentDistributor.cpp b/Source/core/html/shadow/ContentDistributor.cpp
new file mode 100644
index 0000000..9f30ad6
--- /dev/null
+++ b/Source/core/html/shadow/ContentDistributor.cpp
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "core/html/shadow/ContentDistributor.h"
+
+#include "core/dom/ElementShadow.h"
+#include "core/dom/NodeTraversal.h"
+#include "core/dom/ShadowRoot.h"
+#include "core/html/shadow/ContentSelectorQuery.h"
+#include "core/html/shadow/HTMLContentElement.h"
+#include "core/html/shadow/HTMLShadowElement.h"
+
+
+namespace WebCore {
+
+void ContentDistribution::swap(ContentDistribution& other)
+{
+ m_nodes.swap(other.m_nodes);
+ m_indices.swap(other.m_indices);
+}
+
+void ContentDistribution::append(PassRefPtr<Node> node)
+{
+ size_t size = m_nodes.size();
+ m_indices.set(node.get(), size);
+ m_nodes.append(node);
+}
+
+size_t ContentDistribution::find(const Node* node) const
+{
+ HashMap<const Node*, size_t>::const_iterator it = m_indices.find(node);
+ if (it == m_indices.end())
+ return notFound;
+
+ return it.get()->value;
+}
+
+Node* ContentDistribution::nextTo(const Node* node) const
+{
+ size_t index = find(node);
+ if (index == notFound || index + 1 == size())
+ return 0;
+ return at(index + 1).get();
+}
+
+Node* ContentDistribution::previousTo(const Node* node) const
+{
+ size_t index = find(node);
+ if (index == notFound || !index)
+ return 0;
+ return at(index - 1).get();
+}
+
+
+ScopeContentDistribution::ScopeContentDistribution()
+ : m_insertionPointAssignedTo(0)
+ , m_numberOfShadowElementChildren(0)
+ , m_numberOfContentElementChildren(0)
+ , m_numberOfElementShadowChildren(0)
+ , m_insertionPointListIsValid(false)
+{
+}
+
+void ScopeContentDistribution::invalidateInsertionPointList()
+{
+ m_insertionPointListIsValid = false;
+ m_insertionPointList.clear();
+}
+
+const Vector<RefPtr<InsertionPoint> >& ScopeContentDistribution::ensureInsertionPointList(ShadowRoot* shadowRoot)
+{
+ if (m_insertionPointListIsValid)
+ return m_insertionPointList;
+
+ m_insertionPointListIsValid = true;
+ ASSERT(m_insertionPointList.isEmpty());
+
+ if (!hasInsertionPoint(shadowRoot))
+ return m_insertionPointList;
+
+ for (Element* element = ElementTraversal::firstWithin(shadowRoot); element; element = ElementTraversal::next(element, shadowRoot)) {
+ if (element->isInsertionPoint())
+ m_insertionPointList.append(toInsertionPoint(element));
+ }
+
+ return m_insertionPointList;
+}
+
+void ScopeContentDistribution::registerInsertionPoint(InsertionPoint* point)
+{
+ switch (point->insertionPointType()) {
+ case InsertionPoint::ShadowInsertionPoint:
+ ++m_numberOfShadowElementChildren;
+ break;
+ case InsertionPoint::ContentInsertionPoint:
+ ++m_numberOfContentElementChildren;
+ break;
+ }
+
+ invalidateInsertionPointList();
+}
+
+void ScopeContentDistribution::unregisterInsertionPoint(InsertionPoint* point)
+{
+ switch (point->insertionPointType()) {
+ case InsertionPoint::ShadowInsertionPoint:
+ ASSERT(m_numberOfShadowElementChildren > 0);
+ --m_numberOfShadowElementChildren;
+ break;
+ case InsertionPoint::ContentInsertionPoint:
+ ASSERT(m_numberOfContentElementChildren > 0);
+ --m_numberOfContentElementChildren;
+ break;
+ }
+
+ invalidateInsertionPointList();
+}
+
+bool ScopeContentDistribution::hasShadowElement(const ShadowRoot* holder)
+{
+ if (!holder->scopeDistribution())
+ return false;
+
+ return holder->scopeDistribution()->hasShadowElementChildren();
+}
+
+bool ScopeContentDistribution::hasContentElement(const ShadowRoot* holder)
+{
+ if (!holder->scopeDistribution())
+ return false;
+
+ return holder->scopeDistribution()->hasContentElementChildren();
+}
+
+unsigned ScopeContentDistribution::countElementShadow(const ShadowRoot* holder)
+{
+ if (!holder->scopeDistribution())
+ return 0;
+
+ return holder->scopeDistribution()->numberOfElementShadowChildren();
+}
+
+bool ScopeContentDistribution::hasInsertionPoint(const ShadowRoot* holder)
+{
+ return hasShadowElement(holder) || hasContentElement(holder);
+}
+
+InsertionPoint* ScopeContentDistribution::assignedTo(const ShadowRoot* holder)
+{
+ if (!holder->scopeDistribution())
+ return 0;
+
+ return holder->scopeDistribution()->insertionPointAssignedTo();
+}
+
+ContentDistributor::ContentDistributor()
+ : m_needsSelectFeatureSet(false)
+ , m_validity(Undetermined)
+{
+}
+
+ContentDistributor::~ContentDistributor()
+{
+}
+
+InsertionPoint* ContentDistributor::findInsertionPointFor(const Node* key) const
+{
+ return m_nodeToInsertionPoint.get(key).get();
+}
+
+void ContentDistributor::populate(Node* node, ContentDistribution& pool)
+{
+ if (!isActiveInsertionPoint(node)) {
+ pool.append(node);
+ return;
+ }
+
+ InsertionPoint* insertionPoint = toInsertionPoint(node);
+ if (insertionPoint->hasDistribution()) {
+ for (size_t i = 0; i < insertionPoint->size(); ++i)
+ populate(insertionPoint->at(i), pool);
+ } else {
+ for (Node* fallbackNode = insertionPoint->firstChild(); fallbackNode; fallbackNode = fallbackNode->nextSibling())
+ pool.append(fallbackNode);
+ }
+}
+
+void ContentDistributor::distribute(Element* host)
+{
+ ASSERT(needsDistribution());
+ ASSERT(m_nodeToInsertionPoint.isEmpty());
+ ASSERT(!host->containingShadowRoot() || host->containingShadowRoot()->owner()->distributor().isValid());
+
+ m_validity = Valid;
+
+ ContentDistribution pool;
+ for (Node* node = host->firstChild(); node; node = node->nextSibling())
+ populate(node, pool);
+
+ Vector<bool> distributed(pool.size());
+ distributed.fill(false);
+
+ Vector<HTMLShadowElement*, 8> activeShadowInsertionPoints;
+ for (ShadowRoot* root = host->youngestShadowRoot(); root; root = root->olderShadowRoot()) {
+ HTMLShadowElement* firstActiveShadowInsertionPoint = 0;
+
+ if (ScopeContentDistribution* scope = root->scopeDistribution()) {
+ const Vector<RefPtr<InsertionPoint> >& insertionPoints = scope->ensureInsertionPointList(root);
+ for (size_t i = 0; i < insertionPoints.size(); ++i) {
+ InsertionPoint* point = insertionPoints[i].get();
+ if (!point->isActive())
+ continue;
+
+ if (isHTMLShadowElement(point)) {
+ if (!firstActiveShadowInsertionPoint)
+ firstActiveShadowInsertionPoint = toHTMLShadowElement(point);
+ } else {
+ distributeSelectionsTo(point, pool, distributed);
+ if (ElementShadow* shadow = point->parentNode()->isElementNode() ? toElement(point->parentNode())->shadow() : 0)
+ shadow->invalidateDistribution();
+ }
+ }
+ }
+
+ if (firstActiveShadowInsertionPoint)
+ activeShadowInsertionPoints.append(firstActiveShadowInsertionPoint);
+ }
+
+ for (size_t i = activeShadowInsertionPoints.size(); i > 0; --i) {
+ HTMLShadowElement* shadowElement = activeShadowInsertionPoints[i - 1];
+ ShadowRoot* root = shadowElement->containingShadowRoot();
+ ASSERT(root);
+ if (root->olderShadowRoot()) {
+ distributeNodeChildrenTo(shadowElement, root->olderShadowRoot());
+ root->olderShadowRoot()->ensureScopeDistribution()->setInsertionPointAssignedTo(shadowElement);
+ } else {
+ distributeSelectionsTo(shadowElement, pool, distributed);
+ if (ElementShadow* shadow = shadowElement->parentNode()->isElementNode() ? toElement(shadowElement->parentNode())->shadow() : 0)
+ shadow->invalidateDistribution();
+ }
+ }
+}
+
+bool ContentDistributor::invalidate(Element* host)
+{
+ ASSERT(needsInvalidation());
+ bool needsReattach = (m_validity == Undetermined) || !m_nodeToInsertionPoint.isEmpty();
+
+ for (ShadowRoot* root = host->youngestShadowRoot(); root; root = root->olderShadowRoot()) {
+ if (ScopeContentDistribution* scope = root->scopeDistribution()) {
+ scope->setInsertionPointAssignedTo(0);
+ const Vector<RefPtr<InsertionPoint> >& insertionPoints = scope->ensureInsertionPointList(root);
+ for (size_t i = 0; i < insertionPoints.size(); ++i) {
+ needsReattach = needsReattach || true;
+ insertionPoints[i]->clearDistribution();
+
+ // After insertionPoint's distribution is invalidated, its reprojection should also be invalidated.
+ if (!insertionPoints[i]->isActive())
+ continue;
+
+ if (Element* parent = insertionPoints[i]->parentElement()) {
+ if (ElementShadow* shadow = parent->shadow())
+ shadow->invalidateDistribution();
+ }
+ }
+ }
+ }
+
+ m_validity = Invalidating;
+ m_nodeToInsertionPoint.clear();
+ return needsReattach;
+}
+
+void ContentDistributor::distributeSelectionsTo(InsertionPoint* insertionPoint, const ContentDistribution& pool, Vector<bool>& distributed)
+{
+ ContentDistribution distribution;
+ ContentSelectorQuery query(insertionPoint);
+
+ for (size_t i = 0; i < pool.size(); ++i) {
+ if (distributed[i])
+ continue;
+
+ if (!query.matches(pool.nodes(), i))
+ continue;
+
+ Node* child = pool.at(i).get();
+ distribution.append(child);
+ m_nodeToInsertionPoint.add(child, insertionPoint);
+ distributed[i] = true;
+ }
+
+ insertionPoint->setDistribution(distribution);
+}
+
+void ContentDistributor::distributeNodeChildrenTo(InsertionPoint* insertionPoint, ContainerNode* containerNode)
+{
+ ContentDistribution distribution;
+ for (Node* node = containerNode->firstChild(); node; node = node->nextSibling()) {
+ if (isActiveInsertionPoint(node)) {
+ InsertionPoint* innerInsertionPoint = toInsertionPoint(node);
+ if (innerInsertionPoint->hasDistribution()) {
+ for (size_t i = 0; i < innerInsertionPoint->size(); ++i) {
+ distribution.append(innerInsertionPoint->at(i));
+ if (!m_nodeToInsertionPoint.contains(innerInsertionPoint->at(i)))
+ m_nodeToInsertionPoint.add(innerInsertionPoint->at(i), insertionPoint);
+ }
+ } else {
+ for (Node* child = innerInsertionPoint->firstChild(); child; child = child->nextSibling()) {
+ distribution.append(child);
+ m_nodeToInsertionPoint.add(child, insertionPoint);
+ }
+ }
+ } else {
+ distribution.append(node);
+ if (!m_nodeToInsertionPoint.contains(node))
+ m_nodeToInsertionPoint.add(node, insertionPoint);
+ }
+ }
+
+ insertionPoint->setDistribution(distribution);
+}
+
+void ContentDistributor::ensureDistribution(ShadowRoot* shadowRoot)
+{
+ ASSERT(shadowRoot);
+
+ Vector<ElementShadow*, 8> elementShadows;
+ for (Element* current = shadowRoot->host(); current; current = current->shadowHost()) {
+ ElementShadow* elementShadow = current->shadow();
+ if (!elementShadow->distributor().needsDistribution())
+ break;
+
+ elementShadows.append(elementShadow);
+ }
+
+ for (size_t i = elementShadows.size(); i > 0; --i)
+ elementShadows[i - 1]->distributor().distribute(elementShadows[i - 1]->host());
+}
+
+
+void ContentDistributor::invalidateDistribution(Element* host)
+{
+ bool didNeedInvalidation = needsInvalidation();
+ bool needsReattach = didNeedInvalidation ? invalidate(host) : false;
+
+ if (needsReattach && host->attached()) {
+ for (Node* n = host->firstChild(); n; n = n->nextSibling())
+ n->lazyReattach();
+ host->setNeedsStyleRecalc();
+ }
+
+ if (didNeedInvalidation) {
+ ASSERT(m_validity == Invalidating);
+ m_validity = Invalidated;
+ }
+}
+
+const SelectRuleFeatureSet& ContentDistributor::ensureSelectFeatureSet(ElementShadow* shadow)
+{
+ if (!m_needsSelectFeatureSet)
+ return m_selectFeatures;
+
+ m_selectFeatures.clear();
+ for (ShadowRoot* root = shadow->oldestShadowRoot(); root; root = root->youngerShadowRoot())
+ collectSelectFeatureSetFrom(root);
+ m_needsSelectFeatureSet = false;
+ return m_selectFeatures;
+}
+
+void ContentDistributor::collectSelectFeatureSetFrom(ShadowRoot* root)
+{
+ if (ScopeContentDistribution::hasElementShadow(root)) {
+ for (Element* element = ElementTraversal::firstWithin(root); element; element = ElementTraversal::next(element)) {
+ if (ElementShadow* elementShadow = element->shadow())
+ m_selectFeatures.add(elementShadow->distributor().ensureSelectFeatureSet(elementShadow));
+ }
+ }
+
+ if (ScopeContentDistribution::hasContentElement(root)) {
+ for (Element* element = ElementTraversal::firstWithin(root); element; element = ElementTraversal::next(element)) {
+ if (!isHTMLContentElement(element))
+ continue;
+ const CSSSelectorList& list = toHTMLContentElement(element)->selectorList();
+ for (const CSSSelector* selector = list.first(); selector; selector = CSSSelectorList::next(selector)) {
+ for (const CSSSelector* component = selector; component; component = component->tagHistory())
+ m_selectFeatures.collectFeaturesFromSelector(component);
+ }
+ }
+ }
+}
+
+void ContentDistributor::didAffectSelector(Element* host, AffectedSelectorMask mask)
+{
+ if (ensureSelectFeatureSet(host->shadow()).hasSelectorFor(mask))
+ invalidateDistribution(host);
+}
+
+void ContentDistributor::willAffectSelector(Element* host)
+{
+ for (ElementShadow* shadow = host->shadow(); shadow; shadow = shadow->containingShadow()) {
+ if (shadow->distributor().needsSelectFeatureSet())
+ break;
+ shadow->distributor().setNeedsSelectFeatureSet();
+ }
+
+ invalidateDistribution(host);
+}
+
+void ContentDistributor::didShadowBoundaryChange(Element* host)
+{
+ setValidity(Undetermined);
+ invalidateDistribution(host);
+}
+
+}
diff --git a/Source/core/html/shadow/ContentDistributor.h b/Source/core/html/shadow/ContentDistributor.h
new file mode 100644
index 0000000..1d3e657
--- /dev/null
+++ b/Source/core/html/shadow/ContentDistributor.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ContentDistributor_h
+#define ContentDistributor_h
+
+#include "core/html/shadow/SelectRuleFeatureSet.h"
+#include <wtf/Forward.h>
+#include <wtf/HashMap.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class ContainerNode;
+class Element;
+class InsertionPoint;
+class Node;
+class ShadowRoot;
+
+class ContentDistribution {
+public:
+ PassRefPtr<Node> first() const { return m_nodes.first(); }
+ PassRefPtr<Node> last() const { return m_nodes.last(); }
+ PassRefPtr<Node> at(size_t index) const { return m_nodes.at(index); }
+
+ size_t size() const { return m_nodes.size(); }
+ bool isEmpty() const { return m_nodes.isEmpty(); }
+
+ void append(PassRefPtr<Node>);
+ void clear() { m_nodes.clear(); m_indices.clear(); }
+
+ bool contains(const Node* node) const { return m_indices.contains(node); }
+ size_t find(const Node*) const;
+ Node* nextTo(const Node*) const;
+ Node* previousTo(const Node*) const;
+
+ void swap(ContentDistribution& other);
+
+ const Vector<RefPtr<Node> >& nodes() const { return m_nodes; }
+
+private:
+ Vector<RefPtr<Node> > m_nodes;
+ HashMap<const Node*, size_t> m_indices;
+};
+
+class ScopeContentDistribution {
+public:
+ ScopeContentDistribution();
+
+ InsertionPoint* insertionPointAssignedTo() const { return m_insertionPointAssignedTo; }
+ void setInsertionPointAssignedTo(InsertionPoint* insertionPoint) { m_insertionPointAssignedTo = insertionPoint; }
+
+ void registerInsertionPoint(InsertionPoint*);
+ void unregisterInsertionPoint(InsertionPoint*);
+ bool hasShadowElementChildren() const { return m_numberOfShadowElementChildren > 0; }
+ bool hasContentElementChildren() const { return m_numberOfContentElementChildren > 0; }
+
+ void registerElementShadow() { ++m_numberOfElementShadowChildren; }
+ void unregisterElementShadow() { ASSERT(m_numberOfElementShadowChildren > 0); --m_numberOfElementShadowChildren; }
+ unsigned numberOfElementShadowChildren() const { return m_numberOfElementShadowChildren; }
+ bool hasElementShadowChildren() const { return m_numberOfElementShadowChildren > 0; }
+
+ void invalidateInsertionPointList();
+ const Vector<RefPtr<InsertionPoint> >& ensureInsertionPointList(ShadowRoot*);
+
+ bool isUsedForRendering() const;
+
+ static bool hasShadowElement(const ShadowRoot*);
+ static bool hasContentElement(const ShadowRoot*);
+ static bool hasInsertionPoint(const ShadowRoot*);
+ static bool hasElementShadow(const ShadowRoot* holder) { return countElementShadow(holder); }
+ static unsigned countElementShadow(const ShadowRoot*);
+ static InsertionPoint* assignedTo(const ShadowRoot*);
+
+private:
+ InsertionPoint* m_insertionPointAssignedTo;
+ unsigned m_numberOfShadowElementChildren;
+ unsigned m_numberOfContentElementChildren;
+ unsigned m_numberOfElementShadowChildren;
+ bool m_insertionPointListIsValid;
+ Vector<RefPtr<InsertionPoint> > m_insertionPointList;
+};
+
+class ContentDistributor {
+ WTF_MAKE_NONCOPYABLE(ContentDistributor);
+public:
+ enum Validity {
+ Valid = 0,
+ Invalidated = 1,
+ Invalidating = 2,
+ Undetermined = 3
+ };
+
+ ContentDistributor();
+ ~ContentDistributor();
+
+ InsertionPoint* findInsertionPointFor(const Node* key) const;
+ const SelectRuleFeatureSet& ensureSelectFeatureSet(ElementShadow*);
+
+ void distributeSelectionsTo(InsertionPoint*, const ContentDistribution& pool, Vector<bool>& distributed);
+ void distributeNodeChildrenTo(InsertionPoint*, ContainerNode*);
+
+ void invalidateDistribution(Element* host);
+ void didShadowBoundaryChange(Element* host);
+ void didAffectSelector(Element* host, AffectedSelectorMask);
+ void willAffectSelector(Element* host);
+
+ static void ensureDistribution(ShadowRoot*);
+
+private:
+ void distribute(Element* host);
+ bool invalidate(Element* host);
+ void populate(Node*, ContentDistribution&);
+
+ void collectSelectFeatureSetFrom(ShadowRoot*);
+ bool needsSelectFeatureSet() const { return m_needsSelectFeatureSet; }
+ void setNeedsSelectFeatureSet() { m_needsSelectFeatureSet = true; }
+
+ void setValidity(Validity validity) { m_validity = validity; }
+ bool isValid() const { return m_validity == Valid; }
+ bool needsDistribution() const;
+ bool needsInvalidation() const { return m_validity != Invalidated; }
+
+ HashMap<const Node*, RefPtr<InsertionPoint> > m_nodeToInsertionPoint;
+ SelectRuleFeatureSet m_selectFeatures;
+ bool m_needsSelectFeatureSet : 1;
+ unsigned m_validity : 2;
+};
+
+inline bool ContentDistributor::needsDistribution() const
+{
+ // During the invalidation, re-distribution should be supressed.
+ return m_validity != Valid && m_validity != Invalidating;
+}
+
+}
+
+#endif
diff --git a/Source/core/html/shadow/ContentSelectorQuery.cpp b/Source/core/html/shadow/ContentSelectorQuery.cpp
new file mode 100644
index 0000000..418e159
--- /dev/null
+++ b/Source/core/html/shadow/ContentSelectorQuery.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "core/html/shadow/ContentSelectorQuery.h"
+
+#include "core/css/CSSSelectorList.h"
+#include "core/css/SelectorChecker.h"
+#include "core/css/SiblingTraversalStrategies.h"
+#include "core/dom/ShadowRoot.h"
+#include "core/html/shadow/InsertionPoint.h"
+
+namespace WebCore {
+
+bool ContentSelectorDataList::checkContentSelector(const CSSSelector* selector, const Vector<RefPtr<Node> >& siblings, int nth)
+{
+ Element* element = toElement(siblings[nth].get());
+ SelectorChecker selectorChecker(element->document(), SelectorChecker::CollectingRules);
+ SelectorChecker::SelectorCheckingContext context(selector, element, SelectorChecker::VisitedMatchEnabled);
+ ShadowDOMSiblingTraversalStrategy strategy(siblings, nth);
+ PseudoId ignoreDynamicPseudo = NOPSEUDO;
+ return selectorChecker.match(context, ignoreDynamicPseudo, strategy) == SelectorChecker::SelectorMatches;
+}
+
+void ContentSelectorDataList::initialize(const CSSSelectorList& selectors)
+{
+ for (const CSSSelector* selector = selectors.first(); selector; selector = CSSSelectorList::next(selector))
+ m_selectors.append(selector);
+}
+
+bool ContentSelectorDataList::matches(const Vector<RefPtr<Node> >& siblings, int nth) const
+{
+ unsigned selectorCount = m_selectors.size();
+ for (unsigned i = 0; i < selectorCount; ++i) {
+ if (checkContentSelector(m_selectors[i], siblings, nth))
+ return true;
+ }
+ return false;
+}
+
+ContentSelectorQuery::ContentSelectorQuery(InsertionPoint* insertionPoint)
+ : m_insertionPoint(insertionPoint)
+{
+ m_selectors.initialize(insertionPoint->selectorList());
+}
+
+bool ContentSelectorQuery::matches(const Vector<RefPtr<Node> >& siblings, int nth) const
+{
+ Node* node = siblings[nth].get();
+ ASSERT(node);
+
+ switch (m_insertionPoint->matchTypeFor(node)) {
+ case InsertionPoint::AlwaysMatches:
+ return true;
+ case InsertionPoint::NeverMatches:
+ return false;
+ case InsertionPoint::HasToMatchSelector:
+ return node->isElementNode() && m_selectors.matches(siblings, nth);
+ default:
+ ASSERT_NOT_REACHED();
+ return false;
+ }
+}
+
+}
diff --git a/Source/core/html/shadow/ContentSelectorQuery.h b/Source/core/html/shadow/ContentSelectorQuery.h
new file mode 100644
index 0000000..b86098d
--- /dev/null
+++ b/Source/core/html/shadow/ContentSelectorQuery.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ContentSelectorQuery_h
+#define ContentSelectorQuery_h
+
+#include "core/css/CSSSelectorList.h"
+#include "core/css/SelectorChecker.h"
+#include "core/dom/SelectorQuery.h"
+#include <wtf/Forward.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class Document;
+class Node;
+class InsertionPoint;
+
+class ContentSelectorDataList {
+public:
+ void initialize(const CSSSelectorList&);
+ bool matches(const Vector<RefPtr<Node> >& siblings, int nthNode) const;
+
+private:
+ static bool checkContentSelector(const CSSSelector*, const Vector<RefPtr<Node> >& siblings, int nthNode);
+
+ Vector<const CSSSelector*> m_selectors;
+};
+
+class ContentSelectorQuery {
+ WTF_MAKE_NONCOPYABLE(ContentSelectorQuery);
+public:
+ explicit ContentSelectorQuery(InsertionPoint*);
+
+ bool matches(const Vector<RefPtr<Node> >& siblings, int nthNode) const;
+
+private:
+ InsertionPoint* m_insertionPoint;
+ ContentSelectorDataList m_selectors;
+};
+
+}
+
+#endif
diff --git a/Source/core/html/shadow/DateTimeEditElement.cpp b/Source/core/html/shadow/DateTimeEditElement.cpp
new file mode 100644
index 0000000..1e323a1
--- /dev/null
+++ b/Source/core/html/shadow/DateTimeEditElement.cpp
@@ -0,0 +1,798 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
+#include "core/html/shadow/DateTimeEditElement.h"
+
+#include "HTMLNames.h"
+#include "core/css/StyleResolver.h"
+#include "core/dom/KeyboardEvent.h"
+#include "core/dom/MouseEvent.h"
+#include "core/dom/Text.h"
+#include "core/html/DateTimeFieldsState.h"
+#include "core/html/shadow/DateTimeFieldElements.h"
+#include "core/html/shadow/DateTimeSymbolicFieldElement.h"
+#include "core/page/EventHandler.h"
+#include "core/platform/DateComponents.h"
+#include "core/platform/graphics/FontCache.h"
+#include "core/platform/text/DateTimeFormat.h"
+#include "core/platform/text/PlatformLocale.h"
+#include "core/rendering/style/RenderStyle.h"
+#include <wtf/DateMath.h>
+#include <wtf/text/StringBuilder.h>
+
+namespace WebCore {
+
+using namespace HTMLNames;
+using namespace WTF::Unicode;
+
+class DateTimeEditBuilder : private DateTimeFormat::TokenHandler {
+ WTF_MAKE_NONCOPYABLE(DateTimeEditBuilder);
+
+public:
+ // The argument objects must be alive until this object dies.
+ DateTimeEditBuilder(DateTimeEditElement&, const DateTimeEditElement::LayoutParameters&, const DateComponents&);
+
+ bool build(const String&);
+
+private:
+ bool needMillisecondField() const;
+ bool shouldAMPMFieldDisabled() const;
+ bool shouldDayOfMonthFieldDisabled() const;
+ bool shouldHourFieldDisabled() const;
+ bool shouldMillisecondFieldDisabled() const;
+ bool shouldMinuteFieldDisabled() const;
+ bool shouldSecondFieldDisabled() const;
+ bool shouldYearFieldDisabled() const;
+ inline const StepRange& stepRange() const { return m_parameters.stepRange; }
+ DateTimeNumericFieldElement::Step createStep(double msPerFieldUnit, double msPerFieldSize) const;
+
+ // DateTimeFormat::TokenHandler functions.
+ virtual void visitField(DateTimeFormat::FieldType, int) OVERRIDE FINAL;
+ virtual void visitLiteral(const String&) OVERRIDE FINAL;
+
+ DateTimeEditElement& m_editElement;
+ const DateComponents m_dateValue;
+ const DateTimeEditElement::LayoutParameters& m_parameters;
+ DateTimeNumericFieldElement::Range m_dayRange;
+ DateTimeNumericFieldElement::Range m_hour23Range;
+ DateTimeNumericFieldElement::Range m_minuteRange;
+ DateTimeNumericFieldElement::Range m_secondRange;
+ DateTimeNumericFieldElement::Range m_millisecondRange;
+};
+
+DateTimeEditBuilder::DateTimeEditBuilder(DateTimeEditElement& elemnt, const DateTimeEditElement::LayoutParameters& layoutParameters, const DateComponents& dateValue)
+ : m_editElement(elemnt)
+ , m_dateValue(dateValue)
+ , m_parameters(layoutParameters)
+ , m_dayRange(1, 31)
+ , m_hour23Range(0, 23)
+ , m_minuteRange(0, 59)
+ , m_secondRange(0, 59)
+ , m_millisecondRange(0, 999)
+{
+ if (m_dateValue.type() == DateComponents::Date
+ || m_dateValue.type() == DateComponents::DateTimeLocal
+ || m_dateValue.type() == DateComponents::DateTime) {
+ if (m_parameters.minimum.type() != DateComponents::Invalid
+ && m_parameters.maximum.type() != DateComponents::Invalid
+ && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
+ && m_parameters.minimum.month() == m_parameters.maximum.month()
+ && m_parameters.minimum.monthDay() <= m_parameters.maximum.monthDay()) {
+ m_dayRange.minimum = m_parameters.minimum.monthDay();
+ m_dayRange.maximum = m_parameters.maximum.monthDay();
+ }
+ }
+
+ if (m_dateValue.type() == DateComponents::Time || m_dayRange.isSingleton()) {
+ if (m_parameters.minimum.type() != DateComponents::Invalid
+ && m_parameters.maximum.type() != DateComponents::Invalid
+ && m_parameters.minimum.hour() <= m_parameters.maximum.hour()) {
+ m_hour23Range.minimum = m_parameters.minimum.hour();
+ m_hour23Range.maximum = m_parameters.maximum.hour();
+ }
+ }
+
+ if (m_hour23Range.isSingleton() && m_parameters.minimum.minute() <= m_parameters.maximum.minute()) {
+ m_minuteRange.minimum = m_parameters.minimum.minute();
+ m_minuteRange.maximum = m_parameters.maximum.minute();
+ }
+ if (m_minuteRange.isSingleton() && m_parameters.minimum.second() <= m_parameters.maximum.second()) {
+ m_secondRange.minimum = m_parameters.minimum.second();
+ m_secondRange.maximum = m_parameters.maximum.second();
+ }
+ if (m_secondRange.isSingleton() && m_parameters.minimum.millisecond() <= m_parameters.maximum.millisecond()) {
+ m_millisecondRange.minimum = m_parameters.minimum.millisecond();
+ m_millisecondRange.maximum = m_parameters.maximum.millisecond();
+ }
+}
+
+bool DateTimeEditBuilder::build(const String& formatString)
+{
+ m_editElement.resetFields();
+ return DateTimeFormat::parse(formatString, *this);
+}
+
+bool DateTimeEditBuilder::needMillisecondField() const
+{
+ return m_dateValue.millisecond()
+ || !stepRange().minimum().remainder(static_cast<int>(msPerSecond)).isZero()
+ || !stepRange().step().remainder(static_cast<int>(msPerSecond)).isZero();
+}
+
+void DateTimeEditBuilder::visitField(DateTimeFormat::FieldType fieldType, int count)
+{
+ const int countForAbbreviatedMonth = 3;
+ const int countForFullMonth = 4;
+ const int countForNarrowMonth = 5;
+ Document* const document = m_editElement.document();
+
+ switch (fieldType) {
+ case DateTimeFormat::FieldTypeDayOfMonth: {
+ RefPtr<DateTimeFieldElement> field = DateTimeDayFieldElement::create(document, m_editElement, m_parameters.placeholderForDay, m_dayRange);
+ m_editElement.addField(field);
+ if (shouldDayOfMonthFieldDisabled()) {
+ field->setValueAsDate(m_dateValue);
+ field->setDisabled();
+ }
+ return;
+ }
+
+ case DateTimeFormat::FieldTypeHour11: {
+ DateTimeNumericFieldElement::Step step = createStep(msPerHour, msPerHour * 12);
+ RefPtr<DateTimeFieldElement> field = DateTimeHour11FieldElement::create(document, m_editElement, m_hour23Range, step);
+ m_editElement.addField(field);
+ if (shouldHourFieldDisabled()) {
+ field->setValueAsDate(m_dateValue);
+ field->setDisabled();
+ }
+ return;
+ }
+
+ case DateTimeFormat::FieldTypeHour12: {
+ DateTimeNumericFieldElement::Step step = createStep(msPerHour, msPerHour * 12);
+ RefPtr<DateTimeFieldElement> field = DateTimeHour12FieldElement::create(document, m_editElement, m_hour23Range, step);
+ m_editElement.addField(field);
+ if (shouldHourFieldDisabled()) {
+ field->setValueAsDate(m_dateValue);
+ field->setDisabled();
+ }
+ return;
+ }
+
+ case DateTimeFormat::FieldTypeHour23: {
+ DateTimeNumericFieldElement::Step step = createStep(msPerHour, msPerDay);
+ RefPtr<DateTimeFieldElement> field = DateTimeHour23FieldElement::create(document, m_editElement, m_hour23Range, step);
+ m_editElement.addField(field);
+ if (shouldHourFieldDisabled()) {
+ field->setValueAsDate(m_dateValue);
+ field->setDisabled();
+ }
+ return;
+ }
+
+ case DateTimeFormat::FieldTypeHour24: {
+ DateTimeNumericFieldElement::Step step = createStep(msPerHour, msPerDay);
+ RefPtr<DateTimeFieldElement> field = DateTimeHour24FieldElement::create(document, m_editElement, m_hour23Range, step);
+ m_editElement.addField(field);
+ if (shouldHourFieldDisabled()) {
+ field->setValueAsDate(m_dateValue);
+ field->setDisabled();
+ }
+ return;
+ }
+
+ case DateTimeFormat::FieldTypeMinute: {
+ DateTimeNumericFieldElement::Step step = createStep(msPerMinute, msPerHour);
+ RefPtr<DateTimeNumericFieldElement> field = DateTimeMinuteFieldElement::create(document, m_editElement, m_minuteRange, step);
+ m_editElement.addField(field);
+ if (shouldMinuteFieldDisabled()) {
+ field->setValueAsDate(m_dateValue);
+ field->setDisabled();
+ }
+ return;
+ }
+
+ case DateTimeFormat::FieldTypeMonth: // Fallthrough.
+ case DateTimeFormat::FieldTypeMonthStandAlone: {
+ int minMonth = 0, maxMonth = 11;
+ if (m_parameters.minimum.type() != DateComponents::Invalid
+ && m_parameters.maximum.type() != DateComponents::Invalid
+ && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
+ && m_parameters.minimum.month() <= m_parameters.maximum.month()) {
+ minMonth = m_parameters.minimum.month();
+ maxMonth = m_parameters.maximum.month();
+ }
+ RefPtr<DateTimeFieldElement> field;
+ switch (count) {
+ case countForNarrowMonth: // Fallthrough.
+ case countForAbbreviatedMonth:
+ field = DateTimeSymbolicMonthFieldElement::create(document, m_editElement, fieldType == DateTimeFormat::FieldTypeMonth ? m_parameters.locale.shortMonthLabels() : m_parameters.locale.shortStandAloneMonthLabels(), minMonth, maxMonth);
+ break;
+ case countForFullMonth:
+ field = DateTimeSymbolicMonthFieldElement::create(document, m_editElement, fieldType == DateTimeFormat::FieldTypeMonth ? m_parameters.locale.monthLabels() : m_parameters.locale.standAloneMonthLabels(), minMonth, maxMonth);
+ break;
+ default:
+ field = DateTimeMonthFieldElement::create(document, m_editElement, m_parameters.placeholderForMonth, DateTimeNumericFieldElement::Range(minMonth + 1, maxMonth + 1));
+ break;
+ }
+ m_editElement.addField(field);
+ if (minMonth == maxMonth && minMonth == m_dateValue.month() && m_dateValue.type() != DateComponents::Month) {
+ field->setValueAsDate(m_dateValue);
+ field->setDisabled();
+ }
+ return;
+ }
+
+ case DateTimeFormat::FieldTypePeriod: {
+ RefPtr<DateTimeFieldElement> field = DateTimeAMPMFieldElement::create(document, m_editElement, m_parameters.locale.timeAMPMLabels());
+ m_editElement.addField(field);
+ if (shouldAMPMFieldDisabled()) {
+ field->setValueAsDate(m_dateValue);
+ field->setDisabled();
+ }
+ return;
+ }
+
+ case DateTimeFormat::FieldTypeSecond: {
+ DateTimeNumericFieldElement::Step step = createStep(msPerSecond, msPerMinute);
+ RefPtr<DateTimeNumericFieldElement> field = DateTimeSecondFieldElement::create(document, m_editElement, m_secondRange, step);
+ m_editElement.addField(field);
+ if (shouldSecondFieldDisabled()) {
+ field->setValueAsDate(m_dateValue);
+ field->setDisabled();
+ }
+
+ if (needMillisecondField()) {
+ visitLiteral(m_parameters.locale.localizedDecimalSeparator());
+ visitField(DateTimeFormat::FieldTypeFractionalSecond, 3);
+ }
+ return;
+ }
+
+ case DateTimeFormat::FieldTypeFractionalSecond: {
+ DateTimeNumericFieldElement::Step step = createStep(1, msPerSecond);
+ RefPtr<DateTimeNumericFieldElement> field = DateTimeMillisecondFieldElement::create(document, m_editElement, m_millisecondRange, step);
+ m_editElement.addField(field);
+ if (shouldMillisecondFieldDisabled()) {
+ field->setValueAsDate(m_dateValue);
+ field->setDisabled();
+ }
+ return;
+ }
+
+ case DateTimeFormat::FieldTypeWeekOfYear: {
+ DateTimeNumericFieldElement::Range range(DateComponents::minimumWeekNumber, DateComponents::maximumWeekNumber);
+ if (m_parameters.minimum.type() != DateComponents::Invalid
+ && m_parameters.maximum.type() != DateComponents::Invalid
+ && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
+ && m_parameters.minimum.week() <= m_parameters.maximum.week()) {
+ range.minimum = m_parameters.minimum.week();
+ range.maximum = m_parameters.maximum.week();
+ }
+ m_editElement.addField(DateTimeWeekFieldElement::create(document, m_editElement, range));
+ return;
+ }
+
+ case DateTimeFormat::FieldTypeYear: {
+ DateTimeYearFieldElement::Parameters yearParams;
+ if (m_parameters.minimum.type() == DateComponents::Invalid) {
+ yearParams.minimumYear = DateComponents::minimumYear();
+ yearParams.minIsSpecified = false;
+ } else {
+ yearParams.minimumYear = m_parameters.minimum.fullYear();
+ yearParams.minIsSpecified = true;
+ }
+ if (m_parameters.maximum.type() == DateComponents::Invalid) {
+ yearParams.maximumYear = DateComponents::maximumYear();
+ yearParams.maxIsSpecified = false;
+ } else {
+ yearParams.maximumYear = m_parameters.maximum.fullYear();
+ yearParams.maxIsSpecified = true;
+ }
+ if (yearParams.minimumYear > yearParams.maximumYear) {
+ std::swap(yearParams.minimumYear, yearParams.maximumYear);
+ std::swap(yearParams.minIsSpecified, yearParams.maxIsSpecified);
+ }
+ yearParams.placeholder = m_parameters.placeholderForYear;
+ RefPtr<DateTimeFieldElement> field = DateTimeYearFieldElement::create(document, m_editElement, yearParams);
+ m_editElement.addField(field);
+ if (shouldYearFieldDisabled()) {
+ field->setValueAsDate(m_dateValue);
+ field->setDisabled();
+ }
+ return;
+ }
+
+ default:
+ return;
+ }
+}
+
+bool DateTimeEditBuilder::shouldAMPMFieldDisabled() const
+{
+ return shouldHourFieldDisabled()
+ || (m_hour23Range.minimum < 12 && m_hour23Range.maximum < 12 && m_dateValue.hour() < 12)
+ || (m_hour23Range.minimum >= 12 && m_hour23Range.maximum >= 12 && m_dateValue.hour() >= 12);
+}
+
+bool DateTimeEditBuilder::shouldDayOfMonthFieldDisabled() const
+{
+ return m_dayRange.isSingleton() && m_dayRange.minimum == m_dateValue.monthDay() && m_dateValue.type() != DateComponents::Date;
+}
+
+bool DateTimeEditBuilder::shouldHourFieldDisabled() const
+{
+ if (m_hour23Range.isSingleton() && m_hour23Range.minimum == m_dateValue.hour()
+ && !(shouldMinuteFieldDisabled() && shouldSecondFieldDisabled() && shouldMillisecondFieldDisabled()))
+ return true;
+
+ if (m_dateValue.type() == DateComponents::Time)
+ return false;
+ ASSERT(m_dateValue.type() == DateComponents::DateTimeLocal || m_dateValue.type() == DateComponents::DateTime);
+
+ if (shouldDayOfMonthFieldDisabled()) {
+ ASSERT(m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear());
+ ASSERT(m_parameters.minimum.month() == m_parameters.maximum.month());
+ return false;
+ }
+
+ const Decimal decimalMsPerDay(static_cast<int>(msPerDay));
+ Decimal hourPartOfMinimum = (stepRange().minimum().abs().remainder(decimalMsPerDay) / static_cast<int>(msPerHour)).floor();
+ return hourPartOfMinimum == m_dateValue.hour() && stepRange().step().remainder(decimalMsPerDay).isZero();
+}
+
+bool DateTimeEditBuilder::shouldMillisecondFieldDisabled() const
+{
+ if (m_millisecondRange.isSingleton() && m_millisecondRange.minimum == m_dateValue.millisecond())
+ return true;
+
+ const Decimal decimalMsPerSecond(static_cast<int>(msPerSecond));
+ return stepRange().minimum().abs().remainder(decimalMsPerSecond) == m_dateValue.millisecond() && stepRange().step().remainder(decimalMsPerSecond).isZero();
+}
+
+bool DateTimeEditBuilder::shouldMinuteFieldDisabled() const
+{
+ if (m_minuteRange.isSingleton() && m_minuteRange.minimum == m_dateValue.minute())
+ return true;
+
+ const Decimal decimalMsPerHour(static_cast<int>(msPerHour));
+ Decimal minutePartOfMinimum = (stepRange().minimum().abs().remainder(decimalMsPerHour) / static_cast<int>(msPerMinute)).floor();
+ return minutePartOfMinimum == m_dateValue.minute() && stepRange().step().remainder(decimalMsPerHour).isZero();
+}
+
+bool DateTimeEditBuilder::shouldSecondFieldDisabled() const
+{
+ if (m_secondRange.isSingleton() && m_secondRange.minimum == m_dateValue.second())
+ return true;
+
+ const Decimal decimalMsPerMinute(static_cast<int>(msPerMinute));
+ Decimal secondPartOfMinimum = (stepRange().minimum().abs().remainder(decimalMsPerMinute) / static_cast<int>(msPerSecond)).floor();
+ return secondPartOfMinimum == m_dateValue.second() && stepRange().step().remainder(decimalMsPerMinute).isZero();
+}
+
+bool DateTimeEditBuilder::shouldYearFieldDisabled() const
+{
+ return m_parameters.minimum.type() != DateComponents::Invalid
+ && m_parameters.maximum.type() != DateComponents::Invalid
+ && m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
+ && m_parameters.minimum.fullYear() == m_dateValue.fullYear();
+}
+
+void DateTimeEditBuilder::visitLiteral(const String& text)
+{
+ DEFINE_STATIC_LOCAL(AtomicString, textPseudoId, ("-webkit-datetime-edit-text", AtomicString::ConstructFromLiteral));
+ ASSERT(text.length());
+ RefPtr<HTMLDivElement> element = HTMLDivElement::create(m_editElement.document());
+ element->setPseudo(textPseudoId);
+ if (m_parameters.locale.isRTL() && text.length()) {
+ Direction dir = direction(text[0]);
+ if (dir == SegmentSeparator || dir == WhiteSpaceNeutral || dir == OtherNeutral)
+ element->appendChild(Text::create(m_editElement.document(), String(&rightToLeftMark, 1)));
+ }
+ element->appendChild(Text::create(m_editElement.document(), text));
+ m_editElement.fieldsWrapperElement()->appendChild(element);
+}
+
+DateTimeNumericFieldElement::Step DateTimeEditBuilder::createStep(double msPerFieldUnit, double msPerFieldSize) const
+{
+ const Decimal msPerFieldUnitDecimal(static_cast<int>(msPerFieldUnit));
+ const Decimal msPerFieldSizeDecimal(static_cast<int>(msPerFieldSize));
+ Decimal stepMilliseconds = stepRange().step();
+ ASSERT(!msPerFieldUnitDecimal.isZero());
+ ASSERT(!msPerFieldSizeDecimal.isZero());
+ ASSERT(!stepMilliseconds.isZero());
+
+ DateTimeNumericFieldElement::Step step(1, 0);
+
+ if (stepMilliseconds.remainder(msPerFieldSizeDecimal).isZero())
+ stepMilliseconds = msPerFieldSizeDecimal;
+
+ if (msPerFieldSizeDecimal.remainder(stepMilliseconds).isZero() && stepMilliseconds.remainder(msPerFieldUnitDecimal).isZero()) {
+ step.step = static_cast<int>((stepMilliseconds / msPerFieldUnitDecimal).toDouble());
+ step.stepBase = static_cast<int>((stepRange().stepBase() / msPerFieldUnitDecimal).floor().remainder(msPerFieldSizeDecimal / msPerFieldUnitDecimal).toDouble());
+ }
+ return step;
+}
+
+// ----------------------------
+
+DateTimeEditElement::EditControlOwner::~EditControlOwner()
+{
+}
+
+DateTimeEditElement::DateTimeEditElement(Document* document, EditControlOwner& editControlOwner)
+ : HTMLDivElement(divTag, document)
+ , m_editControlOwner(&editControlOwner)
+{
+ DEFINE_STATIC_LOCAL(AtomicString, dateTimeEditPseudoId, ("-webkit-datetime-edit", AtomicString::ConstructFromLiteral));
+ setPseudo(dateTimeEditPseudoId);
+ setHasCustomStyleCallbacks();
+}
+
+DateTimeEditElement::~DateTimeEditElement()
+{
+ for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
+ m_fields[fieldIndex]->removeEventHandler();
+}
+
+inline Element* DateTimeEditElement::fieldsWrapperElement() const
+{
+ ASSERT(firstChild());
+ return toElement(firstChild());
+}
+
+void DateTimeEditElement::addField(PassRefPtr<DateTimeFieldElement> field)
+{
+ if (m_fields.size() == m_fields.capacity())
+ return;
+ m_fields.append(field.get());
+ fieldsWrapperElement()->appendChild(field);
+}
+
+bool DateTimeEditElement::anyEditableFieldsHaveValues() const
+{
+ for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
+ if (!m_fields[fieldIndex]->isDisabled() && m_fields[fieldIndex]->hasValue())
+ return true;
+ }
+ return false;
+}
+
+void DateTimeEditElement::blurByOwner()
+{
+ if (DateTimeFieldElement* field = focusedField())
+ field->blur();
+}
+
+PassRefPtr<DateTimeEditElement> DateTimeEditElement::create(Document* document, EditControlOwner& editControlOwner)
+{
+ RefPtr<DateTimeEditElement> container = adoptRef(new DateTimeEditElement(document, editControlOwner));
+ return container.release();
+}
+
+PassRefPtr<RenderStyle> DateTimeEditElement::customStyleForRenderer()
+{
+ // FIXME: This is a kind of layout. We might want to introduce new renderer.
+ FontCachePurgePreventer fontCachePurgePreventer;
+ RefPtr<RenderStyle> originalStyle = document()->styleResolver()->styleForElement(this);
+ RefPtr<RenderStyle> style = RenderStyle::clone(originalStyle.get());
+ float width = 0;
+ for (Node* child = fieldsWrapperElement()->firstChild(); child; child = child->nextSibling()) {
+ if (!child->isElementNode())
+ continue;
+ Element* childElement = toElement(child);
+ if (childElement->isDateTimeFieldElement()) {
+ // We need to pass the Font of this element because child elements
+ // can't resolve inherited style at this timing.
+ width += static_cast<DateTimeFieldElement*>(childElement)->maximumWidth(style->font());
+ } else {
+ // ::-webkit-datetime-edit-text case. It has no
+ // border/padding/margin in html.css.
+ width += style->font().width(childElement->textContent());
+ }
+ }
+ style->setWidth(Length(ceilf(width), Fixed));
+ return style.release();
+}
+
+void DateTimeEditElement::didBlurFromField()
+{
+ if (m_editControlOwner)
+ m_editControlOwner->didBlurFromControl();
+}
+
+void DateTimeEditElement::didFocusOnField()
+{
+ if (m_editControlOwner)
+ m_editControlOwner->didFocusOnControl();
+}
+
+void DateTimeEditElement::disabledStateChanged()
+{
+ updateUIState();
+}
+
+DateTimeFieldElement* DateTimeEditElement::fieldAt(size_t fieldIndex) const
+{
+ return fieldIndex < m_fields.size() ? m_fields[fieldIndex] : 0;
+}
+
+size_t DateTimeEditElement::fieldIndexOf(const DateTimeFieldElement& field) const
+{
+ for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
+ if (m_fields[fieldIndex] == &field)
+ return fieldIndex;
+ }
+ return invalidFieldIndex;
+}
+
+void DateTimeEditElement::focusIfNoFocus()
+{
+ if (focusedFieldIndex() != invalidFieldIndex)
+ return;
+ focusOnNextFocusableField(0);
+}
+
+void DateTimeEditElement::focusByOwner(Node* oldFocusedNode)
+{
+ if (oldFocusedNode && oldFocusedNode->isElementNode() && toElement(oldFocusedNode)->isDateTimeFieldElement()) {
+ DateTimeFieldElement* oldFocusedField = static_cast<DateTimeFieldElement*>(oldFocusedNode);
+ size_t index = fieldIndexOf(*oldFocusedField);
+ if (index != invalidFieldIndex && oldFocusedField->isFocusable()) {
+ oldFocusedField->focus();
+ return;
+ }
+ }
+ focusOnNextFocusableField(0);
+}
+
+DateTimeFieldElement* DateTimeEditElement::focusedField() const
+{
+ return fieldAt(focusedFieldIndex());
+}
+
+size_t DateTimeEditElement::focusedFieldIndex() const
+{
+ Node* const focusedFieldNode = document()->focusedNode();
+ for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
+ if (m_fields[fieldIndex] == focusedFieldNode)
+ return fieldIndex;
+ }
+ return invalidFieldIndex;
+}
+
+void DateTimeEditElement::fieldValueChanged()
+{
+ if (m_editControlOwner)
+ m_editControlOwner->editControlValueChanged();
+}
+
+bool DateTimeEditElement::focusOnNextFocusableField(size_t startIndex)
+{
+ for (size_t fieldIndex = startIndex; fieldIndex < m_fields.size(); ++fieldIndex) {
+ if (m_fields[fieldIndex]->isFocusable()) {
+ m_fields[fieldIndex]->focus();
+ return true;
+ }
+ }
+ return false;
+}
+
+bool DateTimeEditElement::focusOnNextField(const DateTimeFieldElement& field)
+{
+ const size_t startFieldIndex = fieldIndexOf(field);
+ if (startFieldIndex == invalidFieldIndex)
+ return false;
+ return focusOnNextFocusableField(startFieldIndex + 1);
+}
+
+bool DateTimeEditElement::focusOnPreviousField(const DateTimeFieldElement& field)
+{
+ const size_t startFieldIndex = fieldIndexOf(field);
+ if (startFieldIndex == invalidFieldIndex)
+ return false;
+ size_t fieldIndex = startFieldIndex;
+ while (fieldIndex > 0) {
+ --fieldIndex;
+ if (m_fields[fieldIndex]->isFocusable()) {
+ m_fields[fieldIndex]->focus();
+ return true;
+ }
+ }
+ return false;
+}
+
+bool DateTimeEditElement::isDisabled() const
+{
+ return m_editControlOwner && m_editControlOwner->isEditControlOwnerDisabled();
+}
+
+bool DateTimeEditElement::isFieldOwnerDisabled() const
+{
+ return isDisabled();
+}
+
+bool DateTimeEditElement::isFieldOwnerReadOnly() const
+{
+ return isReadOnly();
+}
+
+bool DateTimeEditElement::isReadOnly() const
+{
+ return m_editControlOwner && m_editControlOwner->isEditControlOwnerReadOnly();
+}
+
+void DateTimeEditElement::layout(const LayoutParameters& layoutParameters, const DateComponents& dateValue)
+{
+ DEFINE_STATIC_LOCAL(AtomicString, fieldsWrapperPseudoId, ("-webkit-datetime-edit-fields-wrapper", AtomicString::ConstructFromLiteral));
+ if (!firstChild()) {
+ RefPtr<HTMLDivElement> element = HTMLDivElement::create(document());
+ element->setPseudo(fieldsWrapperPseudoId);
+ appendChild(element.get());
+ }
+ Element* fieldsWrapper = fieldsWrapperElement();
+
+ size_t focusedFieldIndex = this->focusedFieldIndex();
+ DateTimeFieldElement* const focusedField = fieldAt(focusedFieldIndex);
+ const AtomicString focusedFieldId = focusedField ? focusedField->shadowPseudoId() : nullAtom;
+
+ DateTimeEditBuilder builder(*this, layoutParameters, dateValue);
+ Node* lastChildToBeRemoved = fieldsWrapper->lastChild();
+ if (!builder.build(layoutParameters.dateTimeFormat) || m_fields.isEmpty()) {
+ lastChildToBeRemoved = fieldsWrapper->lastChild();
+ builder.build(layoutParameters.fallbackDateTimeFormat);
+ }
+
+ if (focusedFieldIndex != invalidFieldIndex) {
+ for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
+ if (m_fields[fieldIndex]->shadowPseudoId() == focusedFieldId) {
+ focusedFieldIndex = fieldIndex;
+ break;
+ }
+ }
+ if (DateTimeFieldElement* field = fieldAt(std::min(focusedFieldIndex, m_fields.size() - 1)))
+ field->focus();
+ }
+
+ if (lastChildToBeRemoved) {
+ for (Node* childNode = fieldsWrapper->firstChild(); childNode; childNode = fieldsWrapper->firstChild()) {
+ fieldsWrapper->removeChild(childNode);
+ if (childNode == lastChildToBeRemoved)
+ break;
+ }
+ setNeedsStyleRecalc();
+ }
+}
+
+AtomicString DateTimeEditElement::localeIdentifier() const
+{
+ return m_editControlOwner ? m_editControlOwner->localeIdentifier() : nullAtom;
+}
+
+void DateTimeEditElement::readOnlyStateChanged()
+{
+ updateUIState();
+}
+
+void DateTimeEditElement::resetFields()
+{
+ for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
+ m_fields[fieldIndex]->removeEventHandler();
+ m_fields.shrink(0);
+}
+
+void DateTimeEditElement::defaultEventHandler(Event* event)
+{
+ // In case of control owner forward event to control, e.g. DOM
+ // dispatchEvent method.
+ if (DateTimeFieldElement* field = focusedField()) {
+ field->defaultEventHandler(event);
+ if (event->defaultHandled())
+ return;
+ }
+
+ HTMLDivElement::defaultEventHandler(event);
+}
+
+void DateTimeEditElement::setValueAsDate(const LayoutParameters& layoutParameters, const DateComponents& date)
+{
+ layout(layoutParameters, date);
+ for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
+ m_fields[fieldIndex]->setValueAsDate(date);
+}
+
+void DateTimeEditElement::setValueAsDateTimeFieldsState(const DateTimeFieldsState& dateTimeFieldsState)
+{
+ for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
+ m_fields[fieldIndex]->setValueAsDateTimeFieldsState(dateTimeFieldsState);
+}
+
+void DateTimeEditElement::setEmptyValue(const LayoutParameters& layoutParameters, const DateComponents& dateForReadOnlyField)
+{
+ layout(layoutParameters, dateForReadOnlyField);
+ for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
+ m_fields[fieldIndex]->setEmptyValue(DateTimeFieldElement::DispatchNoEvent);
+}
+
+bool DateTimeEditElement::hasFocusedField()
+{
+ return focusedFieldIndex() != invalidFieldIndex;
+}
+
+void DateTimeEditElement::setOnlyYearMonthDay(const DateComponents& date)
+{
+ ASSERT(date.type() == DateComponents::Date);
+
+ if (!m_editControlOwner)
+ return;
+
+ DateTimeFieldsState dateTimeFieldsState = valueAsDateTimeFieldsState();
+ dateTimeFieldsState.setYear(date.fullYear());
+ dateTimeFieldsState.setMonth(date.month() + 1);
+ dateTimeFieldsState.setDayOfMonth(date.monthDay());
+ setValueAsDateTimeFieldsState(dateTimeFieldsState);
+ m_editControlOwner->editControlValueChanged();
+}
+
+void DateTimeEditElement::stepDown()
+{
+ if (DateTimeFieldElement* const field = focusedField())
+ field->stepDown();
+}
+
+void DateTimeEditElement::stepUp()
+{
+ if (DateTimeFieldElement* const field = focusedField())
+ field->stepUp();
+}
+
+void DateTimeEditElement::updateUIState()
+{
+ if (isDisabled()) {
+ if (DateTimeFieldElement* field = focusedField())
+ field->blur();
+ }
+}
+
+String DateTimeEditElement::value() const
+{
+ if (!m_editControlOwner)
+ return emptyString();
+ return m_editControlOwner->formatDateTimeFieldsState(valueAsDateTimeFieldsState());
+}
+
+DateTimeFieldsState DateTimeEditElement::valueAsDateTimeFieldsState() const
+{
+ DateTimeFieldsState dateTimeFieldsState;
+ for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
+ m_fields[fieldIndex]->populateDateTimeFieldsState(dateTimeFieldsState);
+ return dateTimeFieldsState;
+}
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/core/html/shadow/DateTimeEditElement.h b/Source/core/html/shadow/DateTimeEditElement.h
new file mode 100644
index 0000000..5e45f90
--- /dev/null
+++ b/Source/core/html/shadow/DateTimeEditElement.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef DateTimeEditElement_h
+#define DateTimeEditElement_h
+
+#if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
+#include "core/html/StepRange.h"
+#include "core/html/shadow/DateTimeFieldElement.h"
+#include "core/platform/DateComponents.h"
+
+namespace WebCore {
+
+class DateTimeFieldsState;
+class KeyboardEvent;
+class Locale;
+class MouseEvent;
+class StepRange;
+
+// DateTimeEditElement class contains numberic field and symbolc field for
+// representing date and time, such as
+// - Year, Month, Day Of Month
+// - Hour, Minute, Second, Millisecond, AM/PM
+class DateTimeEditElement FINAL : public HTMLDivElement, public DateTimeFieldElement::FieldOwner {
+ WTF_MAKE_NONCOPYABLE(DateTimeEditElement);
+
+public:
+ // EditControlOwner implementer must call removeEditControlOwner when
+ // it doesn't handle event, e.g. at destruction.
+ class EditControlOwner {
+ public:
+ virtual ~EditControlOwner();
+ virtual void didBlurFromControl() = 0;
+ virtual void didFocusOnControl() = 0;
+ virtual void editControlValueChanged() = 0;
+ virtual String formatDateTimeFieldsState(const DateTimeFieldsState&) const = 0;
+ virtual bool isEditControlOwnerDisabled() const = 0;
+ virtual bool isEditControlOwnerReadOnly() const = 0;
+ virtual AtomicString localeIdentifier() const = 0;
+ };
+
+ struct LayoutParameters {
+ String dateTimeFormat;
+ String fallbackDateTimeFormat;
+ Locale& locale;
+ const StepRange stepRange;
+ DateComponents minimum;
+ DateComponents maximum;
+ String placeholderForDay;
+ String placeholderForMonth;
+ String placeholderForYear;
+
+ LayoutParameters(Locale& locale, const StepRange& stepRange)
+ : locale(locale)
+ , stepRange(stepRange)
+ {
+ }
+ };
+
+ static PassRefPtr<DateTimeEditElement> create(Document*, EditControlOwner&);
+
+ virtual ~DateTimeEditElement();
+ void addField(PassRefPtr<DateTimeFieldElement>);
+ bool anyEditableFieldsHaveValues() const;
+ void blurByOwner();
+ virtual void defaultEventHandler(Event*) OVERRIDE;
+ void disabledStateChanged();
+ Element* fieldsWrapperElement() const;
+ void focusIfNoFocus();
+ // If oldFocusedNode is one of sub-fields, focus on it. Otherwise focus on
+ // the first sub-field.
+ void focusByOwner(Node* oldFocusedNode = 0);
+ bool hasFocusedField();
+ void readOnlyStateChanged();
+ void removeEditControlOwner() { m_editControlOwner = 0; }
+ void resetFields();
+ void setEmptyValue(const LayoutParameters&, const DateComponents& dateForReadOnlyField);
+ void setValueAsDate(const LayoutParameters&, const DateComponents&);
+ void setValueAsDateTimeFieldsState(const DateTimeFieldsState&);
+ void setOnlyYearMonthDay(const DateComponents&);
+ void stepDown();
+ void stepUp();
+ String value() const;
+ DateTimeFieldsState valueAsDateTimeFieldsState() const;
+
+private:
+ static const size_t invalidFieldIndex = static_cast<size_t>(-1);
+
+ // Datetime can be represent at most five fields, such as
+ // 1. year
+ // 2. month
+ // 3. day-of-month
+ // 4. hour
+ // 5. minute
+ // 6. second
+ // 7. millisecond
+ // 8. AM/PM
+ static const int maximumNumberOfFields = 8;
+
+ DateTimeEditElement(Document*, EditControlOwner&);
+
+ DateTimeFieldElement* fieldAt(size_t) const;
+ size_t fieldIndexOf(const DateTimeFieldElement&) const;
+ DateTimeFieldElement* focusedField() const;
+ size_t focusedFieldIndex() const;
+ bool focusOnNextFocusableField(size_t startIndex);
+ bool isDisabled() const;
+ bool isReadOnly() const;
+ void layout(const LayoutParameters&, const DateComponents&);
+ void updateUIState();
+
+ // Element function.
+ virtual PassRefPtr<RenderStyle> customStyleForRenderer() OVERRIDE;
+
+ // DateTimeFieldElement::FieldOwner functions.
+ virtual void didBlurFromField() OVERRIDE FINAL;
+ virtual void didFocusOnField() OVERRIDE FINAL;
+ virtual void fieldValueChanged() OVERRIDE FINAL;
+ virtual bool focusOnNextField(const DateTimeFieldElement&) OVERRIDE FINAL;
+ virtual bool focusOnPreviousField(const DateTimeFieldElement&) OVERRIDE FINAL;
+ virtual bool isFieldOwnerDisabled() const OVERRIDE FINAL;
+ virtual bool isFieldOwnerReadOnly() const OVERRIDE FINAL;
+ virtual AtomicString localeIdentifier() const OVERRIDE FINAL;
+
+ Vector<DateTimeFieldElement*, maximumNumberOfFields> m_fields;
+ EditControlOwner* m_editControlOwner;
+};
+
+} // namespace WebCore
+
+#endif
+#endif
diff --git a/Source/core/html/shadow/DateTimeFieldElement.cpp b/Source/core/html/shadow/DateTimeFieldElement.cpp
new file mode 100644
index 0000000..46e0868
--- /dev/null
+++ b/Source/core/html/shadow/DateTimeFieldElement.cpp
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
+#include "core/html/shadow/DateTimeFieldElement.h"
+
+#include "HTMLNames.h"
+#include "core/dom/KeyboardEvent.h"
+#include "core/dom/Text.h"
+#include "core/platform/DateComponents.h"
+#include "core/platform/LocalizedStrings.h"
+#include "core/platform/text/PlatformLocale.h"
+#include "core/rendering/RenderObject.h"
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+DateTimeFieldElement::FieldOwner::~FieldOwner()
+{
+}
+
+DateTimeFieldElement::DateTimeFieldElement(Document* document, FieldOwner& fieldOwner)
+ : HTMLSpanElement(spanTag, document)
+ , m_fieldOwner(&fieldOwner)
+{
+ // On accessibility, DateTimeFieldElement acts like spin button.
+ setAttribute(roleAttr, "spinbutton");
+ setAttribute(aria_valuetextAttr, AXDateTimeFieldEmptyValueText());
+}
+
+void DateTimeFieldElement::defaultEventHandler(Event* event)
+{
+ if (event->type() == eventNames().blurEvent)
+ didBlur();
+
+ if (event->type() == eventNames().focusEvent)
+ didFocus();
+
+ if (event->isKeyboardEvent()) {
+ KeyboardEvent* keyboardEvent = static_cast<KeyboardEvent*>(event);
+ if (!isDisabled() && !isFieldOwnerDisabled() && !isFieldOwnerReadOnly()) {
+ handleKeyboardEvent(keyboardEvent);
+ if (keyboardEvent->defaultHandled())
+ return;
+ }
+ defaultKeyboardEventHandler(keyboardEvent);
+ if (keyboardEvent->defaultHandled())
+ return;
+ }
+
+ HTMLElement::defaultEventHandler(event);
+}
+
+void DateTimeFieldElement::defaultKeyboardEventHandler(KeyboardEvent* keyboardEvent)
+{
+ if (keyboardEvent->type() != eventNames().keydownEvent)
+ return;
+
+ if (isDisabled() || isFieldOwnerDisabled())
+ return;
+
+ const String& keyIdentifier = keyboardEvent->keyIdentifier();
+
+ if (keyIdentifier == "Left") {
+ if (!m_fieldOwner)
+ return;
+ // FIXME: We'd like to use FocusController::advanceFocus(FocusDirectionLeft, ...)
+ // but it doesn't work for shadow nodes. webkit.org/b/104650
+ if (!localeForOwner().isRTL() && m_fieldOwner->focusOnPreviousField(*this))
+ keyboardEvent->setDefaultHandled();
+ return;
+ }
+
+ if (keyIdentifier == "Right") {
+ if (!m_fieldOwner)
+ return;
+ // FIXME: We'd like to use FocusController::advanceFocus(FocusDirectionRight, ...)
+ // but it doesn't work for shadow nodes. webkit.org/b/104650
+ if (!localeForOwner().isRTL() && m_fieldOwner->focusOnNextField(*this))
+ keyboardEvent->setDefaultHandled();
+ return;
+ }
+
+ if (isFieldOwnerReadOnly())
+ return;
+
+ if (keyIdentifier == "Down") {
+ if (keyboardEvent->getModifierState("Alt"))
+ return;
+ keyboardEvent->setDefaultHandled();
+ stepDown();
+ return;
+ }
+
+ if (keyIdentifier == "Up") {
+ keyboardEvent->setDefaultHandled();
+ stepUp();
+ return;
+ }
+
+ if (keyIdentifier == "U+0008" || keyIdentifier == "U+007F") {
+ keyboardEvent->setDefaultHandled();
+ setEmptyValue(DispatchEvent);
+ return;
+ }
+}
+
+void DateTimeFieldElement::didBlur()
+{
+ if (m_fieldOwner)
+ m_fieldOwner->didBlurFromField();
+}
+
+void DateTimeFieldElement::didFocus()
+{
+ if (m_fieldOwner)
+ m_fieldOwner->didFocusOnField();
+}
+
+void DateTimeFieldElement::focusOnNextField()
+{
+ if (!m_fieldOwner)
+ return;
+ m_fieldOwner->focusOnNextField(*this);
+}
+
+void DateTimeFieldElement::initialize(const AtomicString& pseudo, const String& axHelpText, int axMinimum, int axMaximum)
+{
+ setAttribute(aria_helpAttr, axHelpText);
+ setAttribute(aria_valueminAttr, String::number(axMinimum));
+ setAttribute(aria_valuemaxAttr, String::number(axMaximum));
+ setPseudo(pseudo);
+ appendChild(Text::create(document(), visibleValue()));
+}
+
+bool DateTimeFieldElement::isDateTimeFieldElement() const
+{
+ return true;
+}
+
+bool DateTimeFieldElement::isFieldOwnerDisabled() const
+{
+ return m_fieldOwner && m_fieldOwner->isFieldOwnerDisabled();
+}
+
+bool DateTimeFieldElement::isFieldOwnerReadOnly() const
+{
+ return m_fieldOwner && m_fieldOwner->isFieldOwnerReadOnly();
+}
+
+bool DateTimeFieldElement::isFocusable() const
+{
+ if (isDisabled())
+ return false;
+ if (isFieldOwnerDisabled())
+ return false;
+ return HTMLElement::isFocusable();
+}
+
+bool DateTimeFieldElement::isDisabled() const
+{
+ return fastHasAttribute(disabledAttr);
+}
+
+Locale& DateTimeFieldElement::localeForOwner() const
+{
+ return document()->getCachedLocale(localeIdentifier());
+}
+
+AtomicString DateTimeFieldElement::localeIdentifier() const
+{
+ return m_fieldOwner ? m_fieldOwner->localeIdentifier() : nullAtom;
+}
+
+float DateTimeFieldElement::maximumWidth(const Font&)
+{
+ const float paddingLeftAndRight = 2; // This should match to html.css.
+ return paddingLeftAndRight;
+}
+
+void DateTimeFieldElement::setDisabled()
+{
+ // Set HTML attribute disabled to change apperance.
+ setBooleanAttribute(disabledAttr, true);
+ setNeedsStyleRecalc();
+}
+
+bool DateTimeFieldElement::supportsFocus() const
+{
+ return true;
+}
+
+void DateTimeFieldElement::updateVisibleValue(EventBehavior eventBehavior)
+{
+ Text* const textNode = toText(firstChild());
+ const String newVisibleValue = visibleValue();
+ ASSERT(newVisibleValue.length() > 0);
+
+ if (textNode->wholeText() == newVisibleValue)
+ return;
+
+ textNode->replaceWholeText(newVisibleValue, ASSERT_NO_EXCEPTION);
+ if (hasValue()) {
+ setAttribute(aria_valuetextAttr, newVisibleValue);
+ setAttribute(aria_valuenowAttr, String::number(valueForARIAValueNow()));
+ } else {
+ setAttribute(aria_valuetextAttr, AXDateTimeFieldEmptyValueText());
+ removeAttribute(aria_valuenowAttr);
+ }
+
+ if (eventBehavior == DispatchEvent && m_fieldOwner)
+ m_fieldOwner->fieldValueChanged();
+}
+
+int DateTimeFieldElement::valueForARIAValueNow() const
+{
+ return valueAsInteger();
+}
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/core/html/shadow/DateTimeFieldElement.h b/Source/core/html/shadow/DateTimeFieldElement.h
new file mode 100644
index 0000000..0b0118d
--- /dev/null
+++ b/Source/core/html/shadow/DateTimeFieldElement.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef DateTimeFieldElement_h
+#define DateTimeFieldElement_h
+
+#if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
+#include "core/html/HTMLDivElement.h"
+#include "core/html/HTMLSpanElement.h"
+
+namespace WebCore {
+
+class DateComponents;
+class DateTimeFieldsState;
+class Font;
+
+// DateTimeFieldElement is base class of date time field element.
+class DateTimeFieldElement : public HTMLSpanElement {
+ WTF_MAKE_NONCOPYABLE(DateTimeFieldElement);
+
+public:
+ enum EventBehavior {
+ DispatchNoEvent,
+ DispatchEvent,
+ };
+
+ // FieldOwner implementer must call removeEventHandler when
+ // it doesn't handle event, e.g. at destruction.
+ class FieldOwner {
+ public:
+ virtual ~FieldOwner();
+ virtual void didBlurFromField() = 0;
+ virtual void didFocusOnField() = 0;
+ virtual void fieldValueChanged() = 0;
+ virtual bool focusOnNextField(const DateTimeFieldElement&) = 0;
+ virtual bool focusOnPreviousField(const DateTimeFieldElement&) = 0;
+ virtual bool isFieldOwnerDisabled() const = 0;
+ virtual bool isFieldOwnerReadOnly() const = 0;
+ virtual AtomicString localeIdentifier() const = 0;
+ };
+
+ virtual void defaultEventHandler(Event*) OVERRIDE;
+ virtual bool hasValue() const = 0;
+ bool isDisabled() const;
+ virtual bool isFocusable() const OVERRIDE FINAL;
+ virtual float maximumWidth(const Font&);
+ virtual void populateDateTimeFieldsState(DateTimeFieldsState&) = 0;
+ void removeEventHandler() { m_fieldOwner = 0; }
+ void setDisabled();
+ virtual void setEmptyValue(EventBehavior = DispatchNoEvent) = 0;
+ virtual void setValueAsDate(const DateComponents&) = 0;
+ virtual void setValueAsDateTimeFieldsState(const DateTimeFieldsState&) = 0;
+ virtual void setValueAsInteger(int, EventBehavior = DispatchNoEvent) = 0;
+ virtual void stepDown() = 0;
+ virtual void stepUp() = 0;
+ virtual String value() const = 0;
+ virtual String visibleValue() const = 0;
+
+protected:
+ DateTimeFieldElement(Document*, FieldOwner&);
+ virtual void didBlur();
+ virtual void didFocus();
+ void focusOnNextField();
+ virtual void handleKeyboardEvent(KeyboardEvent*) = 0;
+ void initialize(const AtomicString& pseudo, const String& axHelpText, int axMinimum, int axMaximum);
+ Locale& localeForOwner() const;
+ AtomicString localeIdentifier() const;
+ void updateVisibleValue(EventBehavior);
+ virtual int valueAsInteger() const = 0;
+ virtual int valueForARIAValueNow() const;
+
+private:
+ void defaultKeyboardEventHandler(KeyboardEvent*);
+ virtual bool isDateTimeFieldElement() const OVERRIDE;
+ bool isFieldOwnerDisabled() const;
+ bool isFieldOwnerReadOnly() const;
+ virtual bool supportsFocus() const OVERRIDE FINAL;
+
+ FieldOwner* m_fieldOwner;
+};
+
+} // namespace WebCore
+
+#endif
+#endif
diff --git a/Source/core/html/shadow/DateTimeFieldElements.cpp b/Source/core/html/shadow/DateTimeFieldElements.cpp
new file mode 100644
index 0000000..d7aff17
--- /dev/null
+++ b/Source/core/html/shadow/DateTimeFieldElements.cpp
@@ -0,0 +1,635 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
+#include "core/html/shadow/DateTimeFieldElements.h"
+
+#include "core/html/DateTimeFieldsState.h"
+#include "core/platform/DateComponents.h"
+#include "core/platform/LocalizedStrings.h"
+#include <wtf/CurrentTime.h>
+#include <wtf/DateMath.h>
+
+namespace WebCore {
+
+DateTimeAMPMFieldElement::DateTimeAMPMFieldElement(Document* document, FieldOwner& fieldOwner, const Vector<String>& ampmLabels)
+ : DateTimeSymbolicFieldElement(document, fieldOwner, ampmLabels, 0, 1)
+{
+}
+
+PassRefPtr<DateTimeAMPMFieldElement> DateTimeAMPMFieldElement::create(Document* document, FieldOwner& fieldOwner, const Vector<String>& ampmLabels)
+{
+ DEFINE_STATIC_LOCAL(AtomicString, ampmPsuedoId, ("-webkit-datetime-edit-ampm-field", AtomicString::ConstructFromLiteral));
+ RefPtr<DateTimeAMPMFieldElement> field = adoptRef(new DateTimeAMPMFieldElement(document, fieldOwner, ampmLabels));
+ field->initialize(ampmPsuedoId, AXAMPMFieldText());
+ return field.release();
+}
+
+void DateTimeAMPMFieldElement::populateDateTimeFieldsState(DateTimeFieldsState& dateTimeFieldsState)
+{
+ if (hasValue())
+ dateTimeFieldsState.setAMPM(valueAsInteger() ? DateTimeFieldsState::AMPMValuePM : DateTimeFieldsState::AMPMValueAM);
+ else
+ dateTimeFieldsState.setAMPM(DateTimeFieldsState::AMPMValueEmpty);
+}
+
+void DateTimeAMPMFieldElement::setValueAsDate(const DateComponents& date)
+{
+ setValueAsInteger(date.hour() >= 12 ? 1 : 0);
+}
+
+void DateTimeAMPMFieldElement::setValueAsDateTimeFieldsState(const DateTimeFieldsState& dateTimeFieldsState)
+{
+ if (dateTimeFieldsState.hasAMPM())
+ setValueAsInteger(dateTimeFieldsState.ampm());
+ else
+ setEmptyValue();
+}
+
+// ----------------------------
+
+DateTimeDayFieldElement::DateTimeDayFieldElement(Document* document, FieldOwner& fieldOwner, const String& placeholder, const Range& range)
+ : DateTimeNumericFieldElement(document, fieldOwner, range, Range(1, 31), placeholder)
+{
+}
+
+PassRefPtr<DateTimeDayFieldElement> DateTimeDayFieldElement::create(Document* document, FieldOwner& fieldOwner, const String& placeholder, const Range& range)
+{
+ DEFINE_STATIC_LOCAL(AtomicString, dayPsuedoId, ("-webkit-datetime-edit-day-field", AtomicString::ConstructFromLiteral));
+ RefPtr<DateTimeDayFieldElement> field = adoptRef(new DateTimeDayFieldElement(document, fieldOwner, placeholder.isEmpty() ? ASCIILiteral("--") : placeholder, range));
+ field->initialize(dayPsuedoId, AXDayOfMonthFieldText());
+ return field.release();
+}
+
+void DateTimeDayFieldElement::populateDateTimeFieldsState(DateTimeFieldsState& dateTimeFieldsState)
+{
+ dateTimeFieldsState.setDayOfMonth(hasValue() ? valueAsInteger() : DateTimeFieldsState::emptyValue);
+}
+
+void DateTimeDayFieldElement::setValueAsDate(const DateComponents& date)
+{
+ setValueAsInteger(date.monthDay());
+}
+
+void DateTimeDayFieldElement::setValueAsDateTimeFieldsState(const DateTimeFieldsState& dateTimeFieldsState)
+{
+ if (!dateTimeFieldsState.hasDayOfMonth()) {
+ setEmptyValue();
+ return;
+ }
+
+ const unsigned value = dateTimeFieldsState.dayOfMonth();
+ if (range().isInRange(static_cast<int>(value))) {
+ setValueAsInteger(value);
+ return;
+ }
+
+ setEmptyValue();
+}
+
+// ----------------------------
+
+DateTimeHourFieldElementBase::DateTimeHourFieldElementBase(Document* document, FieldOwner& fieldOwner, const Range& range, const Range& hardLimits, const Step& step)
+ : DateTimeNumericFieldElement(document, fieldOwner, range, hardLimits, "--", step)
+{
+}
+
+void DateTimeHourFieldElementBase::initialize()
+{
+ DEFINE_STATIC_LOCAL(AtomicString, hourPsuedoId, ("-webkit-datetime-edit-hour-field", AtomicString::ConstructFromLiteral));
+ DateTimeNumericFieldElement::initialize(hourPsuedoId, AXHourFieldText());
+}
+
+void DateTimeHourFieldElementBase::setValueAsDate(const DateComponents& date)
+{
+ setValueAsInteger(date.hour());
+}
+
+void DateTimeHourFieldElementBase::setValueAsDateTimeFieldsState(const DateTimeFieldsState& dateTimeFieldsState)
+{
+ if (!dateTimeFieldsState.hasHour()) {
+ setEmptyValue();
+ return;
+ }
+
+ const int hour12 = dateTimeFieldsState.hour();
+ if (hour12 < 1 || hour12 > 12) {
+ setEmptyValue();
+ return;
+ }
+
+ if (dateTimeFieldsState.ampm() == DateTimeFieldsState::AMPMValuePM)
+ setValueAsInteger((hour12 + 12) % 24);
+ else
+ setValueAsInteger(hour12 % 12);
+}
+// ----------------------------
+
+DateTimeHour11FieldElement::DateTimeHour11FieldElement(Document* document, FieldOwner& fieldOwner, const Range& range, const Step& step)
+ : DateTimeHourFieldElementBase(document, fieldOwner, range, Range(0, 11), step)
+{
+}
+
+PassRefPtr<DateTimeHour11FieldElement> DateTimeHour11FieldElement::create(Document* document, FieldOwner& fieldOwner, const Range& hour23Range, const Step& step)
+{
+ ASSERT(hour23Range.minimum >= 0);
+ ASSERT(hour23Range.maximum <= 23);
+ ASSERT(hour23Range.minimum <= hour23Range.maximum);
+ Range range(0, 11);
+ if (hour23Range.maximum < 12)
+ range = hour23Range;
+ else if (hour23Range.minimum >= 12) {
+ range.minimum = hour23Range.minimum - 12;
+ range.maximum = hour23Range.maximum - 12;
+ }
+
+ RefPtr<DateTimeHour11FieldElement> field = adoptRef(new DateTimeHour11FieldElement(document, fieldOwner, range, step));
+ field->initialize();
+ return field.release();
+}
+
+void DateTimeHour11FieldElement::populateDateTimeFieldsState(DateTimeFieldsState& dateTimeFieldsState)
+{
+ if (!hasValue()) {
+ dateTimeFieldsState.setHour(DateTimeFieldsState::emptyValue);
+ return;
+ }
+ const int value = valueAsInteger();
+ dateTimeFieldsState.setHour(value ? value : 12);
+}
+
+void DateTimeHour11FieldElement::setValueAsInteger(int value, EventBehavior eventBehavior)
+{
+ value = Range(0, 23).clampValue(value) % 12;
+ DateTimeNumericFieldElement::setValueAsInteger(value, eventBehavior);
+}
+
+// ----------------------------
+
+DateTimeHour12FieldElement::DateTimeHour12FieldElement(Document* document, FieldOwner& fieldOwner, const Range& range, const Step& step)
+ : DateTimeHourFieldElementBase(document, fieldOwner, range, Range(1, 12), step)
+{
+}
+
+PassRefPtr<DateTimeHour12FieldElement> DateTimeHour12FieldElement::create(Document* document, FieldOwner& fieldOwner, const Range& hour23Range, const Step& step)
+{
+ ASSERT(hour23Range.minimum >= 0);
+ ASSERT(hour23Range.maximum <= 23);
+ ASSERT(hour23Range.minimum <= hour23Range.maximum);
+ Range range(1, 12);
+ if (hour23Range.maximum < 12)
+ range = hour23Range;
+ else if (hour23Range.minimum >= 12) {
+ range.minimum = hour23Range.minimum - 12;
+ range.maximum = hour23Range.maximum - 12;
+ }
+ if (!range.minimum)
+ range.minimum = 12;
+ if (!range.maximum)
+ range.maximum = 12;
+ if (range.minimum > range.maximum) {
+ range.minimum = 1;
+ range.maximum = 12;
+ }
+ RefPtr<DateTimeHour12FieldElement> field = adoptRef(new DateTimeHour12FieldElement(document, fieldOwner, range, step));
+ field->initialize();
+ return field.release();
+}
+
+void DateTimeHour12FieldElement::populateDateTimeFieldsState(DateTimeFieldsState& dateTimeFieldsState)
+{
+ dateTimeFieldsState.setHour(hasValue() ? valueAsInteger() : DateTimeFieldsState::emptyValue);
+}
+
+void DateTimeHour12FieldElement::setValueAsInteger(int value, EventBehavior eventBehavior)
+{
+ value = Range(0, 24).clampValue(value) % 12;
+ DateTimeNumericFieldElement::setValueAsInteger(value ? value : 12, eventBehavior);
+}
+
+// ----------------------------
+
+DateTimeHour23FieldElement::DateTimeHour23FieldElement(Document* document, FieldOwner& fieldOwner, const Range& range, const Step& step)
+ : DateTimeHourFieldElementBase(document, fieldOwner, range, Range(0, 23), step)
+{
+}
+
+PassRefPtr<DateTimeHour23FieldElement> DateTimeHour23FieldElement::create(Document* document, FieldOwner& fieldOwner, const Range& hour23Range, const Step& step)
+{
+ ASSERT(hour23Range.minimum >= 0);
+ ASSERT(hour23Range.maximum <= 23);
+ ASSERT(hour23Range.minimum <= hour23Range.maximum);
+ RefPtr<DateTimeHour23FieldElement> field = adoptRef(new DateTimeHour23FieldElement(document, fieldOwner, hour23Range, step));
+ field->initialize();
+ return field.release();
+}
+
+void DateTimeHour23FieldElement::populateDateTimeFieldsState(DateTimeFieldsState& dateTimeFieldsState)
+{
+ if (!hasValue()) {
+ dateTimeFieldsState.setHour(DateTimeFieldsState::emptyValue);
+ return;
+ }
+
+ const int value = valueAsInteger();
+
+ dateTimeFieldsState.setHour(value ? value % 12 : 12);
+ dateTimeFieldsState.setAMPM(value >= 12 ? DateTimeFieldsState::AMPMValuePM : DateTimeFieldsState::AMPMValueAM);
+}
+
+void DateTimeHour23FieldElement::setValueAsInteger(int value, EventBehavior eventBehavior)
+{
+ value = Range(0, 23).clampValue(value);
+ DateTimeNumericFieldElement::setValueAsInteger(value, eventBehavior);
+}
+
+// ----------------------------
+
+DateTimeHour24FieldElement::DateTimeHour24FieldElement(Document* document, FieldOwner& fieldOwner, const Range& range, const Step& step)
+ : DateTimeHourFieldElementBase(document, fieldOwner, range, Range(1, 24), step)
+{
+}
+
+PassRefPtr<DateTimeHour24FieldElement> DateTimeHour24FieldElement::create(Document* document, FieldOwner& fieldOwner, const Range& hour23Range, const Step& step)
+{
+ ASSERT(hour23Range.minimum >= 0);
+ ASSERT(hour23Range.maximum <= 23);
+ ASSERT(hour23Range.minimum <= hour23Range.maximum);
+ Range range(hour23Range.minimum ? hour23Range.minimum : 24, hour23Range.maximum ? hour23Range.maximum : 24);
+ if (range.minimum > range.maximum) {
+ range.minimum = 1;
+ range.maximum = 24;
+ }
+
+ RefPtr<DateTimeHour24FieldElement> field = adoptRef(new DateTimeHour24FieldElement(document, fieldOwner, range, step));
+ field->initialize();
+ return field.release();
+}
+
+void DateTimeHour24FieldElement::populateDateTimeFieldsState(DateTimeFieldsState& dateTimeFieldsState)
+{
+ if (!hasValue()) {
+ dateTimeFieldsState.setHour(DateTimeFieldsState::emptyValue);
+ return;
+ }
+
+ const int value = valueAsInteger();
+
+ if (value == 24) {
+ dateTimeFieldsState.setHour(12);
+ dateTimeFieldsState.setAMPM(DateTimeFieldsState::AMPMValueAM);
+ } else {
+ dateTimeFieldsState.setHour(value == 12 ? 12 : value % 12);
+ dateTimeFieldsState.setAMPM(value >= 12 ? DateTimeFieldsState::AMPMValuePM : DateTimeFieldsState::AMPMValueAM);
+ }
+}
+
+void DateTimeHour24FieldElement::setValueAsInteger(int value, EventBehavior eventBehavior)
+{
+ value = Range(0, 24).clampValue(value);
+ DateTimeNumericFieldElement::setValueAsInteger(value ? value : 24, eventBehavior);
+}
+
+// ----------------------------
+
+DateTimeMillisecondFieldElement::DateTimeMillisecondFieldElement(Document* document, FieldOwner& fieldOwner, const Range& range, const Step& step)
+ : DateTimeNumericFieldElement(document, fieldOwner, range, Range(0, 999), "---", step)
+{
+}
+
+PassRefPtr<DateTimeMillisecondFieldElement> DateTimeMillisecondFieldElement::create(Document* document, FieldOwner& fieldOwner, const Range& range, const Step& step)
+{
+ DEFINE_STATIC_LOCAL(AtomicString, millisecondPsuedoId, ("-webkit-datetime-edit-millisecond-field", AtomicString::ConstructFromLiteral));
+ RefPtr<DateTimeMillisecondFieldElement> field = adoptRef(new DateTimeMillisecondFieldElement(document, fieldOwner, range, step));
+ field->initialize(millisecondPsuedoId, AXMillisecondFieldText());
+ return field.release();
+}
+
+void DateTimeMillisecondFieldElement::populateDateTimeFieldsState(DateTimeFieldsState& dateTimeFieldsState)
+{
+ dateTimeFieldsState.setMillisecond(hasValue() ? valueAsInteger() : DateTimeFieldsState::emptyValue);
+}
+
+void DateTimeMillisecondFieldElement::setValueAsDate(const DateComponents& date)
+{
+ setValueAsInteger(date.millisecond());
+}
+
+void DateTimeMillisecondFieldElement::setValueAsDateTimeFieldsState(const DateTimeFieldsState& dateTimeFieldsState)
+{
+ if (!dateTimeFieldsState.hasMillisecond()) {
+ setEmptyValue();
+ return;
+ }
+
+ const unsigned value = dateTimeFieldsState.millisecond();
+ if (value > static_cast<unsigned>(maximum())) {
+ setEmptyValue();
+ return;
+ }
+
+ setValueAsInteger(value);
+}
+
+// ----------------------------
+
+DateTimeMinuteFieldElement::DateTimeMinuteFieldElement(Document* document, FieldOwner& fieldOwner, const Range& range, const Step& step)
+ : DateTimeNumericFieldElement(document, fieldOwner, range, Range(0, 59), "--", step)
+{
+}
+
+PassRefPtr<DateTimeMinuteFieldElement> DateTimeMinuteFieldElement::create(Document* document, FieldOwner& fieldOwner, const Range& range, const Step& step)
+{
+ DEFINE_STATIC_LOCAL(AtomicString, minutePsuedoId, ("-webkit-datetime-edit-minute-field", AtomicString::ConstructFromLiteral));
+ RefPtr<DateTimeMinuteFieldElement> field = adoptRef(new DateTimeMinuteFieldElement(document, fieldOwner, range, step));
+ field->initialize(minutePsuedoId, AXMinuteFieldText());
+ return field.release();
+}
+
+void DateTimeMinuteFieldElement::populateDateTimeFieldsState(DateTimeFieldsState& dateTimeFieldsState)
+{
+ dateTimeFieldsState.setMinute(hasValue() ? valueAsInteger() : DateTimeFieldsState::emptyValue);
+}
+
+void DateTimeMinuteFieldElement::setValueAsDate(const DateComponents& date)
+{
+ setValueAsInteger(date.minute());
+}
+
+void DateTimeMinuteFieldElement::setValueAsDateTimeFieldsState(const DateTimeFieldsState& dateTimeFieldsState)
+{
+ if (!dateTimeFieldsState.hasMinute()) {
+ setEmptyValue();
+ return;
+ }
+
+ const unsigned value = dateTimeFieldsState.minute();
+ if (value > static_cast<unsigned>(maximum())) {
+ setEmptyValue();
+ return;
+ }
+
+ setValueAsInteger(value);
+}
+
+// ----------------------------
+
+DateTimeMonthFieldElement::DateTimeMonthFieldElement(Document* document, FieldOwner& fieldOwner, const String& placeholder, const Range& range)
+ : DateTimeNumericFieldElement(document, fieldOwner, range, Range(1, 12), placeholder)
+{
+}
+
+PassRefPtr<DateTimeMonthFieldElement> DateTimeMonthFieldElement::create(Document* document, FieldOwner& fieldOwner, const String& placeholder, const Range& range)
+{
+ DEFINE_STATIC_LOCAL(AtomicString, monthPsuedoId, ("-webkit-datetime-edit-month-field", AtomicString::ConstructFromLiteral));
+ RefPtr<DateTimeMonthFieldElement> field = adoptRef(new DateTimeMonthFieldElement(document, fieldOwner, placeholder.isEmpty() ? ASCIILiteral("--") : placeholder, range));
+ field->initialize(monthPsuedoId, AXMonthFieldText());
+ return field.release();
+}
+
+void DateTimeMonthFieldElement::populateDateTimeFieldsState(DateTimeFieldsState& dateTimeFieldsState)
+{
+ dateTimeFieldsState.setMonth(hasValue() ? valueAsInteger() : DateTimeFieldsState::emptyValue);
+}
+
+void DateTimeMonthFieldElement::setValueAsDate(const DateComponents& date)
+{
+ setValueAsInteger(date.month() + 1);
+}
+
+void DateTimeMonthFieldElement::setValueAsDateTimeFieldsState(const DateTimeFieldsState& dateTimeFieldsState)
+{
+ if (!dateTimeFieldsState.hasMonth()) {
+ setEmptyValue();
+ return;
+ }
+
+ const unsigned value = dateTimeFieldsState.month();
+ if (range().isInRange(static_cast<int>(value))) {
+ setValueAsInteger(value);
+ return;
+ }
+
+ setEmptyValue();
+}
+
+// ----------------------------
+
+DateTimeSecondFieldElement::DateTimeSecondFieldElement(Document* document, FieldOwner& fieldOwner, const Range& range, const Step& step)
+ : DateTimeNumericFieldElement(document, fieldOwner, range, Range(0, 59), "--", step)
+{
+}
+
+PassRefPtr<DateTimeSecondFieldElement> DateTimeSecondFieldElement::create(Document* document, FieldOwner& fieldOwner, const Range& range, const Step& step)
+{
+ DEFINE_STATIC_LOCAL(AtomicString, secondPsuedoId, ("-webkit-datetime-edit-second-field", AtomicString::ConstructFromLiteral));
+ RefPtr<DateTimeSecondFieldElement> field = adoptRef(new DateTimeSecondFieldElement(document, fieldOwner, range, step));
+ field->initialize(secondPsuedoId, AXSecondFieldText());
+ return field.release();
+}
+
+void DateTimeSecondFieldElement::populateDateTimeFieldsState(DateTimeFieldsState& dateTimeFieldsState)
+{
+ dateTimeFieldsState.setSecond(hasValue() ? valueAsInteger() : DateTimeFieldsState::emptyValue);
+}
+
+void DateTimeSecondFieldElement::setValueAsDate(const DateComponents& date)
+{
+ setValueAsInteger(date.second());
+}
+
+void DateTimeSecondFieldElement::setValueAsDateTimeFieldsState(const DateTimeFieldsState& dateTimeFieldsState)
+{
+ if (!dateTimeFieldsState.hasSecond()) {
+ setEmptyValue();
+ return;
+ }
+
+ const unsigned value = dateTimeFieldsState.second();
+ if (value > static_cast<unsigned>(maximum())) {
+ setEmptyValue();
+ return;
+ }
+
+ setValueAsInteger(value);
+}
+
+// ----------------------------
+
+DateTimeSymbolicMonthFieldElement::DateTimeSymbolicMonthFieldElement(Document* document, FieldOwner& fieldOwner, const Vector<String>& labels, int minimum, int maximum)
+ : DateTimeSymbolicFieldElement(document, fieldOwner, labels, minimum, maximum)
+{
+}
+
+PassRefPtr<DateTimeSymbolicMonthFieldElement> DateTimeSymbolicMonthFieldElement::create(Document* document, FieldOwner& fieldOwner, const Vector<String>& labels, int minimum, int maximum)
+{
+ DEFINE_STATIC_LOCAL(AtomicString, monthPsuedoId, ("-webkit-datetime-edit-month-field", AtomicString::ConstructFromLiteral));
+ RefPtr<DateTimeSymbolicMonthFieldElement> field = adoptRef(new DateTimeSymbolicMonthFieldElement(document, fieldOwner, labels, minimum, maximum));
+ field->initialize(monthPsuedoId, AXMonthFieldText());
+ return field.release();
+}
+
+void DateTimeSymbolicMonthFieldElement::populateDateTimeFieldsState(DateTimeFieldsState& dateTimeFieldsState)
+{
+ if (!hasValue())
+ dateTimeFieldsState.setMonth(DateTimeFieldsState::emptyValue);
+ ASSERT(valueAsInteger() < static_cast<int>(symbolsSize()));
+ dateTimeFieldsState.setMonth(valueAsInteger() + 1);
+}
+
+void DateTimeSymbolicMonthFieldElement::setValueAsDate(const DateComponents& date)
+{
+ setValueAsInteger(date.month());
+}
+
+void DateTimeSymbolicMonthFieldElement::setValueAsDateTimeFieldsState(const DateTimeFieldsState& dateTimeFieldsState)
+{
+ if (!dateTimeFieldsState.hasMonth()) {
+ setEmptyValue();
+ return;
+ }
+
+ const unsigned value = dateTimeFieldsState.month() - 1;
+ if (value >= symbolsSize()) {
+ setEmptyValue();
+ return;
+ }
+
+ setValueAsInteger(value);
+}
+
+// ----------------------------
+
+DateTimeWeekFieldElement::DateTimeWeekFieldElement(Document* document, FieldOwner& fieldOwner, const Range& range)
+ : DateTimeNumericFieldElement(document, fieldOwner, range, Range(DateComponents::minimumWeekNumber, DateComponents::maximumWeekNumber), "--")
+{
+}
+
+PassRefPtr<DateTimeWeekFieldElement> DateTimeWeekFieldElement::create(Document* document, FieldOwner& fieldOwner, const Range& range)
+{
+ DEFINE_STATIC_LOCAL(AtomicString, weekPsuedoId, ("-webkit-datetime-edit-week-field", AtomicString::ConstructFromLiteral));
+ RefPtr<DateTimeWeekFieldElement> field = adoptRef(new DateTimeWeekFieldElement(document, fieldOwner, range));
+ field->initialize(weekPsuedoId, AXWeekOfYearFieldText());
+ return field.release();
+}
+
+void DateTimeWeekFieldElement::populateDateTimeFieldsState(DateTimeFieldsState& dateTimeFieldsState)
+{
+ dateTimeFieldsState.setWeekOfYear(hasValue() ? valueAsInteger() : DateTimeFieldsState::emptyValue);
+}
+
+void DateTimeWeekFieldElement::setValueAsDate(const DateComponents& date)
+{
+ setValueAsInteger(date.week());
+}
+
+void DateTimeWeekFieldElement::setValueAsDateTimeFieldsState(const DateTimeFieldsState& dateTimeFieldsState)
+{
+ if (!dateTimeFieldsState.hasWeekOfYear()) {
+ setEmptyValue();
+ return;
+ }
+
+ const unsigned value = dateTimeFieldsState.weekOfYear();
+ if (range().isInRange(static_cast<int>(value))) {
+ setValueAsInteger(value);
+ return;
+ }
+
+ setEmptyValue();
+}
+
+// ----------------------------
+
+DateTimeYearFieldElement::DateTimeYearFieldElement(Document* document, FieldOwner& fieldOwner, const DateTimeYearFieldElement::Parameters& parameters)
+ : DateTimeNumericFieldElement(document, fieldOwner, Range(parameters.minimumYear, parameters.maximumYear), Range(DateComponents::minimumYear(), DateComponents::maximumYear()), parameters.placeholder.isEmpty() ? ASCIILiteral("----") : parameters.placeholder)
+ , m_minIsSpecified(parameters.minIsSpecified)
+ , m_maxIsSpecified(parameters.maxIsSpecified)
+{
+ ASSERT(parameters.minimumYear >= DateComponents::minimumYear());
+ ASSERT(parameters.maximumYear <= DateComponents::maximumYear());
+}
+
+PassRefPtr<DateTimeYearFieldElement> DateTimeYearFieldElement::create(Document* document, FieldOwner& fieldOwner, const DateTimeYearFieldElement::Parameters& parameters)
+{
+ DEFINE_STATIC_LOCAL(AtomicString, yearPsuedoId, ("-webkit-datetime-edit-year-field", AtomicString::ConstructFromLiteral));
+ RefPtr<DateTimeYearFieldElement> field = adoptRef(new DateTimeYearFieldElement(document, fieldOwner, parameters));
+ field->initialize(yearPsuedoId, AXYearFieldText());
+ return field.release();
+}
+
+static int currentFullYear()
+{
+ double current = currentTimeMS();
+ double utcOffset = calculateUTCOffset();
+ double dstOffset = calculateDSTOffset(current, utcOffset);
+ int offset = static_cast<int>((utcOffset + dstOffset) / msPerMinute);
+ current += offset * msPerMinute;
+
+ DateComponents date;
+ date.setMillisecondsSinceEpochForMonth(current);
+ return date.fullYear();
+}
+
+int DateTimeYearFieldElement::defaultValueForStepDown() const
+{
+ return m_maxIsSpecified ? DateTimeNumericFieldElement::defaultValueForStepDown() : currentFullYear();
+}
+
+int DateTimeYearFieldElement::defaultValueForStepUp() const
+{
+ return m_minIsSpecified ? DateTimeNumericFieldElement::defaultValueForStepUp() : currentFullYear();
+}
+
+void DateTimeYearFieldElement::populateDateTimeFieldsState(DateTimeFieldsState& dateTimeFieldsState)
+{
+ dateTimeFieldsState.setYear(hasValue() ? valueAsInteger() : DateTimeFieldsState::emptyValue);
+}
+
+void DateTimeYearFieldElement::setValueAsDate(const DateComponents& date)
+{
+ setValueAsInteger(date.fullYear());
+}
+
+void DateTimeYearFieldElement::setValueAsDateTimeFieldsState(const DateTimeFieldsState& dateTimeFieldsState)
+{
+ if (!dateTimeFieldsState.hasYear()) {
+ setEmptyValue();
+ return;
+ }
+
+ const unsigned value = dateTimeFieldsState.year();
+ if (range().isInRange(static_cast<int>(value))) {
+ setValueAsInteger(value);
+ return;
+ }
+
+ setEmptyValue();
+}
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/core/html/shadow/DateTimeFieldElements.h b/Source/core/html/shadow/DateTimeFieldElements.h
new file mode 100644
index 0000000..60f53a8
--- /dev/null
+++ b/Source/core/html/shadow/DateTimeFieldElements.h
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef DateTimeFieldElements_h
+#define DateTimeFieldElements_h
+
+#if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
+#include "core/html/shadow/DateTimeNumericFieldElement.h"
+#include "core/html/shadow/DateTimeSymbolicFieldElement.h"
+
+namespace WebCore {
+
+class DateTimeAMPMFieldElement FINAL : public DateTimeSymbolicFieldElement {
+ WTF_MAKE_NONCOPYABLE(DateTimeAMPMFieldElement);
+
+public:
+ static PassRefPtr<DateTimeAMPMFieldElement> create(Document*, FieldOwner&, const Vector<String>&);
+
+private:
+ DateTimeAMPMFieldElement(Document*, FieldOwner&, const Vector<String>&);
+
+ // DateTimeFieldElement functions.
+ virtual void populateDateTimeFieldsState(DateTimeFieldsState&) OVERRIDE FINAL;
+ virtual void setValueAsDate(const DateComponents&) OVERRIDE FINAL;
+ virtual void setValueAsDateTimeFieldsState(const DateTimeFieldsState&) OVERRIDE FINAL;
+};
+
+class DateTimeDayFieldElement FINAL : public DateTimeNumericFieldElement {
+ WTF_MAKE_NONCOPYABLE(DateTimeDayFieldElement);
+
+public:
+ static PassRefPtr<DateTimeDayFieldElement> create(Document*, FieldOwner&, const String& placeholder, const Range&);
+
+private:
+ DateTimeDayFieldElement(Document*, FieldOwner&, const String& placeholder, const Range&);
+
+ // DateTimeFieldElement functions.
+ virtual void populateDateTimeFieldsState(DateTimeFieldsState&) OVERRIDE FINAL;
+ virtual void setValueAsDate(const DateComponents&) OVERRIDE FINAL;
+ virtual void setValueAsDateTimeFieldsState(const DateTimeFieldsState&) OVERRIDE FINAL;
+};
+
+class DateTimeHourFieldElementBase : public DateTimeNumericFieldElement {
+ WTF_MAKE_NONCOPYABLE(DateTimeHourFieldElementBase);
+
+protected:
+ DateTimeHourFieldElementBase(Document*, FieldOwner&, const Range&, const Range& hardLimits, const Step&);
+ void initialize();
+
+private:
+ // DateTimeFieldElement functions.
+ virtual void setValueAsDate(const DateComponents&) OVERRIDE FINAL;
+ virtual void setValueAsDateTimeFieldsState(const DateTimeFieldsState&) OVERRIDE FINAL;
+};
+
+class DateTimeHour11FieldElement FINAL : public DateTimeHourFieldElementBase {
+ WTF_MAKE_NONCOPYABLE(DateTimeHour11FieldElement);
+
+public:
+ static PassRefPtr<DateTimeHour11FieldElement> create(Document*, FieldOwner&, const Range&, const Step&);
+
+private:
+ DateTimeHour11FieldElement(Document*, FieldOwner&, const Range& hour23Range, const Step&);
+
+ // DateTimeFieldElement functions.
+ virtual void populateDateTimeFieldsState(DateTimeFieldsState&) OVERRIDE FINAL;
+ virtual void setValueAsInteger(int, EventBehavior = DispatchNoEvent) OVERRIDE FINAL;
+};
+
+class DateTimeHour12FieldElement FINAL : public DateTimeHourFieldElementBase {
+ WTF_MAKE_NONCOPYABLE(DateTimeHour12FieldElement);
+
+public:
+ static PassRefPtr<DateTimeHour12FieldElement> create(Document*, FieldOwner&, const Range&, const Step&);
+
+private:
+ DateTimeHour12FieldElement(Document*, FieldOwner&, const Range& hour23Range, const Step&);
+
+ // DateTimeFieldElement functions.
+ virtual void populateDateTimeFieldsState(DateTimeFieldsState&) OVERRIDE FINAL;
+ virtual void setValueAsInteger(int, EventBehavior = DispatchNoEvent) OVERRIDE FINAL;
+};
+
+class DateTimeHour23FieldElement FINAL : public DateTimeHourFieldElementBase {
+ WTF_MAKE_NONCOPYABLE(DateTimeHour23FieldElement);
+
+public:
+ static PassRefPtr<DateTimeHour23FieldElement> create(Document*, FieldOwner&, const Range&, const Step&);
+
+private:
+ DateTimeHour23FieldElement(Document*, FieldOwner&, const Range& hour23Range, const Step&);
+
+ // DateTimeFieldElement functions.
+ virtual void populateDateTimeFieldsState(DateTimeFieldsState&) OVERRIDE FINAL;
+ virtual void setValueAsInteger(int, EventBehavior = DispatchNoEvent) OVERRIDE FINAL;
+};
+
+class DateTimeHour24FieldElement FINAL : public DateTimeHourFieldElementBase {
+ WTF_MAKE_NONCOPYABLE(DateTimeHour24FieldElement);
+
+public:
+ static PassRefPtr<DateTimeHour24FieldElement> create(Document*, FieldOwner&, const Range&, const Step&);
+
+private:
+ DateTimeHour24FieldElement(Document*, FieldOwner&, const Range& hour23Range, const Step&);
+
+ // DateTimeFieldElement functions.
+ virtual void populateDateTimeFieldsState(DateTimeFieldsState&) OVERRIDE FINAL;
+ virtual void setValueAsInteger(int, EventBehavior = DispatchNoEvent) OVERRIDE FINAL;
+};
+
+class DateTimeMillisecondFieldElement FINAL : public DateTimeNumericFieldElement {
+ WTF_MAKE_NONCOPYABLE(DateTimeMillisecondFieldElement);
+
+public:
+ static PassRefPtr<DateTimeMillisecondFieldElement> create(Document*, FieldOwner&, const Range&, const Step&);
+
+private:
+ DateTimeMillisecondFieldElement(Document*, FieldOwner&, const Range&, const Step&);
+
+ // DateTimeFieldElement functions.
+ virtual void populateDateTimeFieldsState(DateTimeFieldsState&) OVERRIDE FINAL;
+ virtual void setValueAsDate(const DateComponents&) OVERRIDE FINAL;
+ virtual void setValueAsDateTimeFieldsState(const DateTimeFieldsState&) OVERRIDE FINAL;
+};
+
+class DateTimeMinuteFieldElement FINAL : public DateTimeNumericFieldElement {
+ WTF_MAKE_NONCOPYABLE(DateTimeMinuteFieldElement);
+
+public:
+ static PassRefPtr<DateTimeMinuteFieldElement> create(Document*, FieldOwner&, const Range&, const Step&);
+
+private:
+ DateTimeMinuteFieldElement(Document*, FieldOwner&, const Range&, const Step&);
+
+ // DateTimeFieldElement functions.
+ virtual void populateDateTimeFieldsState(DateTimeFieldsState&) OVERRIDE FINAL;
+ virtual void setValueAsDate(const DateComponents&) OVERRIDE FINAL;
+ virtual void setValueAsDateTimeFieldsState(const DateTimeFieldsState&) OVERRIDE FINAL;
+};
+
+class DateTimeMonthFieldElement FINAL : public DateTimeNumericFieldElement {
+ WTF_MAKE_NONCOPYABLE(DateTimeMonthFieldElement);
+
+public:
+ static PassRefPtr<DateTimeMonthFieldElement> create(Document*, FieldOwner&, const String& placeholder, const Range&);
+
+private:
+ DateTimeMonthFieldElement(Document*, FieldOwner&, const String& placeholder, const Range&);
+
+ // DateTimeFieldElement functions.
+ virtual void populateDateTimeFieldsState(DateTimeFieldsState&) OVERRIDE FINAL;
+ virtual void setValueAsDate(const DateComponents&) OVERRIDE FINAL;
+ virtual void setValueAsDateTimeFieldsState(const DateTimeFieldsState&) OVERRIDE FINAL;
+};
+
+class DateTimeSecondFieldElement FINAL : public DateTimeNumericFieldElement {
+ WTF_MAKE_NONCOPYABLE(DateTimeSecondFieldElement);
+
+public:
+ static PassRefPtr<DateTimeSecondFieldElement> create(Document*, FieldOwner&, const Range&, const Step&);
+
+private:
+ DateTimeSecondFieldElement(Document*, FieldOwner&, const Range&, const Step&);
+
+ // DateTimeFieldElement functions.
+ virtual void populateDateTimeFieldsState(DateTimeFieldsState&) OVERRIDE FINAL;
+ virtual void setValueAsDate(const DateComponents&) OVERRIDE FINAL;
+ virtual void setValueAsDateTimeFieldsState(const DateTimeFieldsState&) OVERRIDE FINAL;
+};
+
+class DateTimeSymbolicMonthFieldElement FINAL : public DateTimeSymbolicFieldElement {
+ WTF_MAKE_NONCOPYABLE(DateTimeSymbolicMonthFieldElement);
+
+public:
+ static PassRefPtr<DateTimeSymbolicMonthFieldElement> create(Document*, FieldOwner&, const Vector<String>&, int minimum, int maximum);
+
+private:
+ DateTimeSymbolicMonthFieldElement(Document*, FieldOwner&, const Vector<String>&, int minimum, int maximum);
+
+ // DateTimeFieldElement functions.
+ virtual void populateDateTimeFieldsState(DateTimeFieldsState&) OVERRIDE FINAL;
+ virtual void setValueAsDate(const DateComponents&) OVERRIDE FINAL;
+ virtual void setValueAsDateTimeFieldsState(const DateTimeFieldsState&) OVERRIDE FINAL;
+};
+
+class DateTimeWeekFieldElement FINAL : public DateTimeNumericFieldElement {
+ WTF_MAKE_NONCOPYABLE(DateTimeWeekFieldElement);
+
+public:
+ static PassRefPtr<DateTimeWeekFieldElement> create(Document*, FieldOwner&, const Range&);
+
+private:
+ DateTimeWeekFieldElement(Document*, FieldOwner&, const Range&);
+
+ // DateTimeFieldElement functions.
+ virtual void populateDateTimeFieldsState(DateTimeFieldsState&) OVERRIDE FINAL;
+ virtual void setValueAsDate(const DateComponents&) OVERRIDE FINAL;
+ virtual void setValueAsDateTimeFieldsState(const DateTimeFieldsState&) OVERRIDE FINAL;
+};
+
+class DateTimeYearFieldElement FINAL : public DateTimeNumericFieldElement {
+ WTF_MAKE_NONCOPYABLE(DateTimeYearFieldElement);
+
+public:
+ struct Parameters {
+ int minimumYear;
+ int maximumYear;
+ bool minIsSpecified;
+ bool maxIsSpecified;
+ String placeholder;
+
+ Parameters()
+ : minimumYear(-1)
+ , maximumYear(-1)
+ , minIsSpecified(false)
+ , maxIsSpecified(false)
+ {
+ }
+ };
+
+ static PassRefPtr<DateTimeYearFieldElement> create(Document*, FieldOwner&, const Parameters&);
+
+private:
+ DateTimeYearFieldElement(Document*, FieldOwner&, const Parameters&);
+
+ // DateTimeFieldElement functions.
+ virtual void populateDateTimeFieldsState(DateTimeFieldsState&) OVERRIDE FINAL;
+ virtual void setValueAsDate(const DateComponents&) OVERRIDE FINAL;
+ virtual void setValueAsDateTimeFieldsState(const DateTimeFieldsState&) OVERRIDE FINAL;
+
+ // DateTimeNumericFieldElement functions.
+ virtual int defaultValueForStepDown() const OVERRIDE FINAL;
+ virtual int defaultValueForStepUp() const OVERRIDE FINAL;
+
+ bool m_minIsSpecified;
+ bool m_maxIsSpecified;
+};
+
+} // namespace WebCore
+
+#endif
+#endif
diff --git a/Source/core/html/shadow/DateTimeNumericFieldElement.cpp b/Source/core/html/shadow/DateTimeNumericFieldElement.cpp
new file mode 100644
index 0000000..0b9e174
--- /dev/null
+++ b/Source/core/html/shadow/DateTimeNumericFieldElement.cpp
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
+#include "core/html/shadow/DateTimeNumericFieldElement.h"
+
+#include "CSSPropertyNames.h"
+#include "CSSValueKeywords.h"
+#include "core/dom/KeyboardEvent.h"
+#include "core/platform/graphics/Font.h"
+#include "core/platform/text/PlatformLocale.h"
+#include <wtf/text/StringBuilder.h>
+
+using namespace WTF::Unicode;
+
+namespace WebCore {
+
+static const DOMTimeStamp typeAheadTimeout = 1000;
+
+int DateTimeNumericFieldElement::Range::clampValue(int value) const
+{
+ return std::min(std::max(value, minimum), maximum);
+}
+
+bool DateTimeNumericFieldElement::Range::isInRange(int value) const
+{
+ return value >= minimum && value <= maximum;
+}
+
+// ----------------------------
+
+DateTimeNumericFieldElement::DateTimeNumericFieldElement(Document* document, FieldOwner& fieldOwner, const Range& range, const Range& hardLimits, const String& placeholder, const DateTimeNumericFieldElement::Step& step)
+ : DateTimeFieldElement(document, fieldOwner)
+ , m_lastDigitCharTime(0)
+ , m_placeholder(placeholder)
+ , m_range(range)
+ , m_hardLimits(hardLimits)
+ , m_step(step)
+ , m_value(0)
+ , m_hasValue(false)
+{
+ ASSERT(m_step.step);
+ ASSERT(m_range.minimum <= m_range.maximum);
+ ASSERT(m_hardLimits.minimum <= m_hardLimits.maximum);
+
+ // We show a direction-neutral string such as "--" as a placeholder. It
+ // should follow the direction of numeric values.
+ if (localeForOwner().isRTL()) {
+ Direction dir = direction(formatValue(this->maximum())[0]);
+ if (dir == LeftToRight || dir == EuropeanNumber || dir == ArabicNumber) {
+ setInlineStyleProperty(CSSPropertyUnicodeBidi, CSSValueBidiOverride);
+ setInlineStyleProperty(CSSPropertyDirection, CSSValueLtr);
+ }
+ }
+}
+
+float DateTimeNumericFieldElement::maximumWidth(const Font& font)
+{
+ float maximumWidth = font.width(m_placeholder);
+ maximumWidth = std::max(maximumWidth, font.width(formatValue(maximum())));
+ maximumWidth = std::max(maximumWidth, font.width(value()));
+ return maximumWidth + DateTimeFieldElement::maximumWidth(font);
+}
+
+int DateTimeNumericFieldElement::defaultValueForStepDown() const
+{
+ return m_range.maximum;
+}
+
+int DateTimeNumericFieldElement::defaultValueForStepUp() const
+{
+ return m_range.minimum;
+}
+
+void DateTimeNumericFieldElement::didBlur()
+{
+ int value = typeAheadValue();
+ m_typeAheadBuffer.clear();
+ if (value >= 0)
+ setValueAsInteger(value, DispatchEvent);
+ DateTimeFieldElement::didBlur();
+}
+
+String DateTimeNumericFieldElement::formatValue(int value) const
+{
+ Locale& locale = localeForOwner();
+ if (m_hardLimits.maximum > 999)
+ return locale.convertToLocalizedNumber(String::format("%04d", value));
+ if (m_hardLimits.maximum > 99)
+ return locale.convertToLocalizedNumber(String::format("%03d", value));
+ return locale.convertToLocalizedNumber(String::format("%02d", value));
+}
+
+void DateTimeNumericFieldElement::handleKeyboardEvent(KeyboardEvent* keyboardEvent)
+{
+ ASSERT(!isDisabled());
+ if (keyboardEvent->type() != eventNames().keypressEvent)
+ return;
+
+ UChar charCode = static_cast<UChar>(keyboardEvent->charCode());
+ String number = localeForOwner().convertFromLocalizedNumber(String(&charCode, 1));
+ const int digit = number[0] - '0';
+ if (digit < 0 || digit > 9)
+ return;
+
+ DOMTimeStamp delta = keyboardEvent->timeStamp() - m_lastDigitCharTime;
+ m_lastDigitCharTime = keyboardEvent->timeStamp();
+
+ if (delta > typeAheadTimeout)
+ m_typeAheadBuffer.clear();
+ m_typeAheadBuffer.append(number);
+
+ int newValue = typeAheadValue();
+ if (newValue >= m_hardLimits.minimum)
+ setValueAsInteger(newValue, DispatchEvent);
+ else {
+ m_hasValue = false;
+ updateVisibleValue(DispatchEvent);
+ }
+
+ if (m_typeAheadBuffer.length() >= DateTimeNumericFieldElement::formatValue(m_range.maximum).length() || newValue * 10 > m_range.maximum)
+ focusOnNextField();
+
+ keyboardEvent->setDefaultHandled();
+}
+
+bool DateTimeNumericFieldElement::hasValue() const
+{
+ return m_hasValue;
+}
+
+void DateTimeNumericFieldElement::initialize(const AtomicString& pseudo, const String& axHelpText)
+{
+ DateTimeFieldElement::initialize(pseudo, axHelpText, m_range.minimum, m_range.maximum);
+}
+
+int DateTimeNumericFieldElement::maximum() const
+{
+ return m_range.maximum;
+}
+
+void DateTimeNumericFieldElement::setEmptyValue(EventBehavior eventBehavior)
+{
+ if (isDisabled())
+ return;
+
+ m_hasValue = false;
+ m_value = 0;
+ m_typeAheadBuffer.clear();
+ updateVisibleValue(eventBehavior);
+}
+
+void DateTimeNumericFieldElement::setValueAsInteger(int value, EventBehavior eventBehavior)
+{
+ m_value = m_hardLimits.clampValue(value);
+ m_hasValue = true;
+ updateVisibleValue(eventBehavior);
+}
+
+void DateTimeNumericFieldElement::stepDown()
+{
+ int newValue = roundDown(m_hasValue ? m_value - 1 : defaultValueForStepDown());
+ if (!m_range.isInRange(newValue))
+ newValue = roundDown(m_range.maximum);
+ m_typeAheadBuffer.clear();
+ setValueAsInteger(newValue, DispatchEvent);
+}
+
+void DateTimeNumericFieldElement::stepUp()
+{
+ int newValue = roundUp(m_hasValue ? m_value + 1 : defaultValueForStepUp());
+ if (!m_range.isInRange(newValue))
+ newValue = roundUp(m_range.minimum);
+ m_typeAheadBuffer.clear();
+ setValueAsInteger(newValue, DispatchEvent);
+}
+
+String DateTimeNumericFieldElement::value() const
+{
+ return m_hasValue ? formatValue(m_value) : emptyString();
+}
+
+int DateTimeNumericFieldElement::valueAsInteger() const
+{
+ return m_hasValue ? m_value : -1;
+}
+
+int DateTimeNumericFieldElement::typeAheadValue() const
+{
+ if (m_typeAheadBuffer.length())
+ return m_typeAheadBuffer.toString().toInt();
+ return -1;
+}
+
+String DateTimeNumericFieldElement::visibleValue() const
+{
+ if (m_typeAheadBuffer.length())
+ return formatValue(typeAheadValue());
+ return m_hasValue ? value() : m_placeholder;
+}
+
+int DateTimeNumericFieldElement::roundDown(int n) const
+{
+ n -= m_step.stepBase;
+ if (n >= 0)
+ n = n / m_step.step * m_step.step;
+ else
+ n = -((-n + m_step.step - 1) / m_step.step * m_step.step);
+ return n + m_step.stepBase;
+}
+
+int DateTimeNumericFieldElement::roundUp(int n) const
+{
+ n -= m_step.stepBase;
+ if (n >= 0)
+ n = (n + m_step.step - 1) / m_step.step * m_step.step;
+ else
+ n = -(-n / m_step.step * m_step.step);
+ return n + m_step.stepBase;
+}
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/core/html/shadow/DateTimeNumericFieldElement.h b/Source/core/html/shadow/DateTimeNumericFieldElement.h
new file mode 100644
index 0000000..80390ee
--- /dev/null
+++ b/Source/core/html/shadow/DateTimeNumericFieldElement.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef DateTimeNumericFieldElement_h
+#define DateTimeNumericFieldElement_h
+
+#if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
+#include "core/html/shadow/DateTimeFieldElement.h"
+
+namespace WebCore {
+
+// DateTimeNumericFieldElement represents numeric field of date time format,
+// such as:
+// - hour
+// - minute
+// - millisecond
+// - second
+// - year
+class DateTimeNumericFieldElement : public DateTimeFieldElement {
+ WTF_MAKE_NONCOPYABLE(DateTimeNumericFieldElement);
+
+public:
+ struct Step {
+ Step(int step = 1, int stepBase = 0) : step(step), stepBase(stepBase) { }
+ int step;
+ int stepBase;
+ };
+
+ struct Range {
+ Range(int minimum, int maximum) : minimum(minimum), maximum(maximum) { }
+ int clampValue(int) const;
+ bool isInRange(int) const;
+ bool isSingleton() const { return minimum == maximum; }
+
+ int minimum;
+ int maximum;
+ };
+
+protected:
+ DateTimeNumericFieldElement(Document*, FieldOwner&, const Range&, const Range& hardLimits, const String& placeholder, const Step& = Step());
+
+ int clampValue(int value) const { return m_range.clampValue(value); }
+ virtual int defaultValueForStepDown() const;
+ virtual int defaultValueForStepUp() const;
+ const Range& range() const { return m_range; }
+
+ // DateTimeFieldElement functions.
+ virtual bool hasValue() const OVERRIDE FINAL;
+ void initialize(const AtomicString& pseudo, const String& axHelpText);
+ int maximum() const;
+ virtual void setEmptyValue(EventBehavior = DispatchNoEvent) OVERRIDE FINAL;
+ virtual void setValueAsInteger(int, EventBehavior = DispatchNoEvent) OVERRIDE;
+ virtual int valueAsInteger() const OVERRIDE FINAL;
+ virtual String visibleValue() const OVERRIDE FINAL;
+
+private:
+ // DateTimeFieldElement functions.
+ virtual void didBlur() OVERRIDE FINAL;
+ virtual void handleKeyboardEvent(KeyboardEvent*) OVERRIDE FINAL;
+ virtual float maximumWidth(const Font&) OVERRIDE;
+ virtual void stepDown() OVERRIDE FINAL;
+ virtual void stepUp() OVERRIDE FINAL;
+ virtual String value() const OVERRIDE FINAL;
+
+ String formatValue(int) const;
+ int roundUp(int) const;
+ int roundDown(int) const;
+ int typeAheadValue() const;
+
+ DOMTimeStamp m_lastDigitCharTime;
+ const String m_placeholder;
+ const Range m_range;
+ const Range m_hardLimits;
+ const Step m_step;
+ int m_value;
+ bool m_hasValue;
+ mutable StringBuilder m_typeAheadBuffer;
+};
+
+} // namespace WebCore
+
+#endif
+#endif
diff --git a/Source/core/html/shadow/DateTimeSymbolicFieldElement.cpp b/Source/core/html/shadow/DateTimeSymbolicFieldElement.cpp
new file mode 100644
index 0000000..d2ff82d
--- /dev/null
+++ b/Source/core/html/shadow/DateTimeSymbolicFieldElement.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
+#include "core/html/shadow/DateTimeSymbolicFieldElement.h"
+
+#include "core/dom/KeyboardEvent.h"
+#include "core/platform/graphics/Font.h"
+#include "core/platform/text/TextBreakIterator.h"
+#include <wtf/text/StringBuilder.h>
+#include <wtf/unicode/Unicode.h>
+
+namespace WebCore {
+
+static AtomicString makeVisibleEmptyValue(const Vector<String>& symbols)
+{
+ unsigned maximumLength = 0;
+ for (unsigned index = 0; index < symbols.size(); ++index)
+ maximumLength = std::max(maximumLength, numGraphemeClusters(symbols[index]));
+ StringBuilder builder;
+ builder.reserveCapacity(maximumLength);
+ for (unsigned length = 0; length < maximumLength; ++length)
+ builder.append('-');
+ return builder.toAtomicString();
+}
+
+DateTimeSymbolicFieldElement::DateTimeSymbolicFieldElement(Document* document, FieldOwner& fieldOwner, const Vector<String>& symbols, int minimum, int maximum)
+ : DateTimeFieldElement(document, fieldOwner)
+ , m_symbols(symbols)
+ , m_visibleEmptyValue(makeVisibleEmptyValue(symbols))
+ , m_selectedIndex(-1)
+ , m_typeAhead(this)
+ , m_minimumIndex(minimum)
+ , m_maximumIndex(maximum)
+{
+ ASSERT(!symbols.isEmpty());
+ ASSERT(m_minimumIndex >= 0);
+ ASSERT_WITH_SECURITY_IMPLICATION(m_maximumIndex < static_cast<int>(m_symbols.size()));
+ ASSERT(m_minimumIndex <= m_maximumIndex);
+}
+
+float DateTimeSymbolicFieldElement::maximumWidth(const Font& font)
+{
+ float maximumWidth = font.width(visibleEmptyValue());
+ for (unsigned index = 0; index < m_symbols.size(); ++index)
+ maximumWidth = std::max(maximumWidth, font.width(m_symbols[index]));
+ return maximumWidth + DateTimeFieldElement::maximumWidth(font);
+}
+
+void DateTimeSymbolicFieldElement::handleKeyboardEvent(KeyboardEvent* keyboardEvent)
+{
+ if (keyboardEvent->type() != eventNames().keypressEvent)
+ return;
+
+ const UChar charCode = WTF::Unicode::toLower(keyboardEvent->charCode());
+ if (charCode < ' ')
+ return;
+
+ keyboardEvent->setDefaultHandled();
+
+ int index = m_typeAhead.handleEvent(keyboardEvent, TypeAhead::MatchPrefix | TypeAhead::CycleFirstChar | TypeAhead::MatchIndex);
+ if (index < 0)
+ return;
+ setValueAsInteger(index, DispatchEvent);
+}
+
+bool DateTimeSymbolicFieldElement::hasValue() const
+{
+ return m_selectedIndex >= 0;
+}
+
+void DateTimeSymbolicFieldElement::initialize(const AtomicString& pseudo, const String& axHelpText)
+{
+ // The minimum and maximum below are exposed to users, and 1-based numbers
+ // are natural for symbolic fields. For example, the minimum value of a
+ // month field should be 1, not 0.
+ DateTimeFieldElement::initialize(pseudo, axHelpText, m_minimumIndex + 1, m_maximumIndex + 1);
+}
+
+void DateTimeSymbolicFieldElement::setEmptyValue(EventBehavior eventBehavior)
+{
+ if (isDisabled())
+ return;
+ m_selectedIndex = invalidIndex;
+ updateVisibleValue(eventBehavior);
+}
+
+void DateTimeSymbolicFieldElement::setValueAsInteger(int newSelectedIndex, EventBehavior eventBehavior)
+{
+ m_selectedIndex = std::max(0, std::min(newSelectedIndex, static_cast<int>(m_symbols.size() - 1)));
+ updateVisibleValue(eventBehavior);
+}
+
+void DateTimeSymbolicFieldElement::stepDown()
+{
+ if (hasValue()) {
+ if (!indexIsInRange(--m_selectedIndex))
+ m_selectedIndex = m_maximumIndex;
+ } else
+ m_selectedIndex = m_maximumIndex;
+ updateVisibleValue(DispatchEvent);
+}
+
+void DateTimeSymbolicFieldElement::stepUp()
+{
+ if (hasValue()) {
+ if (!indexIsInRange(++m_selectedIndex))
+ m_selectedIndex = m_minimumIndex;
+ } else
+ m_selectedIndex = m_minimumIndex;
+ updateVisibleValue(DispatchEvent);
+}
+
+String DateTimeSymbolicFieldElement::value() const
+{
+ return hasValue() ? m_symbols[m_selectedIndex] : emptyString();
+}
+
+int DateTimeSymbolicFieldElement::valueAsInteger() const
+{
+ return m_selectedIndex;
+}
+
+int DateTimeSymbolicFieldElement::valueForARIAValueNow() const
+{
+ // Synchronize with minimum/maximum adjustment in initialize().
+ return m_selectedIndex + 1;
+}
+
+String DateTimeSymbolicFieldElement::visibleEmptyValue() const
+{
+ return m_visibleEmptyValue;
+}
+
+String DateTimeSymbolicFieldElement::visibleValue() const
+{
+ return hasValue() ? m_symbols[m_selectedIndex] : visibleEmptyValue();
+}
+
+int DateTimeSymbolicFieldElement::indexOfSelectedOption() const
+{
+ return m_selectedIndex;
+}
+
+int DateTimeSymbolicFieldElement::optionCount() const
+{
+ return m_symbols.size();
+}
+
+String DateTimeSymbolicFieldElement::optionAtIndex(int index) const
+{
+ return m_symbols[index];
+}
+
+} // namespace WebCore
+
+#endif
diff --git a/Source/core/html/shadow/DateTimeSymbolicFieldElement.h b/Source/core/html/shadow/DateTimeSymbolicFieldElement.h
new file mode 100644
index 0000000..321fa34
--- /dev/null
+++ b/Source/core/html/shadow/DateTimeSymbolicFieldElement.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef DateTimeSymbolicFieldElement_h
+#define DateTimeSymbolicFieldElement_h
+
+#if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
+#include "core/html/TypeAhead.h"
+#include "core/html/shadow/DateTimeFieldElement.h"
+
+namespace WebCore {
+
+// DateTimeSymbolicFieldElement represents non-numeric field of data time
+// format, such as: AM/PM, and month.
+class DateTimeSymbolicFieldElement : public DateTimeFieldElement, public TypeAheadDataSource {
+ WTF_MAKE_NONCOPYABLE(DateTimeSymbolicFieldElement);
+
+protected:
+ DateTimeSymbolicFieldElement(Document*, FieldOwner&, const Vector<String>&, int minimum, int maximum);
+ size_t symbolsSize() const { return m_symbols.size(); }
+ virtual bool hasValue() const OVERRIDE FINAL;
+ void initialize(const AtomicString& pseudo, const String& axHelpText);
+ virtual void setEmptyValue(EventBehavior = DispatchNoEvent) OVERRIDE FINAL;
+ virtual void setValueAsInteger(int, EventBehavior = DispatchNoEvent) OVERRIDE FINAL;
+ virtual int valueAsInteger() const OVERRIDE FINAL;
+
+private:
+ static const int invalidIndex = -1;
+
+ String visibleEmptyValue() const;
+ bool indexIsInRange(int index) const { return index >= m_minimumIndex && index <= m_maximumIndex; }
+
+ // DateTimeFieldElement functions.
+ virtual void handleKeyboardEvent(KeyboardEvent*) OVERRIDE FINAL;
+ virtual float maximumWidth(const Font&) OVERRIDE;
+ virtual void stepDown() OVERRIDE FINAL;
+ virtual void stepUp() OVERRIDE FINAL;
+ virtual String value() const OVERRIDE FINAL;
+ virtual int valueForARIAValueNow() const OVERRIDE FINAL;
+ virtual String visibleValue() const OVERRIDE FINAL;
+
+ // TypeAheadDataSource functions.
+ virtual int indexOfSelectedOption() const OVERRIDE;
+ virtual int optionCount() const OVERRIDE;
+ virtual String optionAtIndex(int index) const OVERRIDE;
+
+ const Vector<String> m_symbols;
+
+ // We use AtomicString to share visible empty value among multiple
+ // DateTimeEditElements in the page.
+ const AtomicString m_visibleEmptyValue;
+ int m_selectedIndex;
+ TypeAhead m_typeAhead;
+ const int m_minimumIndex;
+ const int m_maximumIndex;
+};
+
+} // namespace WebCore
+
+#endif
+#endif
diff --git a/Source/core/html/shadow/DetailsMarkerControl.cpp b/Source/core/html/shadow/DetailsMarkerControl.cpp
new file mode 100644
index 0000000..e797072
--- /dev/null
+++ b/Source/core/html/shadow/DetailsMarkerControl.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "core/html/shadow/DetailsMarkerControl.h"
+
+#include "HTMLNames.h"
+#include "core/html/HTMLSummaryElement.h"
+#include "core/rendering/RenderDetailsMarker.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+DetailsMarkerControl::DetailsMarkerControl(Document* document)
+ : HTMLDivElement(divTag, document)
+{
+}
+
+RenderObject* DetailsMarkerControl::createRenderer(RenderArena* arena, RenderStyle*)
+{
+ return new (arena) RenderDetailsMarker(this);
+}
+
+bool DetailsMarkerControl::rendererIsNeeded(const NodeRenderingContext& context)
+{
+ return summaryElement()->isMainSummary() && HTMLDivElement::rendererIsNeeded(context);
+}
+
+const AtomicString& DetailsMarkerControl::shadowPseudoId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, pseudId, ("-webkit-details-marker", AtomicString::ConstructFromLiteral));
+ return pseudId;
+}
+
+HTMLSummaryElement* DetailsMarkerControl::summaryElement()
+{
+ Element* element = shadowHost();
+ ASSERT_WITH_SECURITY_IMPLICATION(!element || element->hasTagName(summaryTag));
+ return static_cast<HTMLSummaryElement*>(element);
+}
+
+}
diff --git a/Source/core/html/shadow/DetailsMarkerControl.h b/Source/core/html/shadow/DetailsMarkerControl.h
new file mode 100644
index 0000000..867f6f1
--- /dev/null
+++ b/Source/core/html/shadow/DetailsMarkerControl.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DetailsMarkerControl_h
+#define DetailsMarkerControl_h
+
+#include "core/html/HTMLDivElement.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class HTMLSummaryElement;
+
+class DetailsMarkerControl FINAL : public HTMLDivElement {
+public:
+ DetailsMarkerControl(Document*);
+ static PassRefPtr<DetailsMarkerControl> create(Document*);
+
+private:
+ virtual RenderObject* createRenderer(RenderArena*, RenderStyle*);
+ virtual bool rendererIsNeeded(const NodeRenderingContext&);
+ virtual const AtomicString& shadowPseudoId() const;
+
+ HTMLSummaryElement* summaryElement();
+};
+
+inline PassRefPtr<DetailsMarkerControl> DetailsMarkerControl::create(Document* document)
+{
+ return adoptRef(new DetailsMarkerControl(document));
+}
+
+}
+
+#endif
diff --git a/Source/core/html/shadow/HTMLContentElement.cpp b/Source/core/html/shadow/HTMLContentElement.cpp
new file mode 100644
index 0000000..90c50eb
--- /dev/null
+++ b/Source/core/html/shadow/HTMLContentElement.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "core/html/shadow/HTMLContentElement.h"
+
+#include "HTMLNames.h"
+#include "core/css/CSSParser.h"
+#include "core/dom/QualifiedName.h"
+#include "core/dom/ShadowRoot.h"
+#include "core/html/shadow/ContentDistributor.h"
+#include "core/html/shadow/ContentSelectorQuery.h"
+#include "RuntimeEnabledFeatures.h"
+#include <wtf/StdLibExtras.h>
+
+namespace WebCore {
+
+using HTMLNames::selectAttr;
+
+const QualifiedName& HTMLContentElement::contentTagName(Document*)
+{
+ return HTMLNames::contentTag;
+}
+
+PassRefPtr<HTMLContentElement> HTMLContentElement::create(Document* document)
+{
+ return adoptRef(new HTMLContentElement(contentTagName(document), document));
+}
+
+PassRefPtr<HTMLContentElement> HTMLContentElement::create(const QualifiedName& tagName, Document* document)
+{
+ return adoptRef(new HTMLContentElement(tagName, document));
+}
+
+HTMLContentElement::HTMLContentElement(const QualifiedName& name, Document* document)
+ : InsertionPoint(name, document)
+ , m_shouldParseSelectorList(false)
+ , m_isValidSelector(true)
+{
+ ScriptWrappable::init(this);
+}
+
+HTMLContentElement::~HTMLContentElement()
+{
+}
+
+InsertionPoint::MatchType HTMLContentElement::matchTypeFor(Node*)
+{
+ if (select().isNull() || select().isEmpty())
+ return AlwaysMatches;
+ if (!isSelectValid())
+ return NeverMatches;
+ return HasToMatchSelector;
+}
+
+const AtomicString& HTMLContentElement::select() const
+{
+ return getAttribute(selectAttr);
+}
+
+bool HTMLContentElement::isSelectValid()
+{
+ ensureSelectParsed();
+ return m_isValidSelector;
+}
+
+void HTMLContentElement::ensureSelectParsed()
+{
+ if (!m_shouldParseSelectorList)
+ return;
+
+ CSSParser parser(document());
+ parser.parseSelector(select(), m_selectorList);
+ m_shouldParseSelectorList = false;
+ m_isValidSelector = validateSelect();
+ if (!m_isValidSelector)
+ m_selectorList = CSSSelectorList();
+}
+
+void HTMLContentElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
+{
+ if (name == selectAttr) {
+ if (ShadowRoot* root = containingShadowRoot())
+ root->owner()->willAffectSelector();
+ m_shouldParseSelectorList = true;
+ } else
+ InsertionPoint::parseAttribute(name, value);
+}
+
+static bool validateSubSelector(const CSSSelector* selector)
+{
+ switch (selector->m_match) {
+ case CSSSelector::Tag:
+ case CSSSelector::Id:
+ case CSSSelector::Class:
+ case CSSSelector::Exact:
+ case CSSSelector::Set:
+ case CSSSelector::List:
+ case CSSSelector::Hyphen:
+ case CSSSelector::Contain:
+ case CSSSelector::Begin:
+ case CSSSelector::End:
+ return true;
+ case CSSSelector::PseudoElement:
+ return false;
+ case CSSSelector::PagePseudoClass:
+ case CSSSelector::PseudoClass:
+ break;
+ }
+
+ switch (selector->pseudoType()) {
+ case CSSSelector::PseudoEmpty:
+ case CSSSelector::PseudoLink:
+ case CSSSelector::PseudoVisited:
+ case CSSSelector::PseudoTarget:
+ case CSSSelector::PseudoEnabled:
+ case CSSSelector::PseudoDisabled:
+ case CSSSelector::PseudoChecked:
+ case CSSSelector::PseudoIndeterminate:
+ case CSSSelector::PseudoNthChild:
+ case CSSSelector::PseudoNthLastChild:
+ case CSSSelector::PseudoNthOfType:
+ case CSSSelector::PseudoNthLastOfType:
+ case CSSSelector::PseudoFirstChild:
+ case CSSSelector::PseudoLastChild:
+ case CSSSelector::PseudoFirstOfType:
+ case CSSSelector::PseudoLastOfType:
+ case CSSSelector::PseudoOnlyOfType:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool validateSelector(const CSSSelector* selector)
+{
+ ASSERT(selector);
+
+ if (!validateSubSelector(selector))
+ return false;
+
+ const CSSSelector* prevSubSelector = selector;
+ const CSSSelector* subSelector = selector->tagHistory();
+
+ while (subSelector) {
+ if (prevSubSelector->relation() != CSSSelector::SubSelector)
+ return false;
+ if (!validateSubSelector(subSelector))
+ return false;
+
+ prevSubSelector = subSelector;
+ subSelector = subSelector->tagHistory();
+ }
+
+ return true;
+}
+
+bool HTMLContentElement::validateSelect() const
+{
+ ASSERT(!m_shouldParseSelectorList);
+
+ if (select().isNull() || select().isEmpty())
+ return true;
+
+ if (!m_selectorList.isValid())
+ return false;
+
+ for (const CSSSelector* selector = m_selectorList.first(); selector; selector = m_selectorList.next(selector)) {
+ if (!validateSelector(selector))
+ return false;
+ }
+
+ return true;
+}
+}
+
diff --git a/Source/core/html/shadow/HTMLContentElement.h b/Source/core/html/shadow/HTMLContentElement.h
new file mode 100644
index 0000000..a03e00b
--- /dev/null
+++ b/Source/core/html/shadow/HTMLContentElement.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef HTMLContentElement_h
+#define HTMLContentElement_h
+
+#include "core/css/CSSSelectorList.h"
+#include "core/html/shadow/InsertionPoint.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class HTMLContentElement FINAL : public InsertionPoint {
+public:
+ static const QualifiedName& contentTagName(Document*);
+ static PassRefPtr<HTMLContentElement> create(const QualifiedName&, Document*);
+ static PassRefPtr<HTMLContentElement> create(Document*);
+
+ virtual ~HTMLContentElement();
+
+ void setSelect(const AtomicString&);
+ const AtomicString& select() const;
+
+ virtual MatchType matchTypeFor(Node*) OVERRIDE;
+ virtual const CSSSelectorList& selectorList() OVERRIDE;
+ virtual Type insertionPointType() const OVERRIDE { return ContentInsertionPoint; }
+ virtual bool canAffectSelector() const OVERRIDE { return true; }
+ virtual bool isSelectValid();
+
+protected:
+ HTMLContentElement(const QualifiedName&, Document*);
+
+private:
+ virtual void parseAttribute(const QualifiedName&, const AtomicString&) OVERRIDE;
+ void ensureSelectParsed();
+ bool validateSelect() const;
+
+ bool m_shouldParseSelectorList;
+ bool m_isValidSelector;
+ CSSSelectorList m_selectorList;
+};
+
+inline void HTMLContentElement::setSelect(const AtomicString& selectValue)
+{
+ setAttribute(HTMLNames::selectAttr, selectValue);
+ m_shouldParseSelectorList = true;
+}
+
+inline const CSSSelectorList& HTMLContentElement::selectorList()
+{
+ ensureSelectParsed();
+ return m_selectorList;
+}
+
+inline bool isHTMLContentElement(const Node* node)
+{
+ ASSERT(node);
+ return node->isInsertionPoint() && toInsertionPoint(node)->insertionPointType() == InsertionPoint::ContentInsertionPoint;
+}
+
+inline HTMLContentElement* toHTMLContentElement(Node* node)
+{
+ ASSERT_WITH_SECURITY_IMPLICATION(!node || isHTMLContentElement(node));
+ return static_cast<HTMLContentElement*>(node);
+}
+
+}
+
+#endif
diff --git a/Source/core/html/shadow/HTMLContentElement.idl b/Source/core/html/shadow/HTMLContentElement.idl
new file mode 100644
index 0000000..e5c20d8
--- /dev/null
+++ b/Source/core/html/shadow/HTMLContentElement.idl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+[
+ SkipVTableValidation
+] interface HTMLContentElement : HTMLElement {
+ [Reflect] attribute DOMString select;
+ attribute boolean resetStyleInheritance;
+ NodeList getDistributedNodes();
+};
diff --git a/Source/core/html/shadow/HTMLShadowElement.cpp b/Source/core/html/shadow/HTMLShadowElement.cpp
new file mode 100644
index 0000000..ad304ea
--- /dev/null
+++ b/Source/core/html/shadow/HTMLShadowElement.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "core/html/shadow/HTMLShadowElement.h"
+
+#include "HTMLNames.h"
+#include "core/dom/ShadowRoot.h"
+#include <wtf/text/AtomicString.h>
+
+namespace WebCore {
+
+class Document;
+
+inline HTMLShadowElement::HTMLShadowElement(const QualifiedName& tagName, Document* document)
+ : InsertionPoint(tagName, document)
+{
+ ASSERT(hasTagName(HTMLNames::shadowTag));
+ ScriptWrappable::init(this);
+}
+
+PassRefPtr<HTMLShadowElement> HTMLShadowElement::create(const QualifiedName& tagName, Document* document)
+{
+ return adoptRef(new HTMLShadowElement(tagName, document));
+}
+
+HTMLShadowElement::~HTMLShadowElement()
+{
+}
+
+ShadowRoot* HTMLShadowElement::olderShadowRoot()
+{
+ ShadowRoot* containingRoot = containingShadowRoot();
+ if (!containingRoot)
+ return 0;
+
+ ContentDistributor::ensureDistribution(containingRoot);
+
+ ShadowRoot* older = containingRoot->olderShadowRoot();
+ if (!older || older->type() != ShadowRoot::AuthorShadowRoot || ScopeContentDistribution::assignedTo(older) != this)
+ return 0;
+
+ return older;
+}
+
+} // namespace WebCore
+
diff --git a/Source/core/html/shadow/HTMLShadowElement.h b/Source/core/html/shadow/HTMLShadowElement.h
new file mode 100644
index 0000000..4ea0b66
--- /dev/null
+++ b/Source/core/html/shadow/HTMLShadowElement.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef HTMLShadowElement_h
+#define HTMLShadowElement_h
+
+#include "core/html/shadow/InsertionPoint.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class HTMLShadowElement FINAL : public InsertionPoint {
+public:
+ static PassRefPtr<HTMLShadowElement> create(const QualifiedName&, Document*);
+
+ virtual ~HTMLShadowElement();
+
+ virtual Type insertionPointType() const OVERRIDE { return ShadowInsertionPoint; }
+
+ ShadowRoot* olderShadowRoot();
+
+private:
+ HTMLShadowElement(const QualifiedName&, Document*);
+};
+
+inline bool isHTMLShadowElement(const Node* node)
+{
+ ASSERT(node);
+ return node->hasTagName(HTMLNames::shadowTag);
+}
+
+inline HTMLShadowElement* toHTMLShadowElement(Node* node)
+{
+ ASSERT_WITH_SECURITY_IMPLICATION(!node || isHTMLShadowElement(node));
+ return static_cast<HTMLShadowElement*>(node);
+}
+
+inline const HTMLShadowElement* toHTMLShadowElement(const Node* node)
+{
+ ASSERT_WITH_SECURITY_IMPLICATION(!node || isHTMLShadowElement(node));
+ return static_cast<const HTMLShadowElement*>(node);
+}
+} // namespace WebCore
+
+#endif // HTMLShadowElement_h
diff --git a/Source/core/html/shadow/HTMLShadowElement.idl b/Source/core/html/shadow/HTMLShadowElement.idl
new file mode 100644
index 0000000..a252e14
--- /dev/null
+++ b/Source/core/html/shadow/HTMLShadowElement.idl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+interface HTMLShadowElement : HTMLElement {
+ attribute boolean resetStyleInheritance;
+ readonly attribute ShadowRoot olderShadowRoot;
+};
diff --git a/Source/core/html/shadow/InsertionPoint.cpp b/Source/core/html/shadow/InsertionPoint.cpp
new file mode 100644
index 0000000..b3608d6
--- /dev/null
+++ b/Source/core/html/shadow/InsertionPoint.cpp
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "core/html/shadow/InsertionPoint.h"
+
+#include "HTMLNames.h"
+#include "core/dom/ElementShadow.h"
+#include "core/dom/QualifiedName.h"
+#include "core/dom/ShadowRoot.h"
+#include "core/dom/StaticNodeList.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+InsertionPoint::InsertionPoint(const QualifiedName& tagName, Document* document)
+ : HTMLElement(tagName, document, CreateInsertionPoint)
+ , m_registeredWithShadowRoot(false)
+{
+}
+
+InsertionPoint::~InsertionPoint()
+{
+}
+
+void InsertionPoint::attach()
+{
+ if (ShadowRoot* shadowRoot = containingShadowRoot())
+ ContentDistributor::ensureDistribution(shadowRoot);
+ for (size_t i = 0; i < m_distribution.size(); ++i) {
+ if (!m_distribution.at(i)->attached())
+ m_distribution.at(i)->attach();
+ }
+
+ HTMLElement::attach();
+}
+
+void InsertionPoint::detach()
+{
+ if (ShadowRoot* shadowRoot = containingShadowRoot())
+ ContentDistributor::ensureDistribution(shadowRoot);
+
+ for (size_t i = 0; i < m_distribution.size(); ++i)
+ m_distribution.at(i)->detach();
+
+ HTMLElement::detach();
+}
+
+bool InsertionPoint::shouldUseFallbackElements() const
+{
+ return isActive() && !hasDistribution();
+}
+
+bool InsertionPoint::isShadowBoundary() const
+{
+ return treeScope()->rootNode()->isShadowRoot() && isActive();
+}
+
+bool InsertionPoint::isActive() const
+{
+ if (!containingShadowRoot())
+ return false;
+ const Node* node = parentNode();
+ while (node) {
+ if (node->isInsertionPoint())
+ return false;
+
+ node = node->parentNode();
+ }
+ return true;
+}
+
+PassRefPtr<NodeList> InsertionPoint::getDistributedNodes() const
+{
+ if (ShadowRoot* shadowRoot = containingShadowRoot())
+ ContentDistributor::ensureDistribution(shadowRoot);
+
+ Vector<RefPtr<Node> > nodes;
+
+ for (size_t i = 0; i < m_distribution.size(); ++i)
+ nodes.append(m_distribution.at(i));
+
+ return StaticNodeList::adopt(nodes);
+}
+
+bool InsertionPoint::rendererIsNeeded(const NodeRenderingContext& context)
+{
+ return !isShadowBoundary() && HTMLElement::rendererIsNeeded(context);
+}
+
+void InsertionPoint::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
+{
+ HTMLElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
+ if (ShadowRoot* root = containingShadowRoot())
+ if (ElementShadow* rootOwner = root->owner())
+ rootOwner->invalidateDistribution();
+}
+
+Node::InsertionNotificationRequest InsertionPoint::insertedInto(ContainerNode* insertionPoint)
+{
+ HTMLElement::insertedInto(insertionPoint);
+
+ if (ShadowRoot* root = containingShadowRoot()) {
+ if (ElementShadow* rootOwner = root->owner()) {
+ rootOwner->distributor().didShadowBoundaryChange(root->host());
+ if (isActive() && !m_registeredWithShadowRoot && insertionPoint->treeScope()->rootNode() == root) {
+ m_registeredWithShadowRoot = true;
+ root->ensureScopeDistribution()->registerInsertionPoint(this);
+ if (canAffectSelector())
+ rootOwner->willAffectSelector();
+ }
+ }
+ }
+
+ return InsertionDone;
+}
+
+void InsertionPoint::removedFrom(ContainerNode* insertionPoint)
+{
+ ShadowRoot* root = containingShadowRoot();
+ if (!root)
+ root = insertionPoint->containingShadowRoot();
+
+ // host can be null when removedFrom() is called from ElementShadow destructor.
+ ElementShadow* rootOwner = root ? root->owner() : 0;
+ if (rootOwner)
+ rootOwner->invalidateDistribution();
+
+ // Since this insertion point is no longer visible from the shadow subtree, it need to clean itself up.
+ clearDistribution();
+
+ if (m_registeredWithShadowRoot && insertionPoint->treeScope()->rootNode() == root) {
+ ASSERT(root);
+ m_registeredWithShadowRoot = false;
+ root->ensureScopeDistribution()->unregisterInsertionPoint(this);
+ if (rootOwner && canAffectSelector())
+ rootOwner->willAffectSelector();
+ }
+
+ HTMLElement::removedFrom(insertionPoint);
+}
+
+void InsertionPoint::parseAttribute(const QualifiedName& name, const AtomicString& value)
+{
+ if (name == reset_style_inheritanceAttr) {
+ if (!inDocument() || !attached() || !isActive())
+ return;
+ containingShadowRoot()->host()->setNeedsStyleRecalc();
+ } else
+ HTMLElement::parseAttribute(name, value);
+}
+
+bool InsertionPoint::resetStyleInheritance() const
+{
+ return fastHasAttribute(reset_style_inheritanceAttr);
+}
+
+void InsertionPoint::setResetStyleInheritance(bool value)
+{
+ setBooleanAttribute(reset_style_inheritanceAttr, value);
+}
+
+bool InsertionPoint::contains(const Node* node) const
+{
+ return m_distribution.contains(const_cast<Node*>(node)) || (node->isShadowRoot() && ScopeContentDistribution::assignedTo(toShadowRoot(node)) == this);
+}
+
+const CSSSelectorList& InsertionPoint::emptySelectorList()
+{
+ DEFINE_STATIC_LOCAL(CSSSelectorList, selectorList, (CSSSelectorList()));
+ return selectorList;
+}
+
+InsertionPoint* resolveReprojection(const Node* projectedNode)
+{
+ InsertionPoint* insertionPoint = 0;
+ const Node* current = projectedNode;
+
+ while (current) {
+ if (ElementShadow* shadow = shadowOfParentForDistribution(current)) {
+ if (ShadowRoot* root = current->containingShadowRoot())
+ ContentDistributor::ensureDistribution(root);
+ if (InsertionPoint* insertedTo = shadow->distributor().findInsertionPointFor(projectedNode)) {
+ current = insertedTo;
+ insertionPoint = insertedTo;
+ continue;
+ }
+ }
+
+ if (Node* parent = parentNodeForDistribution(current)) {
+ if (InsertionPoint* insertedTo = parent->isShadowRoot() ? ScopeContentDistribution::assignedTo(toShadowRoot(parent)) : 0) {
+ current = insertedTo;
+ insertionPoint = insertedTo;
+ continue;
+ }
+ }
+
+ break;
+ }
+
+ return insertionPoint;
+}
+
+void collectInsertionPointsWhereNodeIsDistributed(const Node* node, Vector<InsertionPoint*, 8>& results)
+{
+ const Node* current = node;
+ while (true) {
+ if (ElementShadow* shadow = shadowOfParentForDistribution(current)) {
+ if (ShadowRoot* root = current->containingShadowRoot())
+ ContentDistributor::ensureDistribution(root);
+ if (InsertionPoint* insertedTo = shadow->distributor().findInsertionPointFor(node)) {
+ current = insertedTo;
+ results.append(insertedTo);
+ continue;
+ }
+ }
+ if (Node* parent = parentNodeForDistribution(current)) {
+ if (InsertionPoint* insertedTo = parent->isShadowRoot() ? ScopeContentDistribution::assignedTo(toShadowRoot(parent)) : 0) {
+ current = insertedTo;
+ results.append(insertedTo);
+ continue;
+ }
+ }
+ return;
+ }
+}
+
+} // namespace WebCore
diff --git a/Source/core/html/shadow/InsertionPoint.h b/Source/core/html/shadow/InsertionPoint.h
new file mode 100644
index 0000000..587ef2e
--- /dev/null
+++ b/Source/core/html/shadow/InsertionPoint.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef InsertionPoint_h
+#define InsertionPoint_h
+
+#include "HTMLNames.h"
+#include "core/css/CSSSelectorList.h"
+#include "core/dom/ElementShadow.h"
+#include "core/dom/ShadowRoot.h"
+#include "core/html/HTMLElement.h"
+#include "core/html/shadow/ContentDistributor.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class InsertionPoint : public HTMLElement {
+public:
+ enum Type {
+ ShadowInsertionPoint,
+ ContentInsertionPoint
+ };
+
+ enum MatchType {
+ AlwaysMatches,
+ NeverMatches,
+ HasToMatchSelector
+ };
+
+ virtual ~InsertionPoint();
+
+ bool hasDistribution() const { return !m_distribution.isEmpty(); }
+ void setDistribution(ContentDistribution& distribution) { m_distribution.swap(distribution); }
+ void clearDistribution() { m_distribution.clear(); }
+ bool isShadowBoundary() const;
+ bool isActive() const;
+
+ PassRefPtr<NodeList> getDistributedNodes() const;
+
+ virtual MatchType matchTypeFor(Node*) { return AlwaysMatches; }
+ virtual const CSSSelectorList& selectorList() { return emptySelectorList(); }
+ virtual Type insertionPointType() const = 0;
+ virtual bool canAffectSelector() const { return false; }
+
+ bool resetStyleInheritance() const;
+ void setResetStyleInheritance(bool);
+
+ virtual void attach();
+ virtual void detach();
+
+ bool shouldUseFallbackElements() const;
+
+ size_t indexOf(Node* node) const { return m_distribution.find(node); }
+ bool contains(const Node*) const;
+ size_t size() const { return m_distribution.size(); }
+ Node* at(size_t index) const { return m_distribution.at(index).get(); }
+ Node* first() const { return m_distribution.isEmpty() ? 0 : m_distribution.first().get(); }
+ Node* last() const { return m_distribution.isEmpty() ? 0 : m_distribution.last().get(); }
+ Node* nextTo(const Node* node) const { return m_distribution.nextTo(node); }
+ Node* previousTo(const Node* node) const { return m_distribution.previousTo(node); }
+
+ static const CSSSelectorList& emptySelectorList();
+
+protected:
+ InsertionPoint(const QualifiedName&, Document*);
+ virtual bool rendererIsNeeded(const NodeRenderingContext&) OVERRIDE;
+ virtual void childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) OVERRIDE;
+ virtual InsertionNotificationRequest insertedInto(ContainerNode*) OVERRIDE;
+ virtual void removedFrom(ContainerNode*) OVERRIDE;
+ virtual void parseAttribute(const QualifiedName&, const AtomicString&) OVERRIDE;
+ virtual bool isInsertionPointNode() const OVERRIDE { return true; }
+
+private:
+
+ ContentDistribution m_distribution;
+ bool m_registeredWithShadowRoot;
+};
+
+inline InsertionPoint* toInsertionPoint(Node* node)
+{
+ ASSERT_WITH_SECURITY_IMPLICATION(!node || node->isInsertionPoint());
+ return static_cast<InsertionPoint*>(node);
+}
+
+inline const InsertionPoint* toInsertionPoint(const Node* node)
+{
+ ASSERT_WITH_SECURITY_IMPLICATION(!node || node->isInsertionPoint());
+ return static_cast<const InsertionPoint*>(node);
+}
+
+inline bool isActiveInsertionPoint(const Node* node)
+{
+ return node->isInsertionPoint() && toInsertionPoint(node)->isActive();
+}
+
+inline bool isLowerEncapsulationBoundary(Node* node)
+{
+ if (!node || !node->isInsertionPoint())
+ return false;
+ return toInsertionPoint(node)->isShadowBoundary();
+}
+
+inline Node* parentNodeForDistribution(const Node* node)
+{
+ ASSERT(node);
+
+ if (Node* parent = node->parentNode()) {
+ if (parent->isInsertionPoint() && toInsertionPoint(parent)->shouldUseFallbackElements())
+ return parent->parentNode();
+ return parent;
+ }
+
+ return 0;
+}
+
+inline Element* parentElementForDistribution(const Node* node)
+{
+ if (Node* parent = parentNodeForDistribution(node)) {
+ if (parent->isElementNode())
+ return toElement(parent);
+ }
+
+ return 0;
+}
+
+inline ElementShadow* shadowOfParentForDistribution(const Node* node)
+{
+ ASSERT(node);
+ if (Element* parent = parentElementForDistribution(node))
+ return parent->shadow();
+
+ return 0;
+}
+
+InsertionPoint* resolveReprojection(const Node*);
+
+void collectInsertionPointsWhereNodeIsDistributed(const Node*, Vector<InsertionPoint*, 8>& results);
+
+} // namespace WebCore
+
+#endif // InsertionPoint_h
diff --git a/Source/core/html/shadow/MediaControlElementTypes.cpp b/Source/core/html/shadow/MediaControlElementTypes.cpp
new file mode 100644
index 0000000..37f23d8
--- /dev/null
+++ b/Source/core/html/shadow/MediaControlElementTypes.cpp
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "core/html/shadow/MediaControlElementTypes.h"
+
+#include "CSSValueKeywords.h"
+#include "HTMLNames.h"
+#include "core/css/StylePropertySet.h"
+#include "core/dom/ExceptionCodePlaceholder.h"
+#include "core/dom/MouseEvent.h"
+#include "core/rendering/RenderMedia.h"
+#include "core/rendering/RenderMediaControlElements.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+class Event;
+
+// FIXME: These constants may need to be tweaked to better match the seeking in the QuickTime plug-in.
+static const double cSkipRepeatDelay = 0.1;
+static const double cSkipTime = 0.2;
+static const double cScanRepeatDelay = 1.5;
+static const double cScanMaximumRate = 8;
+
+HTMLMediaElement* toParentMediaElement(Node* node)
+{
+ if (!node)
+ return 0;
+ Node* mediaNode = node->shadowHost();
+ if (!mediaNode)
+ mediaNode = node;
+ if (!mediaNode || !mediaNode->isElementNode() || !toElement(mediaNode)->isMediaElement())
+ return 0;
+
+ return static_cast<HTMLMediaElement*>(mediaNode);
+}
+
+MediaControlElementType mediaControlElementType(Node* node)
+{
+ ASSERT_WITH_SECURITY_IMPLICATION(node->isMediaControlElement());
+ HTMLElement* element = toHTMLElement(node);
+ if (element->hasTagName(inputTag))
+ return static_cast<MediaControlInputElement*>(element)->displayType();
+ return static_cast<MediaControlDivElement*>(element)->displayType();
+}
+
+const AtomicString& trackIndexAttributeName()
+{
+ DEFINE_STATIC_LOCAL(AtomicString, name, ("x-webkit-track-index", AtomicString::ConstructFromLiteral));
+ return name;
+}
+
+int trackListIndexForElement(Element* element)
+{
+ const AtomicString trackIndexAttributeValue = element->getAttribute(trackIndexAttributeName());
+ if (trackIndexAttributeValue.isNull() || trackIndexAttributeValue.isEmpty())
+ return HTMLMediaElement::textTracksIndexNotFound();
+ bool ok;
+ int trackIndex = trackIndexAttributeValue.toInt(&ok);
+ if (!ok)
+ return HTMLMediaElement::textTracksIndexNotFound();
+ return trackIndex;
+}
+
+MediaControlElement::MediaControlElement(MediaControlElementType displayType, HTMLElement* element)
+ : m_mediaController(0)
+ , m_displayType(displayType)
+ , m_element(element)
+{
+}
+
+void MediaControlElement::hide()
+{
+ m_element->setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone);
+}
+
+void MediaControlElement::show()
+{
+ m_element->removeInlineStyleProperty(CSSPropertyDisplay);
+}
+
+bool MediaControlElement::isShowing() const
+{
+ const StylePropertySet* propertySet = m_element->inlineStyle();
+ // Following the code from show() and hide() above, we only have
+ // to check for the presense of inline display.
+ return (!propertySet || !propertySet->getPropertyCSSValue(CSSPropertyDisplay));
+}
+
+void MediaControlElement::setDisplayType(MediaControlElementType displayType)
+{
+ if (displayType == m_displayType)
+ return;
+
+ m_displayType = displayType;
+ if (RenderObject* object = m_element->renderer())
+ object->repaint();
+}
+
+// ----------------------------
+
+MediaControlDivElement::MediaControlDivElement(Document* document, MediaControlElementType displayType)
+ : HTMLDivElement(divTag, document)
+ , MediaControlElement(displayType, this)
+{
+}
+
+// ----------------------------
+
+MediaControlInputElement::MediaControlInputElement(Document* document, MediaControlElementType displayType)
+ : HTMLInputElement(inputTag, document, 0, false)
+ , MediaControlElement(displayType, this)
+{
+}
+
+// ----------------------------
+
+MediaControlTimeDisplayElement::MediaControlTimeDisplayElement(Document* document, MediaControlElementType displayType)
+ : MediaControlDivElement(document, displayType)
+ , m_currentValue(0)
+{
+}
+
+void MediaControlTimeDisplayElement::setCurrentValue(double time)
+{
+ m_currentValue = time;
+}
+
+// ----------------------------
+
+MediaControlMuteButtonElement::MediaControlMuteButtonElement(Document* document, MediaControlElementType displayType)
+ : MediaControlInputElement(document, displayType)
+{
+}
+
+void MediaControlMuteButtonElement::defaultEventHandler(Event* event)
+{
+ if (event->type() == eventNames().clickEvent) {
+ mediaController()->setMuted(!mediaController()->muted());
+ event->setDefaultHandled();
+ }
+
+ HTMLInputElement::defaultEventHandler(event);
+}
+
+void MediaControlMuteButtonElement::changedMute()
+{
+ updateDisplayType();
+}
+
+void MediaControlMuteButtonElement::updateDisplayType()
+{
+ setDisplayType(mediaController()->muted() ? MediaUnMuteButton : MediaMuteButton);
+}
+
+// ----------------------------
+
+MediaControlSeekButtonElement::MediaControlSeekButtonElement(Document* document, MediaControlElementType displayType)
+ : MediaControlInputElement(document, displayType)
+ , m_actionOnStop(Nothing)
+ , m_seekType(Skip)
+ , m_seekTimer(this, &MediaControlSeekButtonElement::seekTimerFired)
+{
+}
+
+void MediaControlSeekButtonElement::defaultEventHandler(Event* event)
+{
+ // Set the mousedown and mouseup events as defaultHandled so they
+ // do not trigger drag start or end actions in MediaControlPanelElement.
+ if (event->type() == eventNames().mousedownEvent || event->type() == eventNames().mouseupEvent)
+ event->setDefaultHandled();
+}
+
+void MediaControlSeekButtonElement::setActive(bool flag, bool pause)
+{
+ if (flag == active())
+ return;
+
+ if (flag)
+ startTimer();
+ else
+ stopTimer();
+
+ MediaControlInputElement::setActive(flag, pause);
+}
+
+void MediaControlSeekButtonElement::startTimer()
+{
+ m_seekType = mediaController()->supportsScanning() ? Scan : Skip;
+
+ if (m_seekType == Skip) {
+ // Seeking by skipping requires the video to be paused during seeking.
+ m_actionOnStop = mediaController()->paused() ? Nothing : Play;
+ mediaController()->pause();
+ } else {
+ // Seeking by scanning requires the video to be playing during seeking.
+ m_actionOnStop = mediaController()->paused() ? Pause : Nothing;
+ mediaController()->play();
+ mediaController()->setPlaybackRate(nextRate());
+ }
+
+ m_seekTimer.start(0, m_seekType == Skip ? cSkipRepeatDelay : cScanRepeatDelay);
+}
+
+void MediaControlSeekButtonElement::stopTimer()
+{
+ if (m_seekType == Scan)
+ mediaController()->setPlaybackRate(mediaController()->defaultPlaybackRate());
+
+ if (m_actionOnStop == Play)
+ mediaController()->play();
+ else if (m_actionOnStop == Pause)
+ mediaController()->pause();
+
+ if (m_seekTimer.isActive())
+ m_seekTimer.stop();
+}
+
+double MediaControlSeekButtonElement::nextRate() const
+{
+ double rate = std::min(cScanMaximumRate, fabs(mediaController()->playbackRate() * 2));
+ if (!isForwardButton())
+ rate *= -1;
+ return rate;
+}
+
+void MediaControlSeekButtonElement::seekTimerFired(Timer<MediaControlSeekButtonElement>*)
+{
+ if (m_seekType == Skip) {
+ double skipTime = isForwardButton() ? cSkipTime : -cSkipTime;
+ mediaController()->setCurrentTime(mediaController()->currentTime() + skipTime, IGNORE_EXCEPTION);
+ } else
+ mediaController()->setPlaybackRate(nextRate());
+}
+
+// ----------------------------
+
+MediaControlVolumeSliderElement::MediaControlVolumeSliderElement(Document* document)
+ : MediaControlInputElement(document, MediaVolumeSlider)
+ , m_clearMutedOnUserInteraction(false)
+{
+}
+
+void MediaControlVolumeSliderElement::defaultEventHandler(Event* event)
+{
+ // Left button is 0. Rejects mouse events not from left button.
+ if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button())
+ return;
+
+ if (!attached())
+ return;
+
+ MediaControlInputElement::defaultEventHandler(event);
+
+ if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
+ return;
+
+ double volume = value().toDouble();
+ if (volume != mediaController()->volume())
+ mediaController()->setVolume(volume, ASSERT_NO_EXCEPTION);
+ if (m_clearMutedOnUserInteraction)
+ mediaController()->setMuted(false);
+}
+
+bool MediaControlVolumeSliderElement::willRespondToMouseMoveEvents()
+{
+ if (!attached())
+ return false;
+
+ return MediaControlInputElement::willRespondToMouseMoveEvents();
+}
+
+bool MediaControlVolumeSliderElement::willRespondToMouseClickEvents()
+{
+ if (!attached())
+ return false;
+
+ return MediaControlInputElement::willRespondToMouseClickEvents();
+}
+
+void MediaControlVolumeSliderElement::setVolume(double volume)
+{
+ if (value().toDouble() != volume)
+ setValue(String::number(volume));
+}
+
+void MediaControlVolumeSliderElement::setClearMutedOnUserInteraction(bool clearMute)
+{
+ m_clearMutedOnUserInteraction = clearMute;
+}
+
+} // namespace WebCore
diff --git a/Source/core/html/shadow/MediaControlElementTypes.h b/Source/core/html/shadow/MediaControlElementTypes.h
new file mode 100644
index 0000000..771f820
--- /dev/null
+++ b/Source/core/html/shadow/MediaControlElementTypes.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MediaControlElementTypes_h
+#define MediaControlElementTypes_h
+
+#include "core/html/HTMLDivElement.h"
+#include "core/html/HTMLInputElement.h"
+#include "core/html/HTMLMediaElement.h"
+#include "core/html/MediaControllerInterface.h"
+#include "core/rendering/RenderBlock.h"
+
+namespace WebCore {
+
+// Must match WebKitSystemInterface.h
+enum MediaControlElementType {
+ MediaEnterFullscreenButton = 0,
+ MediaMuteButton,
+ MediaPlayButton,
+ MediaSeekBackButton,
+ MediaSeekForwardButton,
+ MediaSlider,
+ MediaSliderThumb,
+ MediaRewindButton,
+ MediaReturnToRealtimeButton,
+ MediaShowClosedCaptionsButton,
+ MediaHideClosedCaptionsButton,
+ MediaUnMuteButton,
+ MediaPauseButton,
+ MediaTimelineContainer,
+ MediaCurrentTimeDisplay,
+ MediaTimeRemainingDisplay,
+ MediaStatusDisplay,
+ MediaControlsPanel,
+ MediaVolumeSliderContainer,
+ MediaVolumeSlider,
+ MediaVolumeSliderThumb,
+ MediaFullScreenVolumeSlider,
+ MediaFullScreenVolumeSliderThumb,
+ MediaVolumeSliderMuteButton,
+ MediaTextTrackDisplayContainer,
+ MediaTextTrackDisplay,
+ MediaExitFullscreenButton,
+ MediaOverlayPlayButton,
+ MediaClosedCaptionsContainer,
+ MediaClosedCaptionsTrackList,
+};
+
+HTMLMediaElement* toParentMediaElement(Node*);
+inline HTMLMediaElement* toParentMediaElement(RenderObject* renderer) { return toParentMediaElement(renderer->node()); }
+
+MediaControlElementType mediaControlElementType(Node*);
+
+const AtomicString& trackIndexAttributeName();
+int trackListIndexForElement(Element*);
+
+// ----------------------------
+
+class MediaControlElement {
+public:
+ virtual void hide();
+ virtual void show();
+ virtual bool isShowing() const;
+
+ virtual MediaControlElementType displayType() { return m_displayType; }
+ virtual const AtomicString& shadowPseudoId() const = 0;
+
+ virtual void setMediaController(MediaControllerInterface* controller) { m_mediaController = controller; }
+ virtual MediaControllerInterface* mediaController() const { return m_mediaController; }
+
+protected:
+ explicit MediaControlElement(MediaControlElementType, HTMLElement*);
+ ~MediaControlElement() { }
+
+ virtual void setDisplayType(MediaControlElementType);
+ virtual bool isMediaControlElement() const { return true; }
+
+private:
+ MediaControllerInterface* m_mediaController;
+ MediaControlElementType m_displayType;
+ HTMLElement* m_element;
+};
+
+// ----------------------------
+
+class MediaControlDivElement : public HTMLDivElement, public MediaControlElement {
+protected:
+ virtual bool isMediaControlElement() const OVERRIDE { return MediaControlElement::isMediaControlElement(); }
+ explicit MediaControlDivElement(Document*, MediaControlElementType);
+};
+
+// ----------------------------
+
+class MediaControlInputElement : public HTMLInputElement, public MediaControlElement {
+protected:
+ virtual bool isMediaControlElement() const OVERRIDE { return MediaControlElement::isMediaControlElement(); }
+ explicit MediaControlInputElement(Document*, MediaControlElementType);
+
+private:
+ virtual void updateDisplayType() { }
+};
+
+// ----------------------------
+
+class MediaControlTimeDisplayElement : public MediaControlDivElement {
+public:
+ void setCurrentValue(double);
+ double currentValue() const { return m_currentValue; }
+
+protected:
+ explicit MediaControlTimeDisplayElement(Document*, MediaControlElementType);
+
+private:
+ double m_currentValue;
+};
+
+// ----------------------------
+
+class MediaControlMuteButtonElement : public MediaControlInputElement {
+public:
+ void changedMute();
+
+ virtual bool willRespondToMouseClickEvents() OVERRIDE { return true; }
+
+protected:
+ explicit MediaControlMuteButtonElement(Document*, MediaControlElementType);
+
+ virtual void defaultEventHandler(Event*) OVERRIDE;
+
+private:
+ virtual void updateDisplayType() OVERRIDE;
+};
+
+// ----------------------------
+
+class MediaControlSeekButtonElement : public MediaControlInputElement {
+public:
+ virtual bool willRespondToMouseClickEvents() OVERRIDE { return true; }
+
+protected:
+ explicit MediaControlSeekButtonElement(Document*, MediaControlElementType);
+
+ virtual void defaultEventHandler(Event*) OVERRIDE;
+ virtual bool isForwardButton() const = 0;
+
+private:
+ void setActive(bool /*flag*/ = true, bool /*pause*/ = false);
+
+ void startTimer();
+ void stopTimer();
+ double nextRate() const;
+ void seekTimerFired(Timer<MediaControlSeekButtonElement>*);
+
+ enum ActionType { Nothing, Play, Pause };
+ ActionType m_actionOnStop;
+ enum SeekType { Skip, Scan };
+ SeekType m_seekType;
+ Timer<MediaControlSeekButtonElement> m_seekTimer;
+};
+
+// ----------------------------
+
+class MediaControlVolumeSliderElement : public MediaControlInputElement {
+public:
+ virtual bool willRespondToMouseMoveEvents() OVERRIDE;
+ virtual bool willRespondToMouseClickEvents() OVERRIDE;
+ void setVolume(double);
+ void setClearMutedOnUserInteraction(bool);
+
+protected:
+ explicit MediaControlVolumeSliderElement(Document*);
+
+ virtual void defaultEventHandler(Event*) OVERRIDE;
+
+private:
+ bool m_clearMutedOnUserInteraction;
+};
+
+} // namespace WebCore
+
+#endif // MediaControlElementTypes_h
diff --git a/Source/core/html/shadow/MediaControlElements.cpp b/Source/core/html/shadow/MediaControlElements.cpp
new file mode 100644
index 0000000..83e2e1b
--- /dev/null
+++ b/Source/core/html/shadow/MediaControlElements.cpp
@@ -0,0 +1,1052 @@
+/*
+ * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "core/html/shadow/MediaControlElements.h"
+
+#include "core/dom/EventNames.h"
+#include "core/dom/EventTarget.h"
+#include "core/dom/ExceptionCodePlaceholder.h"
+#include "core/dom/MouseEvent.h"
+#include "core/html/DOMTokenList.h"
+#include "core/html/HTMLVideoElement.h"
+#include "core/html/shadow/MediaControls.h"
+#include "core/html/track/TextTrack.h"
+#include "core/html/track/TextTrackList.h"
+#include "core/page/CaptionUserPreferences.h"
+#include "core/page/EventHandler.h"
+#include "core/page/Frame.h"
+#include "core/page/Page.h"
+#include "core/page/PageGroup.h"
+#include "core/page/Settings.h"
+#include "core/platform/Language.h"
+#include "core/platform/LocalizedStrings.h"
+#include "core/platform/graphics/GraphicsContext.h"
+#include "core/rendering/RenderLayer.h"
+#include "core/rendering/RenderMediaControlElements.h"
+#include "core/rendering/RenderSlider.h"
+#include "core/rendering/RenderTheme.h"
+#include "core/rendering/RenderVideo.h"
+#include "core/rendering/RenderView.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+static const AtomicString& getMediaControlCurrentTimeDisplayElementShadowPseudoId();
+static const AtomicString& getMediaControlTimeRemainingDisplayElementShadowPseudoId();
+
+static const char* textTracksOffAttrValue = "-1"; // This must match HTMLMediaElement::textTracksOffIndex()
+
+MediaControlPanelElement::MediaControlPanelElement(Document* document)
+ : MediaControlDivElement(document, MediaControlsPanel)
+ , m_canBeDragged(false)
+ , m_isBeingDragged(false)
+ , m_isDisplayed(false)
+ , m_opaque(true)
+ , m_transitionTimer(this, &MediaControlPanelElement::transitionTimerFired)
+{
+}
+
+PassRefPtr<MediaControlPanelElement> MediaControlPanelElement::create(Document* document)
+{
+ return adoptRef(new MediaControlPanelElement(document));
+}
+
+const AtomicString& MediaControlPanelElement::shadowPseudoId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-panel", AtomicString::ConstructFromLiteral));
+ return id;
+}
+
+void MediaControlPanelElement::startDrag(const LayoutPoint& eventLocation)
+{
+ if (!m_canBeDragged)
+ return;
+
+ if (m_isBeingDragged)
+ return;
+
+ RenderObject* renderer = this->renderer();
+ if (!renderer || !renderer->isBox())
+ return;
+
+ Frame* frame = document()->frame();
+ if (!frame)
+ return;
+
+ m_lastDragEventLocation = eventLocation;
+
+ frame->eventHandler()->setCapturingMouseEventsNode(this);
+
+ m_isBeingDragged = true;
+}
+
+void MediaControlPanelElement::continueDrag(const LayoutPoint& eventLocation)
+{
+ if (!m_isBeingDragged)
+ return;
+
+ LayoutSize distanceDragged = eventLocation - m_lastDragEventLocation;
+ m_cumulativeDragOffset.move(distanceDragged);
+ m_lastDragEventLocation = eventLocation;
+ setPosition(m_cumulativeDragOffset);
+}
+
+void MediaControlPanelElement::endDrag()
+{
+ if (!m_isBeingDragged)
+ return;
+
+ m_isBeingDragged = false;
+
+ Frame* frame = document()->frame();
+ if (!frame)
+ return;
+
+ frame->eventHandler()->setCapturingMouseEventsNode(0);
+}
+
+void MediaControlPanelElement::startTimer()
+{
+ stopTimer();
+
+ // The timer is required to set the property display:'none' on the panel,
+ // such that captions are correctly displayed at the bottom of the video
+ // at the end of the fadeout transition.
+ double duration = document()->page() ? document()->page()->theme()->mediaControlsFadeOutDuration() : 0;
+ m_transitionTimer.startOneShot(duration);
+}
+
+void MediaControlPanelElement::stopTimer()
+{
+ if (m_transitionTimer.isActive())
+ m_transitionTimer.stop();
+}
+
+void MediaControlPanelElement::transitionTimerFired(Timer<MediaControlPanelElement>*)
+{
+ if (!m_opaque)
+ hide();
+
+ stopTimer();
+}
+
+void MediaControlPanelElement::setPosition(const LayoutPoint& position)
+{
+ double left = position.x();
+ double top = position.y();
+
+ // Set the left and top to control the panel's position; this depends on it being absolute positioned.
+ // Set the margin to zero since the position passed in will already include the effect of the margin.
+ setInlineStyleProperty(CSSPropertyLeft, left, CSSPrimitiveValue::CSS_PX);
+ setInlineStyleProperty(CSSPropertyTop, top, CSSPrimitiveValue::CSS_PX);
+ setInlineStyleProperty(CSSPropertyMarginLeft, 0.0, CSSPrimitiveValue::CSS_PX);
+ setInlineStyleProperty(CSSPropertyMarginTop, 0.0, CSSPrimitiveValue::CSS_PX);
+
+ classList()->add("dragged", IGNORE_EXCEPTION);
+}
+
+void MediaControlPanelElement::resetPosition()
+{
+ removeInlineStyleProperty(CSSPropertyLeft);
+ removeInlineStyleProperty(CSSPropertyTop);
+ removeInlineStyleProperty(CSSPropertyMarginLeft);
+ removeInlineStyleProperty(CSSPropertyMarginTop);
+
+ classList()->remove("dragged", IGNORE_EXCEPTION);
+
+ m_cumulativeDragOffset.setX(0);
+ m_cumulativeDragOffset.setY(0);
+}
+
+void MediaControlPanelElement::makeOpaque()
+{
+ if (m_opaque)
+ return;
+
+ double duration = document()->page() ? document()->page()->theme()->mediaControlsFadeInDuration() : 0;
+
+ setInlineStyleProperty(CSSPropertyWebkitTransitionProperty, CSSPropertyOpacity);
+ setInlineStyleProperty(CSSPropertyWebkitTransitionDuration, duration, CSSPrimitiveValue::CSS_S);
+ setInlineStyleProperty(CSSPropertyOpacity, 1.0, CSSPrimitiveValue::CSS_NUMBER);
+
+ m_opaque = true;
+
+ if (m_isDisplayed)
+ show();
+}
+
+void MediaControlPanelElement::makeTransparent()
+{
+ if (!m_opaque)
+ return;
+
+ double duration = document()->page() ? document()->page()->theme()->mediaControlsFadeOutDuration() : 0;
+
+ setInlineStyleProperty(CSSPropertyWebkitTransitionProperty, CSSPropertyOpacity);
+ setInlineStyleProperty(CSSPropertyWebkitTransitionDuration, duration, CSSPrimitiveValue::CSS_S);
+ setInlineStyleProperty(CSSPropertyOpacity, 0.0, CSSPrimitiveValue::CSS_NUMBER);
+
+ m_opaque = false;
+ startTimer();
+}
+
+void MediaControlPanelElement::defaultEventHandler(Event* event)
+{
+ MediaControlDivElement::defaultEventHandler(event);
+
+ if (event->isMouseEvent()) {
+ LayoutPoint location = static_cast<MouseEvent*>(event)->absoluteLocation();
+ if (event->type() == eventNames().mousedownEvent && event->target() == this) {
+ startDrag(location);
+ event->setDefaultHandled();
+ } else if (event->type() == eventNames().mousemoveEvent && m_isBeingDragged)
+ continueDrag(location);
+ else if (event->type() == eventNames().mouseupEvent && m_isBeingDragged) {
+ continueDrag(location);
+ endDrag();
+ event->setDefaultHandled();
+ }
+ }
+}
+
+void MediaControlPanelElement::setCanBeDragged(bool canBeDragged)
+{
+ if (m_canBeDragged == canBeDragged)
+ return;
+
+ m_canBeDragged = canBeDragged;
+
+ if (!canBeDragged)
+ endDrag();
+}
+
+void MediaControlPanelElement::setIsDisplayed(bool isDisplayed)
+{
+ m_isDisplayed = isDisplayed;
+}
+
+// ----------------------------
+
+MediaControlPanelEnclosureElement::MediaControlPanelEnclosureElement(Document* document)
+ // Mapping onto same MediaControlElementType as panel element, since it has similar properties.
+ : MediaControlDivElement(document, MediaControlsPanel)
+{
+}
+
+PassRefPtr<MediaControlPanelEnclosureElement> MediaControlPanelEnclosureElement::create(Document* document)
+{
+ return adoptRef(new MediaControlPanelEnclosureElement(document));
+}
+
+const AtomicString& MediaControlPanelEnclosureElement::shadowPseudoId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-enclosure", AtomicString::ConstructFromLiteral));
+ return id;
+}
+
+// ----------------------------
+
+MediaControlOverlayEnclosureElement::MediaControlOverlayEnclosureElement(Document* document)
+ // Mapping onto same MediaControlElementType as panel element, since it has similar properties.
+ : MediaControlDivElement(document, MediaControlsPanel)
+{
+}
+
+PassRefPtr<MediaControlOverlayEnclosureElement> MediaControlOverlayEnclosureElement::create(Document* document)
+{
+ return adoptRef(new MediaControlOverlayEnclosureElement(document));
+}
+
+const AtomicString& MediaControlOverlayEnclosureElement::shadowPseudoId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-overlay-enclosure", AtomicString::ConstructFromLiteral));
+ return id;
+}
+
+// ----------------------------
+
+MediaControlPanelMuteButtonElement::MediaControlPanelMuteButtonElement(Document* document, MediaControls* controls)
+ : MediaControlMuteButtonElement(document, MediaMuteButton)
+ , m_controls(controls)
+{
+}
+
+PassRefPtr<MediaControlPanelMuteButtonElement> MediaControlPanelMuteButtonElement::create(Document* document, MediaControls* controls)
+{
+ ASSERT(controls);
+
+ RefPtr<MediaControlPanelMuteButtonElement> button = adoptRef(new MediaControlPanelMuteButtonElement(document, controls));
+ button->ensureUserAgentShadowRoot();
+ button->setType("button");
+ return button.release();
+}
+
+void MediaControlPanelMuteButtonElement::defaultEventHandler(Event* event)
+{
+ if (event->type() == eventNames().mouseoverEvent)
+ m_controls->showVolumeSlider();
+
+ MediaControlMuteButtonElement::defaultEventHandler(event);
+}
+
+const AtomicString& MediaControlPanelMuteButtonElement::shadowPseudoId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-mute-button", AtomicString::ConstructFromLiteral));
+ return id;
+}
+
+// ----------------------------
+
+MediaControlVolumeSliderMuteButtonElement::MediaControlVolumeSliderMuteButtonElement(Document* document)
+ : MediaControlMuteButtonElement(document, MediaMuteButton)
+{
+}
+
+PassRefPtr<MediaControlVolumeSliderMuteButtonElement> MediaControlVolumeSliderMuteButtonElement::create(Document* document)
+{
+ RefPtr<MediaControlVolumeSliderMuteButtonElement> button = adoptRef(new MediaControlVolumeSliderMuteButtonElement(document));
+ button->ensureUserAgentShadowRoot();
+ button->setType("button");
+ return button.release();
+}
+
+const AtomicString& MediaControlVolumeSliderMuteButtonElement::shadowPseudoId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider-mute-button", AtomicString::ConstructFromLiteral));
+ return id;
+}
+
+// ----------------------------
+
+MediaControlPlayButtonElement::MediaControlPlayButtonElement(Document* document)
+ : MediaControlInputElement(document, MediaPlayButton)
+{
+}
+
+PassRefPtr<MediaControlPlayButtonElement> MediaControlPlayButtonElement::create(Document* document)
+{
+ RefPtr<MediaControlPlayButtonElement> button = adoptRef(new MediaControlPlayButtonElement(document));
+ button->ensureUserAgentShadowRoot();
+ button->setType("button");
+ return button.release();
+}
+
+void MediaControlPlayButtonElement::defaultEventHandler(Event* event)
+{
+ if (event->type() == eventNames().clickEvent) {
+ if (mediaController()->canPlay())
+ mediaController()->play();
+ else
+ mediaController()->pause();
+ updateDisplayType();
+ event->setDefaultHandled();
+ }
+ HTMLInputElement::defaultEventHandler(event);
+}
+
+void MediaControlPlayButtonElement::updateDisplayType()
+{
+ setDisplayType(mediaController()->canPlay() ? MediaPlayButton : MediaPauseButton);
+}
+
+const AtomicString& MediaControlPlayButtonElement::shadowPseudoId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-play-button", AtomicString::ConstructFromLiteral));
+ return id;
+}
+
+// ----------------------------
+
+MediaControlOverlayPlayButtonElement::MediaControlOverlayPlayButtonElement(Document* document)
+ : MediaControlInputElement(document, MediaOverlayPlayButton)
+{
+}
+
+PassRefPtr<MediaControlOverlayPlayButtonElement> MediaControlOverlayPlayButtonElement::create(Document* document)
+{
+ RefPtr<MediaControlOverlayPlayButtonElement> button = adoptRef(new MediaControlOverlayPlayButtonElement(document));
+ button->ensureUserAgentShadowRoot();
+ button->setType("button");
+ return button.release();
+}
+
+void MediaControlOverlayPlayButtonElement::defaultEventHandler(Event* event)
+{
+ if (event->type() == eventNames().clickEvent && mediaController()->canPlay()) {
+ mediaController()->play();
+ updateDisplayType();
+ event->setDefaultHandled();
+ }
+ HTMLInputElement::defaultEventHandler(event);
+}
+
+void MediaControlOverlayPlayButtonElement::updateDisplayType()
+{
+ if (mediaController()->canPlay()) {
+ show();
+ } else
+ hide();
+}
+
+const AtomicString& MediaControlOverlayPlayButtonElement::shadowPseudoId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-overlay-play-button", AtomicString::ConstructFromLiteral));
+ return id;
+}
+
+
+// ----------------------------
+
+MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(Document* document, MediaControls* controls)
+ : MediaControlInputElement(document, MediaShowClosedCaptionsButton)
+{
+ UNUSED_PARAM(controls);
+}
+
+PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> MediaControlToggleClosedCaptionsButtonElement::create(Document* document, MediaControls* controls)
+{
+ ASSERT(controls);
+
+ RefPtr<MediaControlToggleClosedCaptionsButtonElement> button = adoptRef(new MediaControlToggleClosedCaptionsButtonElement(document, controls));
+ button->ensureUserAgentShadowRoot();
+ button->setType("button");
+ button->hide();
+ return button.release();
+}
+
+void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType()
+{
+ bool captionsVisible = mediaController()->closedCaptionsVisible();
+ setDisplayType(captionsVisible ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton);
+ setChecked(captionsVisible);
+}
+
+void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event)
+{
+ if (event->type() == eventNames().clickEvent) {
+ // FIXME: It's not great that the shared code is dictating behavior of platform-specific
+ // UI. Not all ports may want the closed captions button to toggle a list of tracks, so
+ // we have to use #if.
+ // https://bugs.webkit.org/show_bug.cgi?id=101877
+ mediaController()->setClosedCaptionsVisible(!mediaController()->closedCaptionsVisible());
+ setChecked(mediaController()->closedCaptionsVisible());
+ updateDisplayType();
+ event->setDefaultHandled();
+ }
+
+ HTMLInputElement::defaultEventHandler(event);
+}
+
+const AtomicString& MediaControlToggleClosedCaptionsButtonElement::shadowPseudoId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-toggle-closed-captions-button", AtomicString::ConstructFromLiteral));
+ return id;
+}
+
+// ----------------------------
+
+MediaControlClosedCaptionsContainerElement::MediaControlClosedCaptionsContainerElement(Document* document)
+ : MediaControlDivElement(document, MediaClosedCaptionsContainer)
+{
+}
+
+PassRefPtr<MediaControlClosedCaptionsContainerElement> MediaControlClosedCaptionsContainerElement::create(Document* document)
+{
+ RefPtr<MediaControlClosedCaptionsContainerElement> element = adoptRef(new MediaControlClosedCaptionsContainerElement(document));
+ element->hide();
+ return element.release();
+}
+
+const AtomicString& MediaControlClosedCaptionsContainerElement::shadowPseudoId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-closed-captions-container", AtomicString::ConstructFromLiteral));
+ return id;
+}
+
+// ----------------------------
+
+MediaControlClosedCaptionsTrackListElement::MediaControlClosedCaptionsTrackListElement(Document* document, MediaControls* controls)
+ : MediaControlDivElement(document, MediaClosedCaptionsTrackList)
+ , m_controls(controls)
+ , m_trackListHasChanged(true)
+{
+}
+
+PassRefPtr<MediaControlClosedCaptionsTrackListElement> MediaControlClosedCaptionsTrackListElement::create(Document* document, MediaControls* controls)
+{
+ ASSERT(controls);
+ RefPtr<MediaControlClosedCaptionsTrackListElement> element = adoptRef(new MediaControlClosedCaptionsTrackListElement(document, controls));
+ return element.release();
+}
+
+void MediaControlClosedCaptionsTrackListElement::defaultEventHandler(Event* event)
+{
+ if (event->type() == eventNames().clickEvent) {
+ Node* target = event->target()->toNode();
+ if (!target || !target->isElementNode())
+ return;
+
+ // When we created the elements in the track list, we gave them a custom
+ // attribute representing the index in the HTMLMediaElement's list of tracks.
+ // Check if the event target has such a custom element and, if so,
+ // tell the HTMLMediaElement to enable that track.
+
+ RefPtr<TextTrack> textTrack;
+ MenuItemToTrackMap::iterator iter = m_menuToTrackMap.find(toElement(target));
+ if (iter != m_menuToTrackMap.end())
+ textTrack = iter->value;
+ m_menuToTrackMap.clear();
+ m_controls->toggleClosedCaptionTrackList();
+
+ int trackIndex = trackListIndexForElement(toElement(target));
+ if (trackIndex == HTMLMediaElement::textTracksIndexNotFound())
+ return;
+
+ HTMLMediaElement* mediaElement = toParentMediaElement(this);
+ if (!mediaElement)
+ return;
+
+ if (textTrack)
+ mediaElement->setSelectedTextTrack(textTrack.get());
+ else if (trackIndex == HTMLMediaElement::textTracksOffIndex())
+ mediaElement->setSelectedTextTrack(0);
+
+ updateDisplay();
+ }
+
+ MediaControlDivElement::defaultEventHandler(event);
+}
+
+const AtomicString& MediaControlClosedCaptionsTrackListElement::shadowPseudoId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-closed-captions-track-list", AtomicString::ConstructFromLiteral));
+ return id;
+}
+
+void MediaControlClosedCaptionsTrackListElement::updateDisplay()
+{
+ DEFINE_STATIC_LOCAL(AtomicString, selectedClassValue, ("selected", AtomicString::ConstructFromLiteral));
+
+ if (!mediaController()->hasClosedCaptions())
+ return;
+
+ HTMLMediaElement* mediaElement = toParentMediaElement(this);
+ if (!mediaElement)
+ return;
+
+ TextTrackList* trackList = mediaElement->textTracks();
+
+ if (!trackList || !trackList->length())
+ return;
+
+ if (m_trackListHasChanged)
+ rebuildTrackListMenu();
+
+ bool captionsVisible = mediaElement->closedCaptionsVisible();
+ for (unsigned i = 0, length = m_menuItems.size(); i < length; ++i) {
+ RefPtr<Element> trackItem = m_menuItems[i];
+
+ int trackIndex = trackListIndexForElement(trackItem.get());
+ if (trackIndex == HTMLMediaElement::textTracksIndexNotFound())
+ continue;
+
+ if (trackIndex == HTMLMediaElement::textTracksOffIndex()) {
+ if (captionsVisible)
+ trackItem->classList()->remove(selectedClassValue, ASSERT_NO_EXCEPTION);
+ else
+ trackItem->classList()->add(selectedClassValue, ASSERT_NO_EXCEPTION);
+ continue;
+ }
+
+ RefPtr<TextTrack> textTrack;
+ MenuItemToTrackMap::iterator iter = m_menuToTrackMap.find(trackItem.get());
+ if (iter == m_menuToTrackMap.end())
+ continue;
+ textTrack = iter->value;
+ if (!textTrack)
+ continue;
+ if (textTrack->mode() == TextTrack::showingKeyword())
+ trackItem->classList()->add(selectedClassValue, ASSERT_NO_EXCEPTION);
+ else
+ trackItem->classList()->remove(selectedClassValue, ASSERT_NO_EXCEPTION);
+ }
+}
+
+void MediaControlClosedCaptionsTrackListElement::rebuildTrackListMenu()
+{
+ // Remove any existing content.
+ removeChildren();
+ m_menuItems.clear();
+
+ m_trackListHasChanged = false;
+ m_menuToTrackMap.clear();
+
+ if (!mediaController()->hasClosedCaptions())
+ return;
+
+ HTMLMediaElement* mediaElement = toParentMediaElement(this);
+ if (!mediaElement)
+ return;
+
+ TextTrackList* trackList = mediaElement->textTracks();
+ if (!trackList || !trackList->length())
+ return;
+
+ Document* doc = document();
+ CaptionUserPreferences* captionsUserPreferences = doc->page()->group().captionPreferences();
+ Vector<RefPtr<TextTrack> > tracksForMenu = captionsUserPreferences->sortedTrackListForMenu(trackList);
+
+ RefPtr<Element> captionsHeader = doc->createElement(h3Tag, ASSERT_NO_EXCEPTION);
+ captionsHeader->appendChild(doc->createTextNode(textTrackSubtitlesText()));
+ appendChild(captionsHeader);
+ RefPtr<Element> captionsMenuList = doc->createElement(ulTag, ASSERT_NO_EXCEPTION);
+
+ RefPtr<Element> menuItem;
+ menuItem = doc->createElement(liTag, ASSERT_NO_EXCEPTION);
+ menuItem->appendChild(doc->createTextNode(textTrackOffText()));
+ menuItem->setAttribute(trackIndexAttributeName(), textTracksOffAttrValue, ASSERT_NO_EXCEPTION);
+ captionsMenuList->appendChild(menuItem);
+ m_menuItems.append(menuItem);
+
+ for (unsigned i = 0, length = tracksForMenu.size(); i < length; ++i) {
+ RefPtr<TextTrack> textTrack = tracksForMenu[i];
+ menuItem = doc->createElement(liTag, ASSERT_NO_EXCEPTION);
+
+ // Add a custom attribute to the <li> element which will allow
+ // us to easily associate the user tapping here with the
+ // track. Since this list is rebuilt if the tracks change, we
+ // should always be in sync.
+ menuItem->setAttribute(trackIndexAttributeName(), String::number(i), ASSERT_NO_EXCEPTION);
+
+ menuItem->appendChild(doc->createTextNode(captionsUserPreferences->displayNameForTrack(textTrack.get())));
+
+ captionsMenuList->appendChild(menuItem);
+ m_menuItems.append(menuItem);
+ m_menuToTrackMap.add(menuItem, textTrack);
+ }
+
+ appendChild(captionsMenuList);
+
+ updateDisplay();
+}
+
+// ----------------------------
+
+MediaControlTimelineElement::MediaControlTimelineElement(Document* document, MediaControls* controls)
+ : MediaControlInputElement(document, MediaSlider)
+ , m_controls(controls)
+{
+}
+
+PassRefPtr<MediaControlTimelineElement> MediaControlTimelineElement::create(Document* document, MediaControls* controls)
+{
+ ASSERT(controls);
+
+ RefPtr<MediaControlTimelineElement> timeline = adoptRef(new MediaControlTimelineElement(document, controls));
+ timeline->ensureUserAgentShadowRoot();
+ timeline->setType("range");
+ timeline->setAttribute(precisionAttr, "float");
+ return timeline.release();
+}
+
+void MediaControlTimelineElement::defaultEventHandler(Event* event)
+{
+ // Left button is 0. Rejects mouse events not from left button.
+ if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button())
+ return;
+
+ if (!attached())
+ return;
+
+ if (event->type() == eventNames().mousedownEvent)
+ mediaController()->beginScrubbing();
+
+ if (event->type() == eventNames().mouseupEvent)
+ mediaController()->endScrubbing();
+
+ MediaControlInputElement::defaultEventHandler(event);
+
+ if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent)
+ return;
+
+ double time = value().toDouble();
+ if (event->type() == eventNames().inputEvent && time != mediaController()->currentTime())
+ mediaController()->setCurrentTime(time, IGNORE_EXCEPTION);
+
+ RenderSlider* slider = toRenderSlider(renderer());
+ if (slider && slider->inDragMode())
+ m_controls->updateCurrentTimeDisplay();
+}
+
+bool MediaControlTimelineElement::willRespondToMouseClickEvents()
+{
+ if (!attached())
+ return false;
+
+ return true;
+}
+
+void MediaControlTimelineElement::setPosition(double currentTime)
+{
+ setValue(String::number(currentTime));
+}
+
+void MediaControlTimelineElement::setDuration(double duration)
+{
+ setAttribute(maxAttr, String::number(std::isfinite(duration) ? duration : 0));
+}
+
+
+const AtomicString& MediaControlTimelineElement::shadowPseudoId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline", AtomicString::ConstructFromLiteral));
+ return id;
+}
+
+// ----------------------------
+
+MediaControlPanelVolumeSliderElement::MediaControlPanelVolumeSliderElement(Document* document)
+ : MediaControlVolumeSliderElement(document)
+{
+}
+
+PassRefPtr<MediaControlPanelVolumeSliderElement> MediaControlPanelVolumeSliderElement::create(Document* document)
+{
+ RefPtr<MediaControlPanelVolumeSliderElement> slider = adoptRef(new MediaControlPanelVolumeSliderElement(document));
+ slider->ensureUserAgentShadowRoot();
+ slider->setType("range");
+ slider->setAttribute(precisionAttr, "float");
+ slider->setAttribute(maxAttr, "1");
+ return slider.release();
+}
+
+const AtomicString& MediaControlPanelVolumeSliderElement::shadowPseudoId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider", AtomicString::ConstructFromLiteral));
+ return id;
+}
+
+// ----------------------------
+
+MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(Document* document)
+ : MediaControlInputElement(document, MediaEnterFullscreenButton)
+{
+}
+
+PassRefPtr<MediaControlFullscreenButtonElement> MediaControlFullscreenButtonElement::create(Document* document)
+{
+ RefPtr<MediaControlFullscreenButtonElement> button = adoptRef(new MediaControlFullscreenButtonElement(document));
+ button->ensureUserAgentShadowRoot();
+ button->setType("button");
+ button->hide();
+ return button.release();
+}
+
+void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event)
+{
+ if (event->type() == eventNames().clickEvent) {
+ // Only use the new full screen API if the fullScreenEnabled setting has
+ // been explicitly enabled. Otherwise, use the old fullscreen API. This
+ // allows apps which embed a WebView to retain the existing full screen
+ // video implementation without requiring them to implement their own full
+ // screen behavior.
+ if (document()->settings() && document()->settings()->fullScreenEnabled()) {
+ if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == toParentMediaElement(this))
+ document()->webkitCancelFullScreen();
+ else
+ document()->requestFullScreenForElement(toParentMediaElement(this), 0, Document::ExemptIFrameAllowFullScreenRequirement);
+ } else
+ mediaController()->enterFullscreen();
+ event->setDefaultHandled();
+ }
+ HTMLInputElement::defaultEventHandler(event);
+}
+
+const AtomicString& MediaControlFullscreenButtonElement::shadowPseudoId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-button", AtomicString::ConstructFromLiteral));
+ return id;
+}
+
+void MediaControlFullscreenButtonElement::setIsFullscreen(bool isFullscreen)
+{
+ setDisplayType(isFullscreen ? MediaExitFullscreenButton : MediaEnterFullscreenButton);
+}
+
+// ----------------------------
+
+MediaControlTimeRemainingDisplayElement::MediaControlTimeRemainingDisplayElement(Document* document)
+ : MediaControlTimeDisplayElement(document, MediaTimeRemainingDisplay)
+{
+}
+
+PassRefPtr<MediaControlTimeRemainingDisplayElement> MediaControlTimeRemainingDisplayElement::create(Document* document)
+{
+ return adoptRef(new MediaControlTimeRemainingDisplayElement(document));
+}
+
+static const AtomicString& getMediaControlTimeRemainingDisplayElementShadowPseudoId()
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-time-remaining-display", AtomicString::ConstructFromLiteral));
+ return id;
+}
+
+const AtomicString& MediaControlTimeRemainingDisplayElement::shadowPseudoId() const
+{
+ return getMediaControlTimeRemainingDisplayElementShadowPseudoId();
+}
+
+// ----------------------------
+
+MediaControlCurrentTimeDisplayElement::MediaControlCurrentTimeDisplayElement(Document* document)
+ : MediaControlTimeDisplayElement(document, MediaCurrentTimeDisplay)
+{
+}
+
+PassRefPtr<MediaControlCurrentTimeDisplayElement> MediaControlCurrentTimeDisplayElement::create(Document* document)
+{
+ return adoptRef(new MediaControlCurrentTimeDisplayElement(document));
+}
+
+static const AtomicString& getMediaControlCurrentTimeDisplayElementShadowPseudoId()
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-current-time-display", AtomicString::ConstructFromLiteral));
+ return id;
+}
+
+const AtomicString& MediaControlCurrentTimeDisplayElement::shadowPseudoId() const
+{
+ return getMediaControlCurrentTimeDisplayElementShadowPseudoId();
+}
+
+// ----------------------------
+
+MediaControlTextTrackContainerElement::MediaControlTextTrackContainerElement(Document* document)
+ : MediaControlDivElement(document, MediaTextTrackDisplayContainer)
+ , m_fontSize(0)
+{
+}
+
+PassRefPtr<MediaControlTextTrackContainerElement> MediaControlTextTrackContainerElement::create(Document* document)
+{
+ RefPtr<MediaControlTextTrackContainerElement> element = adoptRef(new MediaControlTextTrackContainerElement(document));
+ element->hide();
+ return element.release();
+}
+
+RenderObject* MediaControlTextTrackContainerElement::createRenderer(RenderArena* arena, RenderStyle*)
+{
+ return new (arena) RenderTextTrackContainerElement(this);
+}
+
+const AtomicString& MediaControlTextTrackContainerElement::textTrackContainerElementShadowPseudoId()
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-text-track-container", AtomicString::ConstructFromLiteral));
+ return id;
+}
+
+const AtomicString& MediaControlTextTrackContainerElement::shadowPseudoId() const
+{
+ return textTrackContainerElementShadowPseudoId();
+}
+
+void MediaControlTextTrackContainerElement::updateDisplay()
+{
+ if (!mediaController()->closedCaptionsVisible()) {
+ removeChildren();
+ return;
+ }
+
+ HTMLMediaElement* mediaElement = toParentMediaElement(this);
+ // 1. If the media element is an audio element, or is another playback
+ // mechanism with no rendering area, abort these steps. There is nothing to
+ // render.
+ if (!mediaElement || !mediaElement->isVideo())
+ return;
+
+ // 2. Let video be the media element or other playback mechanism.
+ HTMLVideoElement* video = static_cast<HTMLVideoElement*>(mediaElement);
+
+ // 3. Let output be an empty list of absolutely positioned CSS block boxes.
+ Vector<RefPtr<HTMLDivElement> > output;
+
+ // 4. If the user agent is exposing a user interface for video, add to
+ // output one or more completely transparent positioned CSS block boxes that
+ // cover the same region as the user interface.
+
+ // 5. If the last time these rules were run, the user agent was not exposing
+ // a user interface for video, but now it is, let reset be true. Otherwise,
+ // let reset be false.
+
+ // There is nothing to be done explicitly for 4th and 5th steps, as
+ // everything is handled through CSS. The caption box is on top of the
+ // controls box, in a container set with the -webkit-box display property.
+
+ // 6. Let tracks be the subset of video's list of text tracks that have as
+ // their rules for updating the text track rendering these rules for
+ // updating the display of WebVTT text tracks, and whose text track mode is
+ // showing or showing by default.
+ // 7. Let cues be an empty list of text track cues.
+ // 8. For each track track in tracks, append to cues all the cues from
+ // track's list of cues that have their text track cue active flag set.
+ CueList activeCues = video->currentlyActiveCues();
+
+ // 9. If reset is false, then, for each text track cue cue in cues: if cue's
+ // text track cue display state has a set of CSS boxes, then add those boxes
+ // to output, and remove cue from cues.
+
+ // There is nothing explicitly to be done here, as all the caching occurs
+ // within the TextTrackCue instance itself. If parameters of the cue change,
+ // the display tree is cleared.
+
+ // 10. For each text track cue cue in cues that has not yet had
+ // corresponding CSS boxes added to output, in text track cue order, run the
+ // following substeps:
+ for (size_t i = 0; i < activeCues.size(); ++i) {
+ TextTrackCue* cue = activeCues[i].data();
+
+ ASSERT(cue->isActive());
+ if (!cue->track() || !cue->track()->isRendered() || !cue->isActive())
+ continue;
+
+ RefPtr<TextTrackCueBox> displayBox = cue->getDisplayTree(m_videoDisplaySize.size());
+ if (displayBox->hasChildNodes() && !contains(static_cast<Node*>(displayBox.get())))
+ // Note: the display tree of a cue is removed when the active flag of the cue is unset.
+ appendChild(displayBox, ASSERT_NO_EXCEPTION, AttachNow);
+ }
+
+ // 11. Return output.
+ if (hasChildNodes()) {
+ show();
+ if (mediaElement->requiresTextTrackRepresentation()) {
+ if (!m_textTrackRepresentation)
+ m_textTrackRepresentation = TextTrackRepresentation::create(this);
+ mediaElement->setTextTrackRepresentation(m_textTrackRepresentation.get());
+
+ if (Page* page = document()->page())
+ m_textTrackRepresentation->setContentScale(page->deviceScaleFactor());
+
+ m_textTrackRepresentation->update();
+ setInlineStyleProperty(CSSPropertyWidth, String::number(m_videoDisplaySize.size().width()) + "px");
+ setInlineStyleProperty(CSSPropertyHeight, String::number(m_videoDisplaySize.size().height()) + "px");
+ }
+ } else {
+ hide();
+ m_textTrackRepresentation = nullptr;
+ mediaElement->setTextTrackRepresentation(0);
+ removeInlineStyleProperty(CSSPropertyWidth);
+ removeInlineStyleProperty(CSSPropertyHeight);
+ }
+}
+
+void MediaControlTextTrackContainerElement::updateSizes(bool forceUpdate)
+{
+ HTMLMediaElement* mediaElement = toParentMediaElement(this);
+ if (!mediaElement)
+ return;
+
+ if (!document()->page())
+ return;
+
+ IntRect videoBox;
+
+ if (m_textTrackRepresentation)
+ videoBox = m_textTrackRepresentation->bounds();
+ else {
+ if (!mediaElement->renderer() || !mediaElement->renderer()->isVideo())
+ return;
+ videoBox = toRenderVideo(mediaElement->renderer())->videoBox();
+ }
+
+ if (!forceUpdate && m_videoDisplaySize == videoBox)
+ return;
+ m_videoDisplaySize = videoBox;
+
+ if (m_textTrackRepresentation) {
+ setInlineStyleProperty(CSSPropertyWidth, String::number(m_videoDisplaySize.size().width()) + "px");
+ setInlineStyleProperty(CSSPropertyHeight, String::number(m_videoDisplaySize.size().height()) + "px");
+ }
+
+ float smallestDimension = std::min(m_videoDisplaySize.size().height(), m_videoDisplaySize.size().width());
+
+ bool important;
+ float fontSize = smallestDimension * (document()->page()->group().captionPreferences()->captionFontSizeScale(important));
+ if (fontSize != m_fontSize) {
+ m_fontSize = fontSize;
+ setInlineStyleProperty(CSSPropertyFontSize, String::number(fontSize) + "px", important);
+ }
+
+ CueList activeCues = mediaElement->currentlyActiveCues();
+ for (size_t i = 0; i < activeCues.size(); ++i) {
+ TextTrackCue* cue = activeCues[i].data();
+ cue->videoSizeDidChange(m_videoDisplaySize.size());
+ }
+}
+
+void MediaControlTextTrackContainerElement::paintTextTrackRepresentation(GraphicsContext* context, const IntRect& contextRect)
+{
+ if (!hasChildNodes())
+ return;
+
+ RenderObject* renderer = this->renderer();
+ if (!renderer)
+ return;
+
+ Frame* frame = document()->frame();
+ if (!frame)
+ return;
+
+ document()->updateLayout();
+
+ LayoutRect topLevelRect;
+ IntRect paintingRect = pixelSnappedIntRect(renderer->paintingRootRect(topLevelRect));
+
+ // Translate the renderer painting rect into graphics context coordinates.
+ FloatSize translation(-paintingRect.x(), -paintingRect.y());
+
+ // But anchor to the bottom of the graphics context rect.
+ translation.expand(max(0, contextRect.width() - paintingRect.width()), max(0, contextRect.height() - paintingRect.height()));
+
+ context->translate(translation);
+
+ RenderLayer* layer = frame->contentRenderer()->layer();
+ layer->paint(context, paintingRect, PaintBehaviorFlattenCompositingLayers, renderer, 0, RenderLayer::PaintLayerPaintingCompositingAllPhases);
+}
+
+void MediaControlTextTrackContainerElement::textTrackRepresentationBoundsChanged(const IntRect&)
+{
+ updateSizes();
+}
+
+// ----------------------------
+
+} // namespace WebCore
diff --git a/Source/core/html/shadow/MediaControlElements.h b/Source/core/html/shadow/MediaControlElements.h
new file mode 100644
index 0000000..0d52015
--- /dev/null
+++ b/Source/core/html/shadow/MediaControlElements.h
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MediaControlElements_h
+#define MediaControlElements_h
+
+#include "core/html/shadow/MediaControlElementTypes.h"
+#include "core/platform/graphics/TextTrackRepresentation.h"
+
+namespace WebCore {
+
+// ----------------------------
+
+class MediaControlPanelElement FINAL : public MediaControlDivElement {
+public:
+ static PassRefPtr<MediaControlPanelElement> create(Document*);
+
+ void setCanBeDragged(bool);
+ void setIsDisplayed(bool);
+
+ void resetPosition();
+ void makeOpaque();
+ void makeTransparent();
+
+ virtual bool willRespondToMouseMoveEvents() OVERRIDE { return true; }
+ virtual bool willRespondToMouseClickEvents() OVERRIDE { return true; }
+
+private:
+ explicit MediaControlPanelElement(Document*);
+
+ virtual const AtomicString& shadowPseudoId() const OVERRIDE;
+ virtual void defaultEventHandler(Event*) OVERRIDE;
+
+ void startDrag(const LayoutPoint& eventLocation);
+ void continueDrag(const LayoutPoint& eventLocation);
+ void endDrag();
+
+ void startTimer();
+ void stopTimer();
+ void transitionTimerFired(Timer<MediaControlPanelElement>*);
+
+ void setPosition(const LayoutPoint&);
+
+ bool m_canBeDragged;
+ bool m_isBeingDragged;
+ bool m_isDisplayed;
+ bool m_opaque;
+ LayoutPoint m_lastDragEventLocation;
+ LayoutPoint m_cumulativeDragOffset;
+
+ Timer<MediaControlPanelElement> m_transitionTimer;
+};
+
+// ----------------------------
+
+class MediaControlPanelEnclosureElement FINAL : public MediaControlDivElement {
+public:
+ static PassRefPtr<MediaControlPanelEnclosureElement> create(Document*);
+
+private:
+ explicit MediaControlPanelEnclosureElement(Document*);
+ virtual const AtomicString& shadowPseudoId() const OVERRIDE;
+};
+
+// ----------------------------
+
+class MediaControlOverlayEnclosureElement FINAL : public MediaControlDivElement {
+public:
+ static PassRefPtr<MediaControlOverlayEnclosureElement> create(Document*);
+
+private:
+ explicit MediaControlOverlayEnclosureElement(Document*);
+ virtual const AtomicString& shadowPseudoId() const OVERRIDE;
+};
+
+// ----------------------------
+
+class MediaControlPanelMuteButtonElement FINAL : public MediaControlMuteButtonElement {
+public:
+ static PassRefPtr<MediaControlPanelMuteButtonElement> create(Document*, MediaControls*);
+
+ virtual bool willRespondToMouseMoveEvents() OVERRIDE { return true; }
+
+private:
+ explicit MediaControlPanelMuteButtonElement(Document*, MediaControls*);
+
+ virtual const AtomicString& shadowPseudoId() const OVERRIDE;
+ virtual void defaultEventHandler(Event*) OVERRIDE;
+
+ MediaControls* m_controls;
+};
+
+// ----------------------------
+
+class MediaControlVolumeSliderMuteButtonElement FINAL : public MediaControlMuteButtonElement {
+public:
+ static PassRefPtr<MediaControlVolumeSliderMuteButtonElement> create(Document*);
+
+private:
+ explicit MediaControlVolumeSliderMuteButtonElement(Document*);
+ virtual const AtomicString& shadowPseudoId() const OVERRIDE;
+};
+
+
+// ----------------------------
+
+class MediaControlPlayButtonElement FINAL : public MediaControlInputElement {
+public:
+ static PassRefPtr<MediaControlPlayButtonElement> create(Document*);
+
+ virtual bool willRespondToMouseClickEvents() OVERRIDE { return true; }
+ virtual void updateDisplayType() OVERRIDE;
+
+private:
+ explicit MediaControlPlayButtonElement(Document*);
+
+ virtual const AtomicString& shadowPseudoId() const OVERRIDE;
+ virtual void defaultEventHandler(Event*) OVERRIDE;
+};
+
+// ----------------------------
+
+class MediaControlOverlayPlayButtonElement FINAL : public MediaControlInputElement {
+public:
+ static PassRefPtr<MediaControlOverlayPlayButtonElement> create(Document*);
+
+ virtual void updateDisplayType() OVERRIDE;
+
+private:
+ explicit MediaControlOverlayPlayButtonElement(Document*);
+
+ virtual const AtomicString& shadowPseudoId() const OVERRIDE;
+ virtual void defaultEventHandler(Event*) OVERRIDE;
+};
+
+// ----------------------------
+
+class MediaControlToggleClosedCaptionsButtonElement FINAL : public MediaControlInputElement {
+public:
+ static PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> create(Document*, MediaControls*);
+
+ virtual bool willRespondToMouseClickEvents() OVERRIDE { return true; }
+
+ virtual void updateDisplayType() OVERRIDE;
+
+private:
+ explicit MediaControlToggleClosedCaptionsButtonElement(Document*, MediaControls*);
+
+ virtual const AtomicString& shadowPseudoId() const OVERRIDE;
+ virtual void defaultEventHandler(Event*) OVERRIDE;
+};
+
+// ----------------------------
+
+class MediaControlClosedCaptionsContainerElement FINAL : public MediaControlDivElement {
+public:
+ static PassRefPtr<MediaControlClosedCaptionsContainerElement> create(Document*);
+
+ virtual bool willRespondToMouseClickEvents() OVERRIDE { return true; }
+
+private:
+ MediaControlClosedCaptionsContainerElement(Document*);
+ virtual const AtomicString& shadowPseudoId() const OVERRIDE;
+};
+
+// ----------------------------
+
+class MediaControlClosedCaptionsTrackListElement FINAL : public MediaControlDivElement {
+public:
+ static PassRefPtr<MediaControlClosedCaptionsTrackListElement> create(Document*, MediaControls*);
+
+ virtual bool willRespondToMouseClickEvents() OVERRIDE { return true; }
+
+ void updateDisplay();
+ void resetTrackListMenu() { m_trackListHasChanged = true; }
+
+private:
+ MediaControlClosedCaptionsTrackListElement(Document*, MediaControls*);
+
+ void rebuildTrackListMenu();
+
+ virtual const AtomicString& shadowPseudoId() const OVERRIDE;
+ virtual void defaultEventHandler(Event*) OVERRIDE;
+
+ typedef Vector<RefPtr<Element> > TrackMenuItems;
+ TrackMenuItems m_menuItems;
+ typedef HashMap<RefPtr<Element>, RefPtr<TextTrack> > MenuItemToTrackMap;
+ MenuItemToTrackMap m_menuToTrackMap;
+ MediaControls* m_controls;
+ bool m_trackListHasChanged;
+};
+
+// ----------------------------
+
+class MediaControlTimelineElement FINAL : public MediaControlInputElement {
+public:
+ static PassRefPtr<MediaControlTimelineElement> create(Document*, MediaControls*);
+
+ virtual bool willRespondToMouseClickEvents() OVERRIDE;
+
+ void setPosition(double);
+ void setDuration(double);
+
+private:
+ explicit MediaControlTimelineElement(Document*, MediaControls*);
+
+ virtual const AtomicString& shadowPseudoId() const OVERRIDE;
+ virtual void defaultEventHandler(Event*) OVERRIDE;
+
+ MediaControls* m_controls;
+};
+
+// ----------------------------
+
+class MediaControlFullscreenButtonElement FINAL : public MediaControlInputElement {
+public:
+ static PassRefPtr<MediaControlFullscreenButtonElement> create(Document*);
+
+ virtual bool willRespondToMouseClickEvents() OVERRIDE { return true; }
+
+ virtual void setIsFullscreen(bool);
+
+private:
+ explicit MediaControlFullscreenButtonElement(Document*);
+
+ virtual const AtomicString& shadowPseudoId() const OVERRIDE;
+ virtual void defaultEventHandler(Event*) OVERRIDE;
+};
+
+// ----------------------------
+
+class MediaControlPanelVolumeSliderElement FINAL : public MediaControlVolumeSliderElement {
+public:
+ static PassRefPtr<MediaControlPanelVolumeSliderElement> create(Document*);
+
+private:
+ explicit MediaControlPanelVolumeSliderElement(Document*);
+ virtual const AtomicString& shadowPseudoId() const OVERRIDE;
+};
+
+// ----------------------------
+
+class MediaControlTimeRemainingDisplayElement FINAL : public MediaControlTimeDisplayElement {
+public:
+ static PassRefPtr<MediaControlTimeRemainingDisplayElement> create(Document*);
+
+private:
+ explicit MediaControlTimeRemainingDisplayElement(Document*);
+ virtual const AtomicString& shadowPseudoId() const OVERRIDE;
+};
+
+// ----------------------------
+
+class MediaControlCurrentTimeDisplayElement FINAL : public MediaControlTimeDisplayElement {
+public:
+ static PassRefPtr<MediaControlCurrentTimeDisplayElement> create(Document*);
+
+private:
+ explicit MediaControlCurrentTimeDisplayElement(Document*);
+ virtual const AtomicString& shadowPseudoId() const OVERRIDE;
+};
+
+// ----------------------------
+
+class MediaControlTextTrackContainerElement FINAL : public MediaControlDivElement, public TextTrackRepresentationClient {
+public:
+ static PassRefPtr<MediaControlTextTrackContainerElement> create(Document*);
+
+ void updateDisplay();
+ void updateSizes(bool forceUpdate = false);
+ static const AtomicString& textTrackContainerElementShadowPseudoId();
+
+private:
+ explicit MediaControlTextTrackContainerElement(Document*);
+ virtual const AtomicString& shadowPseudoId() const OVERRIDE;
+
+ virtual RenderObject* createRenderer(RenderArena*, RenderStyle*);
+
+ virtual void paintTextTrackRepresentation(GraphicsContext*, const IntRect&) OVERRIDE;
+ virtual void textTrackRepresentationBoundsChanged(const IntRect&) OVERRIDE;
+ OwnPtr<TextTrackRepresentation> m_textTrackRepresentation;
+
+ IntRect m_videoDisplaySize;
+ float m_fontSize;
+};
+
+
+} // namespace WebCore
+
+#endif // MediaControlElements_h
diff --git a/Source/core/html/shadow/MediaControls.cpp b/Source/core/html/shadow/MediaControls.cpp
new file mode 100644
index 0000000..3c4d5d9
--- /dev/null
+++ b/Source/core/html/shadow/MediaControls.cpp
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "core/html/shadow/MediaControls.h"
+
+#include "core/dom/ExceptionCodePlaceholder.h"
+#include "core/page/Settings.h"
+
+namespace WebCore {
+
+static const double timeWithoutMouseMovementBeforeHidingFullscreenControls = 3;
+
+MediaControls::MediaControls(Document* document)
+ : HTMLDivElement(HTMLNames::divTag, document)
+ , m_mediaController(0)
+ , m_panel(0)
+ , m_textDisplayContainer(0)
+ , m_playButton(0)
+ , m_currentTimeDisplay(0)
+ , m_timeline(0)
+ , m_panelMuteButton(0)
+ , m_volumeSlider(0)
+ , m_toggleClosedCaptionsButton(0)
+ , m_fullScreenButton(0)
+ , m_hideFullscreenControlsTimer(this, &MediaControls::hideFullscreenControlsTimerFired)
+ , m_isFullscreen(false)
+ , m_isMouseOverControls(false)
+{
+}
+
+void MediaControls::setMediaController(MediaControllerInterface* controller)
+{
+ if (m_mediaController == controller)
+ return;
+ m_mediaController = controller;
+
+ if (m_panel)
+ m_panel->setMediaController(controller);
+ if (m_textDisplayContainer)
+ m_textDisplayContainer->setMediaController(controller);
+ if (m_playButton)
+ m_playButton->setMediaController(controller);
+ if (m_currentTimeDisplay)
+ m_currentTimeDisplay->setMediaController(controller);
+ if (m_timeline)
+ m_timeline->setMediaController(controller);
+ if (m_panelMuteButton)
+ m_panelMuteButton->setMediaController(controller);
+ if (m_volumeSlider)
+ m_volumeSlider->setMediaController(controller);
+ if (m_toggleClosedCaptionsButton)
+ m_toggleClosedCaptionsButton->setMediaController(controller);
+ if (m_fullScreenButton)
+ m_fullScreenButton->setMediaController(controller);
+}
+
+void MediaControls::reset()
+{
+ Page* page = document()->page();
+ if (!page)
+ return;
+
+ m_playButton->updateDisplayType();
+
+ updateCurrentTimeDisplay();
+
+ double duration = m_mediaController->duration();
+ if (std::isfinite(duration) || page->theme()->hasOwnDisabledStateHandlingFor(MediaSliderPart)) {
+ m_timeline->setDuration(duration);
+ m_timeline->setPosition(m_mediaController->currentTime());
+ }
+
+ if (m_mediaController->hasAudio() || page->theme()->hasOwnDisabledStateHandlingFor(MediaMuteButtonPart))
+ m_panelMuteButton->show();
+ else
+ m_panelMuteButton->hide();
+
+ if (m_volumeSlider) {
+ if (!m_mediaController->hasAudio())
+ m_volumeSlider->hide();
+ else {
+ m_volumeSlider->show();
+ m_volumeSlider->setVolume(m_mediaController->volume());
+ }
+ }
+
+ refreshClosedCaptionsButtonVisibility();
+
+ if (m_fullScreenButton) {
+ if (m_mediaController->supportsFullscreen() && m_mediaController->hasVideo())
+ m_fullScreenButton->show();
+ else
+ m_fullScreenButton->hide();
+ }
+
+ makeOpaque();
+}
+
+void MediaControls::reportedError()
+{
+ Page* page = document()->page();
+ if (!page)
+ return;
+
+ if (!page->theme()->hasOwnDisabledStateHandlingFor(MediaMuteButtonPart)) {
+ m_panelMuteButton->hide();
+ m_volumeSlider->hide();
+ }
+
+ if (m_toggleClosedCaptionsButton && !page->theme()->hasOwnDisabledStateHandlingFor(MediaToggleClosedCaptionsButtonPart))
+ m_toggleClosedCaptionsButton->hide();
+
+ if (m_fullScreenButton && !page->theme()->hasOwnDisabledStateHandlingFor(MediaEnterFullscreenButtonPart))
+ m_fullScreenButton->hide();
+}
+
+void MediaControls::loadedMetadata()
+{
+ reset();
+}
+
+void MediaControls::show()
+{
+ makeOpaque();
+ m_panel->setIsDisplayed(true);
+ m_panel->show();
+}
+
+void MediaControls::hide()
+{
+ m_panel->setIsDisplayed(false);
+ m_panel->hide();
+}
+
+void MediaControls::makeOpaque()
+{
+ m_panel->makeOpaque();
+}
+
+void MediaControls::makeTransparent()
+{
+ m_panel->makeTransparent();
+}
+
+bool MediaControls::shouldHideControls()
+{
+ return !m_panel->hovered();
+}
+
+void MediaControls::bufferingProgressed()
+{
+ // We only need to update buffering progress when paused, during normal
+ // playback playbackProgressed() will take care of it.
+ if (m_mediaController->paused())
+ m_timeline->setPosition(m_mediaController->currentTime());
+}
+
+void MediaControls::playbackStarted()
+{
+ m_playButton->updateDisplayType();
+ m_timeline->setPosition(m_mediaController->currentTime());
+ updateCurrentTimeDisplay();
+
+ if (m_isFullscreen)
+ startHideFullscreenControlsTimer();
+}
+
+void MediaControls::playbackProgressed()
+{
+ m_timeline->setPosition(m_mediaController->currentTime());
+ updateCurrentTimeDisplay();
+
+ if (!m_isMouseOverControls && m_mediaController->hasVideo())
+ makeTransparent();
+}
+
+void MediaControls::playbackStopped()
+{
+ m_playButton->updateDisplayType();
+ m_timeline->setPosition(m_mediaController->currentTime());
+ updateCurrentTimeDisplay();
+ makeOpaque();
+
+ stopHideFullscreenControlsTimer();
+}
+
+void MediaControls::showVolumeSlider()
+{
+ if (!m_mediaController->hasAudio())
+ return;
+
+ m_volumeSlider->show();
+}
+
+void MediaControls::changedMute()
+{
+ m_panelMuteButton->changedMute();
+}
+
+void MediaControls::changedVolume()
+{
+ if (m_volumeSlider)
+ m_volumeSlider->setVolume(m_mediaController->volume());
+}
+
+void MediaControls::changedClosedCaptionsVisibility()
+{
+ if (m_toggleClosedCaptionsButton)
+ m_toggleClosedCaptionsButton->updateDisplayType();
+}
+
+void MediaControls::refreshClosedCaptionsButtonVisibility()
+{
+ if (!m_toggleClosedCaptionsButton)
+ return;
+
+ if (m_mediaController->hasClosedCaptions())
+ m_toggleClosedCaptionsButton->show();
+ else
+ m_toggleClosedCaptionsButton->hide();
+}
+
+void MediaControls::closedCaptionTracksChanged()
+{
+ refreshClosedCaptionsButtonVisibility();
+}
+
+void MediaControls::enteredFullscreen()
+{
+ m_isFullscreen = true;
+ m_fullScreenButton->setIsFullscreen(true);
+
+ if (Page* page = document()->page())
+ page->chrome()->setCursorHiddenUntilMouseMoves(true);
+
+ startHideFullscreenControlsTimer();
+}
+
+void MediaControls::exitedFullscreen()
+{
+ m_isFullscreen = false;
+ m_fullScreenButton->setIsFullscreen(false);
+ stopHideFullscreenControlsTimer();
+}
+
+void MediaControls::defaultEventHandler(Event* event)
+{
+ HTMLDivElement::defaultEventHandler(event);
+
+ if (event->type() == eventNames().mouseoverEvent) {
+ if (!containsRelatedTarget(event)) {
+ m_isMouseOverControls = true;
+ if (!m_mediaController->canPlay()) {
+ makeOpaque();
+ if (shouldHideControls())
+ startHideFullscreenControlsTimer();
+ }
+ }
+ return;
+ }
+
+ if (event->type() == eventNames().mouseoutEvent) {
+ if (!containsRelatedTarget(event)) {
+ m_isMouseOverControls = false;
+ stopHideFullscreenControlsTimer();
+ }
+ return;
+ }
+
+ if (event->type() == eventNames().mousemoveEvent) {
+ if (m_isFullscreen) {
+ // When we get a mouse move in fullscreen mode, show the media controls, and start a timer
+ // that will hide the media controls after a 3 seconds without a mouse move.
+ makeOpaque();
+ if (shouldHideControls())
+ startHideFullscreenControlsTimer();
+ }
+ return;
+ }
+}
+
+void MediaControls::hideFullscreenControlsTimerFired(Timer<MediaControls>*)
+{
+ if (m_mediaController->paused())
+ return;
+
+ if (!m_isFullscreen)
+ return;
+
+ if (!shouldHideControls())
+ return;
+
+ if (Page* page = document()->page())
+ page->chrome()->setCursorHiddenUntilMouseMoves(true);
+
+ makeTransparent();
+}
+
+void MediaControls::startHideFullscreenControlsTimer()
+{
+ if (!m_isFullscreen)
+ return;
+
+ Page* page = document()->page();
+ if (!page)
+ return;
+
+ m_hideFullscreenControlsTimer.startOneShot(timeWithoutMouseMovementBeforeHidingFullscreenControls);
+}
+
+void MediaControls::stopHideFullscreenControlsTimer()
+{
+ m_hideFullscreenControlsTimer.stop();
+}
+
+const AtomicString& MediaControls::shadowPseudoId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls"));
+ return id;
+}
+
+bool MediaControls::containsRelatedTarget(Event* event)
+{
+ if (!event->isMouseEvent())
+ return false;
+ EventTarget* relatedTarget = static_cast<MouseEvent*>(event)->relatedTarget();
+ if (!relatedTarget)
+ return false;
+ return contains(relatedTarget->toNode());
+}
+
+void MediaControls::createTextTrackDisplay()
+{
+ if (m_textDisplayContainer)
+ return;
+
+ RefPtr<MediaControlTextTrackContainerElement> textDisplayContainer = MediaControlTextTrackContainerElement::create(document());
+ m_textDisplayContainer = textDisplayContainer.get();
+
+ if (m_mediaController)
+ m_textDisplayContainer->setMediaController(m_mediaController);
+
+ // Insert it before the first controller element so it always displays behind the controls.
+ insertBefore(textDisplayContainer.release(), m_panel, IGNORE_EXCEPTION, AttachLazily);
+}
+
+void MediaControls::showTextTrackDisplay()
+{
+ if (!m_textDisplayContainer)
+ createTextTrackDisplay();
+ m_textDisplayContainer->show();
+}
+
+void MediaControls::hideTextTrackDisplay()
+{
+ if (!m_textDisplayContainer)
+ createTextTrackDisplay();
+ m_textDisplayContainer->hide();
+}
+
+void MediaControls::updateTextTrackDisplay()
+{
+ if (!m_textDisplayContainer)
+ createTextTrackDisplay();
+
+ m_textDisplayContainer->updateDisplay();
+}
+
+void MediaControls::textTrackPreferencesChanged()
+{
+ if (m_textDisplayContainer)
+ m_textDisplayContainer->updateSizes(true);
+ closedCaptionTracksChanged();
+}
+
+}
diff --git a/Source/core/html/shadow/MediaControls.h b/Source/core/html/shadow/MediaControls.h
new file mode 100644
index 0000000..ef336f7
--- /dev/null
+++ b/Source/core/html/shadow/MediaControls.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MediaControls_h
+#define MediaControls_h
+
+#include "HTMLNames.h"
+#include "core/dom/MouseEvent.h"
+#include "core/dom/Text.h"
+#include "core/html/HTMLDivElement.h"
+#include "core/html/HTMLMediaElement.h"
+#include "core/html/shadow/MediaControlElements.h"
+#include "core/page/Chrome.h"
+#include "core/page/Page.h"
+#include "core/rendering/RenderTheme.h"
+#include <wtf/RefPtr.h>
+
+#include "core/html/track/TextTrackCue.h"
+
+namespace WebCore {
+
+class Document;
+class Event;
+class Page;
+class MediaPlayer;
+
+class RenderBox;
+class RenderMedia;
+
+// An abstract class with the media control elements that all ports support.
+class MediaControls : public HTMLDivElement {
+ public:
+ virtual ~MediaControls() {}
+
+ // This function is to be implemented in your port-specific media
+ // controls implementation since it will return a child instance.
+ static PassRefPtr<MediaControls> create(Document*);
+
+ virtual void setMediaController(MediaControllerInterface*);
+
+ virtual void reset();
+ virtual void reportedError();
+ virtual void loadedMetadata();
+
+ virtual void show();
+ virtual void hide();
+ virtual void makeOpaque();
+ virtual void makeTransparent();
+ virtual bool shouldHideControls();
+
+ virtual void bufferingProgressed();
+ virtual void playbackStarted();
+ virtual void playbackProgressed();
+ virtual void playbackStopped();
+
+ virtual void updateStatusDisplay() { };
+ virtual void updateCurrentTimeDisplay() = 0;
+ virtual void showVolumeSlider();
+
+ virtual void changedMute();
+ virtual void changedVolume();
+
+ virtual void changedClosedCaptionsVisibility();
+ virtual void refreshClosedCaptionsButtonVisibility();
+ virtual void toggleClosedCaptionTrackList() { }
+ virtual void closedCaptionTracksChanged();
+
+ virtual void enteredFullscreen();
+ virtual void exitedFullscreen();
+
+ virtual bool willRespondToMouseMoveEvents() OVERRIDE { return true; }
+
+ virtual void hideFullscreenControlsTimerFired(Timer<MediaControls>*);
+ virtual void startHideFullscreenControlsTimer();
+ virtual void stopHideFullscreenControlsTimer();
+
+ virtual void createTextTrackDisplay();
+ virtual void showTextTrackDisplay();
+ virtual void hideTextTrackDisplay();
+ virtual void updateTextTrackDisplay();
+ virtual void textTrackPreferencesChanged();
+
+protected:
+ explicit MediaControls(Document*);
+
+ virtual void defaultEventHandler(Event*);
+
+ virtual bool containsRelatedTarget(Event*);
+
+ MediaControllerInterface* m_mediaController;
+
+ // Container for the media control elements.
+ MediaControlPanelElement* m_panel;
+
+ // Container for the text track cues.
+ MediaControlTextTrackContainerElement* m_textDisplayContainer;
+
+ // Media control elements.
+ MediaControlPlayButtonElement* m_playButton;
+ MediaControlCurrentTimeDisplayElement* m_currentTimeDisplay;
+ MediaControlTimelineElement* m_timeline;
+ MediaControlPanelMuteButtonElement* m_panelMuteButton;
+ MediaControlPanelVolumeSliderElement* m_volumeSlider;
+ MediaControlToggleClosedCaptionsButtonElement* m_toggleClosedCaptionsButton;
+ MediaControlFullscreenButtonElement* m_fullScreenButton;
+
+ Timer<MediaControls> m_hideFullscreenControlsTimer;
+ bool m_isFullscreen;
+ bool m_isMouseOverControls;
+
+private:
+ virtual bool isMediaControls() const { return true; }
+
+ virtual const AtomicString& shadowPseudoId() const;
+};
+
+inline MediaControls* toMediaControls(Node* node)
+{
+ ASSERT_WITH_SECURITY_IMPLICATION(!node || node->isMediaControls());
+ return static_cast<MediaControls*>(node);
+}
+
+// This will catch anyone doing an unneccessary cast.
+void toMediaControls(const MediaControls*);
+
+}
+
+#endif
diff --git a/Source/core/html/shadow/MediaControlsChromium.cpp b/Source/core/html/shadow/MediaControlsChromium.cpp
new file mode 100644
index 0000000..4d94377
--- /dev/null
+++ b/Source/core/html/shadow/MediaControlsChromium.cpp
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "core/html/shadow/MediaControlsChromium.h"
+
+#include "core/dom/ExceptionCodePlaceholder.h"
+
+using namespace std;
+
+namespace WebCore {
+
+MediaControlsChromium::MediaControlsChromium(Document* document)
+ : MediaControls(document)
+ , m_durationDisplay(0)
+ , m_enclosure(0)
+{
+}
+
+// MediaControls::create() for Android is defined in MediaControlsChromiumAndroid.cpp.
+#if !OS(ANDROID)
+PassRefPtr<MediaControls> MediaControls::create(Document* document)
+{
+ return MediaControlsChromium::createControls(document);
+}
+#endif
+
+PassRefPtr<MediaControlsChromium> MediaControlsChromium::createControls(Document* document)
+{
+ if (!document->page())
+ return 0;
+
+ RefPtr<MediaControlsChromium> controls = adoptRef(new MediaControlsChromium(document));
+
+ if (controls->initializeControls(document))
+ return controls.release();
+
+ return 0;
+}
+
+bool MediaControlsChromium::initializeControls(Document* document)
+{
+ // Create an enclosing element for the panel so we can visually offset the controls correctly.
+ RefPtr<MediaControlPanelEnclosureElement> enclosure = MediaControlPanelEnclosureElement::create(document);
+
+ RefPtr<MediaControlPanelElement> panel = MediaControlPanelElement::create(document);
+
+ ExceptionCode ec;
+
+ RefPtr<MediaControlPlayButtonElement> playButton = MediaControlPlayButtonElement::create(document);
+ m_playButton = playButton.get();
+ panel->appendChild(playButton.release(), ec, AttachLazily);
+ if (ec)
+ return false;
+
+ RefPtr<MediaControlTimelineElement> timeline = MediaControlTimelineElement::create(document, this);
+ m_timeline = timeline.get();
+ panel->appendChild(timeline.release(), ec, AttachLazily);
+ if (ec)
+ return false;
+
+ RefPtr<MediaControlCurrentTimeDisplayElement> currentTimeDisplay = MediaControlCurrentTimeDisplayElement::create(document);
+ m_currentTimeDisplay = currentTimeDisplay.get();
+ m_currentTimeDisplay->hide();
+ panel->appendChild(currentTimeDisplay.release(), ec, AttachLazily);
+ if (ec)
+ return false;
+
+ RefPtr<MediaControlTimeRemainingDisplayElement> durationDisplay = MediaControlTimeRemainingDisplayElement::create(document);
+ m_durationDisplay = durationDisplay.get();
+ panel->appendChild(durationDisplay.release(), ec, AttachLazily);
+ if (ec)
+ return false;
+
+ RefPtr<MediaControlPanelMuteButtonElement> panelMuteButton = MediaControlPanelMuteButtonElement::create(document, this);
+ m_panelMuteButton = panelMuteButton.get();
+ panel->appendChild(panelMuteButton.release(), ec, AttachLazily);
+ if (ec)
+ return false;
+
+ RefPtr<MediaControlPanelVolumeSliderElement> slider = MediaControlPanelVolumeSliderElement::create(document);
+ m_volumeSlider = slider.get();
+ m_volumeSlider->setClearMutedOnUserInteraction(true);
+ panel->appendChild(slider.release(), ec, AttachLazily);
+ if (ec)
+ return false;
+
+ if (document->page()->theme()->supportsClosedCaptioning()) {
+ RefPtr<MediaControlToggleClosedCaptionsButtonElement> toggleClosedCaptionsButton = MediaControlToggleClosedCaptionsButtonElement::create(document, this);
+ m_toggleClosedCaptionsButton = toggleClosedCaptionsButton.get();
+ panel->appendChild(toggleClosedCaptionsButton.release(), ec, AttachLazily);
+ if (ec)
+ return false;
+ }
+
+ RefPtr<MediaControlFullscreenButtonElement> fullscreenButton = MediaControlFullscreenButtonElement::create(document);
+ m_fullScreenButton = fullscreenButton.get();
+ panel->appendChild(fullscreenButton.release(), ec, AttachLazily);
+ if (ec)
+ return false;
+
+ m_panel = panel.get();
+ enclosure->appendChild(panel.release(), ec, AttachLazily);
+ if (ec)
+ return false;
+
+ m_enclosure = enclosure.get();
+ appendChild(enclosure.release(), ec, AttachLazily);
+ if (ec)
+ return false;
+
+ return true;
+}
+
+void MediaControlsChromium::setMediaController(MediaControllerInterface* controller)
+{
+ if (m_mediaController == controller)
+ return;
+
+ MediaControls::setMediaController(controller);
+
+ if (m_durationDisplay)
+ m_durationDisplay->setMediaController(controller);
+ if (m_enclosure)
+ m_enclosure->setMediaController(controller);
+}
+
+void MediaControlsChromium::reset()
+{
+ Page* page = document()->page();
+ if (!page)
+ return;
+
+ double duration = m_mediaController->duration();
+ m_durationDisplay->setInnerText(page->theme()->formatMediaControlsTime(duration), ASSERT_NO_EXCEPTION);
+ m_durationDisplay->setCurrentValue(duration);
+
+ MediaControls::reset();
+}
+
+void MediaControlsChromium::playbackStarted()
+{
+ m_currentTimeDisplay->show();
+ m_durationDisplay->hide();
+
+ MediaControls::playbackStarted();
+}
+
+void MediaControlsChromium::updateCurrentTimeDisplay()
+{
+ double now = m_mediaController->currentTime();
+ double duration = m_mediaController->duration();
+
+ Page* page = document()->page();
+ if (!page)
+ return;
+
+ // After seek, hide duration display and show current time.
+ if (now > 0) {
+ m_currentTimeDisplay->show();
+ m_durationDisplay->hide();
+ }
+
+ // Allow the theme to format the time.
+ m_currentTimeDisplay->setInnerText(page->theme()->formatMediaControlsCurrentTime(now, duration), IGNORE_EXCEPTION);
+ m_currentTimeDisplay->setCurrentValue(now);
+}
+
+void MediaControlsChromium::changedMute()
+{
+ MediaControls::changedMute();
+
+ if (m_mediaController->muted())
+ m_volumeSlider->setVolume(0);
+ else
+ m_volumeSlider->setVolume(m_mediaController->volume());
+}
+
+void MediaControlsChromium::createTextTrackDisplay()
+{
+ if (m_textDisplayContainer)
+ return;
+
+ RefPtr<MediaControlTextTrackContainerElement> textDisplayContainer = MediaControlTextTrackContainerElement::create(document());
+ m_textDisplayContainer = textDisplayContainer.get();
+
+ if (m_mediaController)
+ m_textDisplayContainer->setMediaController(m_mediaController);
+
+ insertTextTrackContainer(textDisplayContainer.release());
+}
+
+void MediaControlsChromium::insertTextTrackContainer(PassRefPtr<MediaControlTextTrackContainerElement> textTrackContainer)
+{
+ // Insert it before the first controller element so it always displays behind the controls.
+ // In the Chromium case, that's the enclosure element.
+ insertBefore(textTrackContainer, m_enclosure, ASSERT_NO_EXCEPTION, AttachLazily);
+}
+
+
+}
diff --git a/Source/core/html/shadow/MediaControlsChromium.h b/Source/core/html/shadow/MediaControlsChromium.h
new file mode 100644
index 0000000..3e8b7c7
--- /dev/null
+++ b/Source/core/html/shadow/MediaControlsChromium.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MediaControlsChromium_h
+#define MediaControlsChromium_h
+
+#include "core/html/shadow/MediaControls.h"
+
+namespace WebCore {
+
+class MediaControlsChromium : public MediaControls {
+public:
+ // Called from port-specific parent create function to create custom controls.
+ static PassRefPtr<MediaControlsChromium> createControls(Document*);
+
+ virtual void setMediaController(MediaControllerInterface*) OVERRIDE;
+
+ virtual void reset() OVERRIDE;
+
+ virtual void playbackStarted() OVERRIDE;
+
+ void changedMute() OVERRIDE;
+
+ virtual void updateCurrentTimeDisplay() OVERRIDE;
+
+ void createTextTrackDisplay() OVERRIDE;
+
+ virtual void insertTextTrackContainer(PassRefPtr<MediaControlTextTrackContainerElement>);
+
+protected:
+ explicit MediaControlsChromium(Document*);
+
+ bool initializeControls(Document*);
+
+private:
+ MediaControlTimeRemainingDisplayElement* m_durationDisplay;
+ MediaControlPanelEnclosureElement* m_enclosure;
+};
+
+}
+
+#endif
diff --git a/Source/core/html/shadow/MediaControlsChromiumAndroid.cpp b/Source/core/html/shadow/MediaControlsChromiumAndroid.cpp
new file mode 100644
index 0000000..34e61af
--- /dev/null
+++ b/Source/core/html/shadow/MediaControlsChromiumAndroid.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "core/html/shadow/MediaControlsChromiumAndroid.h"
+
+namespace WebCore {
+
+MediaControlsChromiumAndroid::MediaControlsChromiumAndroid(Document* document)
+ : MediaControlsChromium(document)
+ , m_overlayPlayButton(0)
+ , m_overlayEnclosure(0)
+{
+}
+
+PassRefPtr<MediaControls> MediaControls::create(Document* document)
+{
+ return MediaControlsChromiumAndroid::createControls(document);
+}
+
+PassRefPtr<MediaControlsChromiumAndroid> MediaControlsChromiumAndroid::createControls(Document* document)
+{
+ if (!document->page())
+ return 0;
+
+ RefPtr<MediaControlsChromiumAndroid> controls = adoptRef(new MediaControlsChromiumAndroid(document));
+
+ ExceptionCode ec;
+
+ RefPtr<MediaControlOverlayEnclosureElement> overlayEnclosure = MediaControlOverlayEnclosureElement::create(document);
+ RefPtr<MediaControlOverlayPlayButtonElement> overlayPlayButton = MediaControlOverlayPlayButtonElement::create(document);
+ controls->m_overlayPlayButton = overlayPlayButton.get();
+ overlayEnclosure->appendChild(overlayPlayButton.release(), ec, AttachLazily);
+ if (ec)
+ return 0;
+
+ controls->m_overlayEnclosure = overlayEnclosure.get();
+ controls->appendChild(overlayEnclosure.release(), ec, AttachLazily);
+ if (ec)
+ return 0;
+
+ if (controls->initializeControls(document))
+ return controls.release();
+
+ return 0;
+}
+
+void MediaControlsChromiumAndroid::setMediaController(MediaControllerInterface* controller)
+{
+ if (m_overlayPlayButton)
+ m_overlayPlayButton->setMediaController(controller);
+ if (m_overlayEnclosure)
+ m_overlayEnclosure->setMediaController(controller);
+ MediaControlsChromium::setMediaController(controller);
+}
+
+void MediaControlsChromiumAndroid::playbackStarted()
+{
+ m_overlayPlayButton->updateDisplayType();
+ MediaControlsChromium::playbackStarted();
+}
+
+void MediaControlsChromiumAndroid::playbackStopped()
+{
+ m_overlayPlayButton->updateDisplayType();
+ MediaControlsChromium::playbackStopped();
+}
+
+void MediaControlsChromiumAndroid::insertTextTrackContainer(PassRefPtr<MediaControlTextTrackContainerElement> textTrackContainer)
+{
+ // Insert it before the overlay play button so it always displays behind it.
+ m_overlayEnclosure->insertBefore(textTrackContainer, m_overlayPlayButton, ASSERT_NO_EXCEPTION, AttachLazily);
+}
+}
diff --git a/Source/core/html/shadow/MediaControlsChromiumAndroid.h b/Source/core/html/shadow/MediaControlsChromiumAndroid.h
new file mode 100644
index 0000000..2980160
--- /dev/null
+++ b/Source/core/html/shadow/MediaControlsChromiumAndroid.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MediaControlsChromiumAndroid_h
+#define MediaControlsChromiumAndroid_h
+
+#include "core/html/shadow/MediaControls.h"
+#include "core/html/shadow/MediaControlsChromium.h"
+
+namespace WebCore {
+
+class MediaControlsChromiumAndroid : public MediaControlsChromium {
+public:
+ static PassRefPtr<MediaControlsChromiumAndroid> createControls(Document*);
+
+ virtual void setMediaController(MediaControllerInterface*) OVERRIDE;
+ virtual void playbackStarted() OVERRIDE;
+ virtual void playbackStopped() OVERRIDE;
+
+ void insertTextTrackContainer(PassRefPtr<MediaControlTextTrackContainerElement>) OVERRIDE;
+
+private:
+ explicit MediaControlsChromiumAndroid(Document*);
+
+ MediaControlOverlayPlayButtonElement* m_overlayPlayButton;
+ MediaControlOverlayEnclosureElement* m_overlayEnclosure;
+};
+
+}
+
+#endif
diff --git a/Source/core/html/shadow/MeterShadowElement.cpp b/Source/core/html/shadow/MeterShadowElement.cpp
new file mode 100644
index 0000000..826bf93
--- /dev/null
+++ b/Source/core/html/shadow/MeterShadowElement.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "core/html/shadow/MeterShadowElement.h"
+
+#include "CSSPropertyNames.h"
+#include "HTMLNames.h"
+#include "core/css/StylePropertySet.h"
+#include "core/dom/ShadowRoot.h"
+#include "core/html/HTMLMeterElement.h"
+#include "core/rendering/RenderMeter.h"
+#include "core/rendering/RenderTheme.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+MeterShadowElement::MeterShadowElement(Document* document)
+ : HTMLDivElement(HTMLNames::divTag, document)
+{
+}
+
+HTMLMeterElement* MeterShadowElement::meterElement() const
+{
+ return toHTMLMeterElement(shadowHost());
+}
+
+bool MeterShadowElement::rendererIsNeeded(const NodeRenderingContext& context)
+{
+ RenderObject* render = meterElement()->renderer();
+ return render && !render->theme()->supportsMeter(render->style()->appearance()) && HTMLDivElement::rendererIsNeeded(context);
+}
+
+MeterInnerElement::MeterInnerElement(Document* document)
+ : MeterShadowElement(document)
+{
+ DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-meter-inner-element", AtomicString::ConstructFromLiteral));
+ setPseudo(pseudoId);
+}
+
+bool MeterInnerElement::rendererIsNeeded(const NodeRenderingContext& context)
+{
+ if (meterElement()->hasAuthorShadowRoot())
+ return HTMLDivElement::rendererIsNeeded(context);
+
+ RenderObject* render = meterElement()->renderer();
+ return render && !render->theme()->supportsMeter(render->style()->appearance()) && HTMLDivElement::rendererIsNeeded(context);
+}
+
+RenderObject* MeterInnerElement::createRenderer(RenderArena* arena, RenderStyle*)
+{
+ return new (arena) RenderMeter(this);
+}
+
+const AtomicString& MeterValueElement::valuePseudoId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, optimumPseudoId, ("-webkit-meter-optimum-value", AtomicString::ConstructFromLiteral));
+ DEFINE_STATIC_LOCAL(AtomicString, suboptimumPseudoId, ("-webkit-meter-suboptimum-value", AtomicString::ConstructFromLiteral));
+ DEFINE_STATIC_LOCAL(AtomicString, evenLessGoodPseudoId, ("-webkit-meter-even-less-good-value", AtomicString::ConstructFromLiteral));
+
+ HTMLMeterElement* meter = meterElement();
+ if (!meter)
+ return optimumPseudoId;
+
+ switch (meter->gaugeRegion()) {
+ case HTMLMeterElement::GaugeRegionOptimum:
+ return optimumPseudoId;
+ case HTMLMeterElement::GaugeRegionSuboptimal:
+ return suboptimumPseudoId;
+ case HTMLMeterElement::GaugeRegionEvenLessGood:
+ return evenLessGoodPseudoId;
+ default:
+ ASSERT_NOT_REACHED();
+ return optimumPseudoId;
+ }
+}
+
+void MeterValueElement::setWidthPercentage(double width)
+{
+ setInlineStyleProperty(CSSPropertyWidth, width, CSSPrimitiveValue::CSS_PERCENTAGE);
+}
+
+}
diff --git a/Source/core/html/shadow/MeterShadowElement.h b/Source/core/html/shadow/MeterShadowElement.h
new file mode 100644
index 0000000..3f4bdd4
--- /dev/null
+++ b/Source/core/html/shadow/MeterShadowElement.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef MeterShadowElement_h
+#define MeterShadowElement_h
+
+#include "core/html/HTMLDivElement.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class HTMLMeterElement;
+class RenderMeter;
+
+class MeterShadowElement : public HTMLDivElement {
+public:
+ MeterShadowElement(Document*);
+ HTMLMeterElement* meterElement() const;
+
+private:
+ virtual bool rendererIsNeeded(const NodeRenderingContext&);
+};
+
+class MeterInnerElement FINAL : public MeterShadowElement {
+public:
+ MeterInnerElement(Document*);
+ static PassRefPtr<MeterInnerElement> create(Document*);
+
+private:
+ virtual bool rendererIsNeeded(const NodeRenderingContext&) OVERRIDE;
+ virtual RenderObject* createRenderer(RenderArena*, RenderStyle*) OVERRIDE;
+};
+
+inline PassRefPtr<MeterInnerElement> MeterInnerElement::create(Document* document)
+{
+ return adoptRef(new MeterInnerElement(document));
+}
+
+class MeterBarElement FINAL : public MeterShadowElement {
+public:
+ MeterBarElement(Document* document)
+ : MeterShadowElement(document)
+ {
+ DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-meter-bar", AtomicString::ConstructFromLiteral));
+ setPseudo(pseudoId);
+ }
+
+ static PassRefPtr<MeterBarElement> create(Document*);
+};
+
+inline PassRefPtr<MeterBarElement> MeterBarElement::create(Document* document)
+{
+ return adoptRef(new MeterBarElement(document));
+}
+
+class MeterValueElement FINAL : public MeterShadowElement {
+public:
+ MeterValueElement(Document* document)
+ : MeterShadowElement(document)
+ {
+ updatePseudo();
+ }
+
+ static PassRefPtr<MeterValueElement> create(Document*);
+ void setWidthPercentage(double);
+ void updatePseudo() { setPseudo(valuePseudoId()); }
+
+private:
+ const AtomicString& valuePseudoId() const;
+};
+
+inline PassRefPtr<MeterValueElement> MeterValueElement::create(Document* document)
+{
+ return adoptRef(new MeterValueElement(document));
+}
+
+}
+
+#endif // MeterShadowElement_h
diff --git a/Source/core/html/shadow/PickerIndicatorElement.cpp b/Source/core/html/shadow/PickerIndicatorElement.cpp
new file mode 100644
index 0000000..f377200
--- /dev/null
+++ b/Source/core/html/shadow/PickerIndicatorElement.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
+#include "core/html/shadow/PickerIndicatorElement.h"
+
+#include "core/dom/Event.h"
+#include "core/page/Chrome.h"
+#include "core/page/Page.h"
+#include "core/rendering/RenderDetailsMarker.h"
+
+using namespace WTF::Unicode;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+inline PickerIndicatorElement::PickerIndicatorElement(Document* document, PickerIndicatorOwner& pickerIndicatorOwner)
+ : HTMLDivElement(divTag, document)
+ , m_pickerIndicatorOwner(&pickerIndicatorOwner)
+{
+ setPseudo(AtomicString("-webkit-calendar-picker-indicator", AtomicString::ConstructFromLiteral));
+}
+
+PassRefPtr<PickerIndicatorElement> PickerIndicatorElement::create(Document* document, PickerIndicatorOwner& pickerIndicatorOwner)
+{
+ return adoptRef(new PickerIndicatorElement(document, pickerIndicatorOwner));
+}
+
+PickerIndicatorElement::~PickerIndicatorElement()
+{
+ closePopup();
+ ASSERT(!m_chooser);
+}
+
+RenderObject* PickerIndicatorElement::createRenderer(RenderArena* arena, RenderStyle*)
+{
+ return new (arena) RenderDetailsMarker(this);
+}
+
+void PickerIndicatorElement::defaultEventHandler(Event* event)
+{
+ if (!renderer())
+ return;
+ if (!m_pickerIndicatorOwner || m_pickerIndicatorOwner->isPickerIndicatorOwnerDisabledOrReadOnly())
+ return;
+
+ if (event->type() == eventNames().clickEvent) {
+ openPopup();
+ event->setDefaultHandled();
+ }
+
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+}
+
+bool PickerIndicatorElement::willRespondToMouseClickEvents()
+{
+ if (renderer() && m_pickerIndicatorOwner && !m_pickerIndicatorOwner->isPickerIndicatorOwnerDisabledOrReadOnly())
+ return true;
+
+ return HTMLDivElement::willRespondToMouseClickEvents();
+}
+
+void PickerIndicatorElement::didChooseValue(const String& value)
+{
+ if (!m_pickerIndicatorOwner)
+ return;
+ m_pickerIndicatorOwner->pickerIndicatorChooseValue(value);
+}
+
+void PickerIndicatorElement::didEndChooser()
+{
+ m_chooser.clear();
+}
+
+void PickerIndicatorElement::openPopup()
+{
+ if (m_chooser)
+ return;
+ if (!document()->page())
+ return;
+ if (!m_pickerIndicatorOwner)
+ return;
+ Chrome* chrome = document()->page()->chrome();
+ if (!chrome)
+ return;
+ DateTimeChooserParameters parameters;
+ if (!m_pickerIndicatorOwner->setupDateTimeChooserParameters(parameters))
+ return;
+ m_chooser = chrome->openDateTimeChooser(this, parameters);
+}
+
+void PickerIndicatorElement::closePopup()
+{
+ if (!m_chooser)
+ return;
+ m_chooser->endChooser();
+}
+
+void PickerIndicatorElement::detach()
+{
+ closePopup();
+ HTMLDivElement::detach();
+}
+
+}
+
+#endif
diff --git a/Source/core/html/shadow/PickerIndicatorElement.h b/Source/core/html/shadow/PickerIndicatorElement.h
new file mode 100644
index 0000000..87712b2
--- /dev/null
+++ b/Source/core/html/shadow/PickerIndicatorElement.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PickerIndicatorElement_h
+#define PickerIndicatorElement_h
+
+#if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
+#include "core/html/HTMLDivElement.h"
+#include "core/platform/DateTimeChooser.h"
+#include "core/platform/DateTimeChooserClient.h"
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+class HTMLInputElement;
+class PagePopup;
+
+class PickerIndicatorElement FINAL : public HTMLDivElement, public DateTimeChooserClient {
+public:
+ // PickerIndicatorOwner implementer must call removePickerIndicatorOwner when
+ // it doesn't handle event, e.g. at destruction.
+ class PickerIndicatorOwner {
+ public:
+ virtual ~PickerIndicatorOwner() { }
+ virtual bool isPickerIndicatorOwnerDisabledOrReadOnly() const = 0;
+ virtual void pickerIndicatorChooseValue(const String&) = 0;
+ virtual bool setupDateTimeChooserParameters(DateTimeChooserParameters&) = 0;
+ };
+
+ static PassRefPtr<PickerIndicatorElement> create(Document*, PickerIndicatorOwner&);
+ virtual ~PickerIndicatorElement();
+ void openPopup();
+ void closePopup();
+ virtual bool willRespondToMouseClickEvents() OVERRIDE;
+ void removePickerIndicatorOwner() { m_pickerIndicatorOwner = 0; }
+
+ // DateTimeChooserClient implementation.
+ virtual void didChooseValue(const String&) OVERRIDE;
+ virtual void didEndChooser() OVERRIDE;
+
+private:
+ PickerIndicatorElement(Document*, PickerIndicatorOwner&);
+ virtual RenderObject* createRenderer(RenderArena*, RenderStyle*) OVERRIDE;
+ virtual void defaultEventHandler(Event*) OVERRIDE;
+ virtual void detach() OVERRIDE;
+
+ HTMLInputElement* hostInput();
+
+ PickerIndicatorOwner* m_pickerIndicatorOwner;
+ RefPtr<DateTimeChooser> m_chooser;
+};
+
+}
+#endif
+#endif
diff --git a/Source/core/html/shadow/ProgressShadowElement.cpp b/Source/core/html/shadow/ProgressShadowElement.cpp
new file mode 100644
index 0000000..030c1ce
--- /dev/null
+++ b/Source/core/html/shadow/ProgressShadowElement.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "core/html/shadow/ProgressShadowElement.h"
+
+#include "HTMLNames.h"
+#include "core/html/HTMLProgressElement.h"
+#include "core/rendering/RenderProgress.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+ProgressShadowElement::ProgressShadowElement(Document* document)
+ : HTMLDivElement(HTMLNames::divTag, document)
+{
+}
+
+HTMLProgressElement* ProgressShadowElement::progressElement() const
+{
+ return toHTMLProgressElement(shadowHost());
+}
+
+bool ProgressShadowElement::rendererIsNeeded(const NodeRenderingContext& context)
+{
+ RenderObject* progressRenderer = progressElement()->renderer();
+ return progressRenderer && !progressRenderer->style()->hasAppearance() && HTMLDivElement::rendererIsNeeded(context);
+}
+
+ProgressInnerElement::ProgressInnerElement(Document* document)
+ : ProgressShadowElement(document)
+{
+ DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-progress-inner-element", AtomicString::ConstructFromLiteral));
+ setPseudo(pseudoId);
+}
+
+PassRefPtr<ProgressInnerElement> ProgressInnerElement::create(Document* document)
+{
+ return adoptRef(new ProgressInnerElement(document));
+}
+
+RenderObject* ProgressInnerElement::createRenderer(RenderArena* arena, RenderStyle*)
+{
+ return new (arena) RenderProgress(this);
+}
+
+bool ProgressInnerElement::rendererIsNeeded(const NodeRenderingContext& context)
+{
+ if (progressElement()->hasAuthorShadowRoot())
+ return HTMLDivElement::rendererIsNeeded(context);
+
+ RenderObject* progressRenderer = progressElement()->renderer();
+ return progressRenderer && !progressRenderer->style()->hasAppearance() && HTMLDivElement::rendererIsNeeded(context);
+}
+
+void ProgressValueElement::setWidthPercentage(double width)
+{
+ setInlineStyleProperty(CSSPropertyWidth, width, CSSPrimitiveValue::CSS_PERCENTAGE);
+}
+
+}
diff --git a/Source/core/html/shadow/ProgressShadowElement.h b/Source/core/html/shadow/ProgressShadowElement.h
new file mode 100644
index 0000000..db6e4d2
--- /dev/null
+++ b/Source/core/html/shadow/ProgressShadowElement.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ProgressShadowElement_h
+#define ProgressShadowElement_h
+
+#include "core/html/HTMLDivElement.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class HTMLProgressElement;
+
+class ProgressShadowElement : public HTMLDivElement {
+public:
+ ProgressShadowElement(Document*);
+ HTMLProgressElement* progressElement() const;
+
+protected:
+ virtual bool rendererIsNeeded(const NodeRenderingContext&);
+};
+
+class ProgressInnerElement FINAL : public ProgressShadowElement {
+public:
+ ProgressInnerElement(Document*);
+
+ static PassRefPtr<ProgressInnerElement> create(Document*);
+private:
+ virtual RenderObject* createRenderer(RenderArena*, RenderStyle*) OVERRIDE;
+ virtual bool rendererIsNeeded(const NodeRenderingContext&);
+};
+
+class ProgressBarElement FINAL : public ProgressShadowElement {
+public:
+ ProgressBarElement(Document* document)
+ : ProgressShadowElement(document)
+ {
+ DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-progress-bar", AtomicString::ConstructFromLiteral));
+ setPseudo(pseudoId);
+ }
+
+ static PassRefPtr<ProgressBarElement> create(Document*);
+};
+
+inline PassRefPtr<ProgressBarElement> ProgressBarElement::create(Document* document)
+{
+ return adoptRef(new ProgressBarElement(document));
+}
+
+class ProgressValueElement FINAL : public ProgressShadowElement {
+public:
+ ProgressValueElement(Document* document)
+ : ProgressShadowElement(document)
+ {
+ DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-progress-value", AtomicString::ConstructFromLiteral));
+ setPseudo(pseudoId);
+ }
+
+ static PassRefPtr<ProgressValueElement> create(Document*);
+ void setWidthPercentage(double);
+};
+
+inline PassRefPtr<ProgressValueElement> ProgressValueElement::create(Document* document)
+{
+ return adoptRef(new ProgressValueElement(document));
+}
+
+}
+
+#endif // ProgressShadowElement_h
diff --git a/Source/core/html/shadow/SelectRuleFeatureSet.cpp b/Source/core/html/shadow/SelectRuleFeatureSet.cpp
new file mode 100644
index 0000000..2a38adc
--- /dev/null
+++ b/Source/core/html/shadow/SelectRuleFeatureSet.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "core/html/shadow/SelectRuleFeatureSet.h"
+
+#include "core/css/CSSSelector.h"
+
+namespace WebCore {
+
+SelectRuleFeatureSet::SelectRuleFeatureSet()
+ : m_featureFlags(0)
+{
+}
+
+void SelectRuleFeatureSet::add(const SelectRuleFeatureSet& featureSet)
+{
+ m_cssRuleFeatureSet.add(featureSet.m_cssRuleFeatureSet);
+ m_featureFlags |= featureSet.m_featureFlags;
+}
+
+void SelectRuleFeatureSet::clear()
+{
+ m_cssRuleFeatureSet.clear();
+ m_featureFlags = 0;
+}
+
+void SelectRuleFeatureSet::collectFeaturesFromSelector(const CSSSelector* selector)
+{
+ m_cssRuleFeatureSet.collectFeaturesFromSelector(selector);
+
+ switch (selector->pseudoType()) {
+ case CSSSelector::PseudoChecked:
+ setSelectRuleFeature(AffectedSelectorChecked);
+ break;
+ case CSSSelector::PseudoEnabled:
+ setSelectRuleFeature(AffectedSelectorEnabled);
+ break;
+ case CSSSelector::PseudoDisabled:
+ setSelectRuleFeature(AffectedSelectorDisabled);
+ break;
+ case CSSSelector::PseudoIndeterminate:
+ setSelectRuleFeature(AffectedSelectorIndeterminate);
+ break;
+ case CSSSelector::PseudoLink:
+ setSelectRuleFeature(AffectedSelectorLink);
+ break;
+ case CSSSelector::PseudoTarget:
+ setSelectRuleFeature(AffectedSelectorTarget);
+ break;
+ case CSSSelector::PseudoVisited:
+ setSelectRuleFeature(AffectedSelectorVisited);
+ break;
+ default:
+ break;
+ }
+}
+
+}
+
diff --git a/Source/core/html/shadow/SelectRuleFeatureSet.h b/Source/core/html/shadow/SelectRuleFeatureSet.h
new file mode 100644
index 0000000..7292b12
--- /dev/null
+++ b/Source/core/html/shadow/SelectRuleFeatureSet.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SelectRuleFeatureSet_h
+#define SelectRuleFeatureSet_h
+
+#include "core/css/RuleFeature.h"
+#include "core/dom/Element.h"
+
+namespace WebCore {
+
+class SelectRuleFeatureSet {
+public:
+ SelectRuleFeatureSet();
+
+ void add(const SelectRuleFeatureSet&);
+ void clear();
+ void collectFeaturesFromSelector(const CSSSelector*);
+
+ bool hasSelectorForId(const AtomicString&) const;
+ bool hasSelectorForClass(const AtomicString&) const;
+ bool hasSelectorForAttribute(const AtomicString&) const;
+
+ bool hasSelectorForChecked() const { return hasSelectorFor(AffectedSelectorChecked); }
+ bool hasSelectorForEnabled() const { return hasSelectorFor(AffectedSelectorEnabled); }
+ bool hasSelectorForDisabled() const { return hasSelectorFor(AffectedSelectorDisabled); }
+ bool hasSelectorForIndeterminate() const { return hasSelectorFor(AffectedSelectorIndeterminate); }
+ bool hasSelectorForLink() const { return hasSelectorFor(AffectedSelectorLink); }
+ bool hasSelectorForTarget() const { return hasSelectorFor(AffectedSelectorTarget); }
+ bool hasSelectorForVisited() const { return hasSelectorFor(AffectedSelectorVisited); }
+
+ bool hasSelectorFor(AffectedSelectorMask features) const { return m_featureFlags & features; }
+
+private:
+ void setSelectRuleFeature(AffectedSelectorType feature) { m_featureFlags |= feature; }
+
+ RuleFeatureSet m_cssRuleFeatureSet;
+ int m_featureFlags;
+};
+
+inline bool SelectRuleFeatureSet::hasSelectorForId(const AtomicString& idValue) const
+{
+ ASSERT(!idValue.isEmpty());
+ return m_cssRuleFeatureSet.idsInRules.contains(idValue.impl());
+}
+
+inline bool SelectRuleFeatureSet::hasSelectorForClass(const AtomicString& classValue) const
+{
+ ASSERT(!classValue.isEmpty());
+ return m_cssRuleFeatureSet.classesInRules.contains(classValue.impl());
+}
+
+inline bool SelectRuleFeatureSet::hasSelectorForAttribute(const AtomicString& attributeName) const
+{
+ ASSERT(!attributeName.isEmpty());
+ return m_cssRuleFeatureSet.attrsInRules.contains(attributeName.impl());
+}
+
+}
+
+#endif
diff --git a/Source/core/html/shadow/SliderThumbElement.cpp b/Source/core/html/shadow/SliderThumbElement.cpp
new file mode 100644
index 0000000..775deb3
--- /dev/null
+++ b/Source/core/html/shadow/SliderThumbElement.cpp
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include "config.h"
+#include "core/html/shadow/SliderThumbElement.h"
+
+#include "CSSValueKeywords.h"
+#include "core/dom/ElementShadow.h"
+#include "core/dom/Event.h"
+#include "core/dom/MouseEvent.h"
+#include "core/dom/ShadowRoot.h"
+#include "core/html/HTMLInputElement.h"
+#include "core/html/StepRange.h"
+#include "core/html/parser/HTMLParserIdioms.h"
+#include "core/page/EventHandler.h"
+#include "core/page/Frame.h"
+#include "core/rendering/RenderFlexibleBox.h"
+#include "core/rendering/RenderSlider.h"
+#include "core/rendering/RenderTheme.h"
+#include <wtf/MathExtras.h>
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+inline static Decimal sliderPosition(HTMLInputElement* element)
+{
+ const StepRange stepRange(element->createStepRange(RejectAny));
+ const Decimal oldValue = parseToDecimalForNumberType(element->value(), stepRange.defaultValue());
+ return stepRange.proportionFromValue(stepRange.clampValue(oldValue));
+}
+
+inline static bool hasVerticalAppearance(HTMLInputElement* input)
+{
+ ASSERT(input->renderer());
+ RenderStyle* sliderStyle = input->renderer()->style();
+
+ if (sliderStyle->appearance() == MediaVolumeSliderPart && input->renderer()->theme()->usesVerticalVolumeSlider())
+ return true;
+
+ return sliderStyle->appearance() == SliderVerticalPart;
+}
+
+SliderThumbElement* sliderThumbElementOf(Node* node)
+{
+ ASSERT(node);
+ ShadowRoot* shadow = node->toInputElement()->userAgentShadowRoot();
+ ASSERT(shadow);
+ Node* thumb = shadow->firstChild()->firstChild()->firstChild();
+ ASSERT(thumb);
+ return toSliderThumbElement(thumb);
+}
+
+HTMLElement* sliderTrackElementOf(Node* node)
+{
+ ASSERT(node);
+ ShadowRoot* shadow = node->toInputElement()->userAgentShadowRoot();
+ ASSERT(shadow);
+ Node* track = shadow->firstChild()->firstChild();
+ ASSERT(track);
+ return toHTMLElement(track);
+}
+
+// --------------------------------
+
+RenderSliderThumb::RenderSliderThumb(SliderThumbElement* element)
+ : RenderBlock(element)
+{
+}
+
+void RenderSliderThumb::updateAppearance(RenderStyle* parentStyle)
+{
+ if (parentStyle->appearance() == SliderVerticalPart)
+ style()->setAppearance(SliderThumbVerticalPart);
+ else if (parentStyle->appearance() == SliderHorizontalPart)
+ style()->setAppearance(SliderThumbHorizontalPart);
+ else if (parentStyle->appearance() == MediaSliderPart)
+ style()->setAppearance(MediaSliderThumbPart);
+ else if (parentStyle->appearance() == MediaVolumeSliderPart)
+ style()->setAppearance(MediaVolumeSliderThumbPart);
+ else if (parentStyle->appearance() == MediaFullScreenVolumeSliderPart)
+ style()->setAppearance(MediaFullScreenVolumeSliderThumbPart);
+ if (style()->hasAppearance())
+ theme()->adjustSliderThumbSize(style(), toElement(node()));
+}
+
+bool RenderSliderThumb::isSliderThumb() const
+{
+ return true;
+}
+
+// --------------------------------
+
+// FIXME: Find a way to cascade appearance and adjust heights, and get rid of this class.
+// http://webkit.org/b/62535
+class RenderSliderContainer : public RenderFlexibleBox {
+public:
+ RenderSliderContainer(SliderContainerElement* element)
+ : RenderFlexibleBox(element) { }
+public:
+ virtual void computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues&) const OVERRIDE;
+
+private:
+ virtual void layout() OVERRIDE;
+};
+
+void RenderSliderContainer::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
+{
+ HTMLInputElement* input = node()->shadowHost()->toInputElement();
+ bool isVertical = hasVerticalAppearance(input);
+
+#if ENABLE(DATALIST_ELEMENT)
+ if (input->renderer()->isSlider() && !isVertical && input->list()) {
+ int offsetFromCenter = theme()->sliderTickOffsetFromTrackCenter();
+ LayoutUnit trackHeight = 0;
+ if (offsetFromCenter < 0)
+ trackHeight = -2 * offsetFromCenter;
+ else {
+ int tickLength = theme()->sliderTickSize().height();
+ trackHeight = 2 * (offsetFromCenter + tickLength);
+ }
+ float zoomFactor = style()->effectiveZoom();
+ if (zoomFactor != 1.0)
+ trackHeight *= zoomFactor;
+
+ RenderBox::computeLogicalHeight(trackHeight, logicalTop, computedValues);
+ return;
+ }
+#endif
+ if (isVertical)
+ logicalHeight = RenderSlider::defaultTrackLength;
+ RenderBox::computeLogicalHeight(logicalHeight, logicalTop, computedValues);
+}
+
+void RenderSliderContainer::layout()
+{
+ HTMLInputElement* input = node()->shadowHost()->toInputElement();
+ bool isVertical = hasVerticalAppearance(input);
+ style()->setFlexDirection(isVertical ? FlowColumn : FlowRow);
+ TextDirection oldTextDirection = style()->direction();
+ if (isVertical) {
+ // FIXME: Work around rounding issues in RTL vertical sliders. We want them to
+ // render identically to LTR vertical sliders. We can remove this work around when
+ // subpixel rendering is enabled on all ports.
+ style()->setDirection(LTR);
+ }
+
+ RenderBox* thumb = input->sliderThumbElement() ? input->sliderThumbElement()->renderBox() : 0;
+ RenderBox* track = input->sliderTrackElement() ? input->sliderTrackElement()->renderBox() : 0;
+ // Force a layout to reset the position of the thumb so the code below doesn't move the thumb to the wrong place.
+ // FIXME: Make a custom Render class for the track and move the thumb positioning code there.
+ if (track)
+ track->setChildNeedsLayout(true, MarkOnlyThis);
+
+ RenderFlexibleBox::layout();
+
+ style()->setDirection(oldTextDirection);
+ // These should always exist, unless someone mutates the shadow DOM (e.g., in the inspector).
+ if (!thumb || !track)
+ return;
+
+ double percentageOffset = sliderPosition(input).toDouble();
+ LayoutUnit availableExtent = isVertical ? track->contentHeight() : track->contentWidth();
+ availableExtent -= isVertical ? thumb->height() : thumb->width();
+ LayoutUnit offset = percentageOffset * availableExtent;
+ LayoutPoint thumbLocation = thumb->location();
+ if (isVertical)
+ thumbLocation.setY(thumbLocation.y() + track->contentHeight() - thumb->height() - offset);
+ else if (style()->isLeftToRightDirection())
+ thumbLocation.setX(thumbLocation.x() + offset);
+ else
+ thumbLocation.setX(thumbLocation.x() - offset);
+ thumb->setLocation(thumbLocation);
+ if (checkForRepaintDuringLayout() && parent()
+ && (parent()->style()->appearance() == MediaVolumeSliderPart || parent()->style()->appearance() == MediaSliderPart)) {
+ // This will sometimes repaint too much. However, it is necessary to
+ // correctly repaint media controls (volume and timeline sliders) -
+ // they have special painting code in RenderMediaControls.cpp:paintMediaVolumeSlider
+ // and paintMediaSlider that gets called via -webkit-appearance and RenderTheme,
+ // so nothing else would otherwise invalidate the slider.
+ repaint();
+ }
+}
+
+// --------------------------------
+
+void SliderThumbElement::setPositionFromValue()
+{
+ // Since the code to calculate position is in the RenderSliderThumb layout
+ // path, we don't actually update the value here. Instead, we poke at the
+ // renderer directly to trigger layout.
+ if (renderer())
+ renderer()->setNeedsLayout(true);
+}
+
+RenderObject* SliderThumbElement::createRenderer(RenderArena* arena, RenderStyle*)
+{
+ return new (arena) RenderSliderThumb(this);
+}
+
+bool SliderThumbElement::isDisabledFormControl() const
+{
+ return hostInput()->isDisabledFormControl();
+}
+
+bool SliderThumbElement::matchesReadOnlyPseudoClass() const
+{
+ return hostInput()->matchesReadOnlyPseudoClass();
+}
+
+bool SliderThumbElement::matchesReadWritePseudoClass() const
+{
+ return hostInput()->matchesReadWritePseudoClass();
+}
+
+Node* SliderThumbElement::focusDelegate()
+{
+ return hostInput();
+}
+
+void SliderThumbElement::dragFrom(const LayoutPoint& point)
+{
+ setPositionFromPoint(point);
+ startDragging();
+}
+
+void SliderThumbElement::setPositionFromPoint(const LayoutPoint& point)
+{
+ HTMLInputElement* input = hostInput();
+ HTMLElement* trackElement = sliderTrackElementOf(input);
+
+ if (!input->renderer() || !renderBox() || !trackElement->renderBox())
+ return;
+
+ input->setTextAsOfLastFormControlChangeEvent(input->value());
+ LayoutPoint offset = roundedLayoutPoint(input->renderer()->absoluteToLocal(point, UseTransforms));
+ bool isVertical = hasVerticalAppearance(input);
+ bool isLeftToRightDirection = renderBox()->style()->isLeftToRightDirection();
+ LayoutUnit trackSize;
+ LayoutUnit position;
+ LayoutUnit currentPosition;
+ // We need to calculate currentPosition from absolute points becaue the
+ // renderer for this node is usually on a layer and renderBox()->x() and
+ // y() are unusable.
+ // FIXME: This should probably respect transforms.
+ LayoutPoint absoluteThumbOrigin = renderBox()->absoluteBoundingBoxRectIgnoringTransforms().location();
+ LayoutPoint absoluteSliderContentOrigin = roundedLayoutPoint(input->renderer()->localToAbsolute());
+ IntRect trackBoundingBox = trackElement->renderer()->absoluteBoundingBoxRectIgnoringTransforms();
+ IntRect inputBoundingBox = input->renderer()->absoluteBoundingBoxRectIgnoringTransforms();
+ if (isVertical) {
+ trackSize = trackElement->renderBox()->contentHeight() - renderBox()->height();
+ position = offset.y() - renderBox()->height() / 2 - trackBoundingBox.y() + inputBoundingBox.y() - renderBox()->marginBottom();
+ currentPosition = absoluteThumbOrigin.y() - absoluteSliderContentOrigin.y();
+ } else {
+ trackSize = trackElement->renderBox()->contentWidth() - renderBox()->width();
+ position = offset.x() - renderBox()->width() / 2 - trackBoundingBox.x() + inputBoundingBox.x();
+ position -= isLeftToRightDirection ? renderBox()->marginLeft() : renderBox()->marginRight();
+ currentPosition = absoluteThumbOrigin.x() - absoluteSliderContentOrigin.x();
+ }
+ position = max<LayoutUnit>(0, min(position, trackSize));
+ const Decimal ratio = Decimal::fromDouble(static_cast<double>(position) / trackSize);
+ const Decimal fraction = isVertical || !isLeftToRightDirection ? Decimal(1) - ratio : ratio;
+ StepRange stepRange(input->createStepRange(RejectAny));
+ Decimal value = stepRange.clampValue(stepRange.valueFromProportion(fraction));
+
+#if ENABLE(DATALIST_ELEMENT)
+ const LayoutUnit snappingThreshold = renderer()->theme()->sliderTickSnappingThreshold();
+ if (snappingThreshold > 0) {
+ Decimal closest = input->findClosestTickMarkValue(value);
+ if (closest.isFinite()) {
+ double closestFraction = stepRange.proportionFromValue(closest).toDouble();
+ double closestRatio = isVertical || !isLeftToRightDirection ? 1.0 - closestFraction : closestFraction;
+ LayoutUnit closestPosition = trackSize * closestRatio;
+ if ((closestPosition - position).abs() <= snappingThreshold)
+ value = closest;
+ }
+ }
+#endif
+
+ String valueString = serializeForNumberType(value);
+ if (valueString == input->value())
+ return;
+
+ // FIXME: This is no longer being set from renderer. Consider updating the method name.
+ input->setValueFromRenderer(valueString);
+ renderer()->setNeedsLayout(true);
+ input->dispatchFormControlChangeEvent();
+}
+
+void SliderThumbElement::startDragging()
+{
+ if (Frame* frame = document()->frame()) {
+ frame->eventHandler()->setCapturingMouseEventsNode(this);
+ m_inDragMode = true;
+ }
+}
+
+void SliderThumbElement::stopDragging()
+{
+ if (!m_inDragMode)
+ return;
+
+ if (Frame* frame = document()->frame())
+ frame->eventHandler()->setCapturingMouseEventsNode(0);
+ m_inDragMode = false;
+ if (renderer())
+ renderer()->setNeedsLayout(true);
+}
+
+void SliderThumbElement::defaultEventHandler(Event* event)
+{
+ if (!event->isMouseEvent()) {
+ HTMLDivElement::defaultEventHandler(event);
+ return;
+ }
+
+ // FIXME: Should handle this readonly/disabled check in more general way.
+ // Missing this kind of check is likely to occur elsewhere if adding it in each shadow element.
+ HTMLInputElement* input = hostInput();
+ if (!input || input->isDisabledOrReadOnly()) {
+ stopDragging();
+ HTMLDivElement::defaultEventHandler(event);
+ return;
+ }
+
+ MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
+ bool isLeftButton = mouseEvent->button() == LeftButton;
+ const AtomicString& eventType = event->type();
+
+ // We intentionally do not call event->setDefaultHandled() here because
+ // MediaControlTimelineElement::defaultEventHandler() wants to handle these
+ // mouse events.
+ if (eventType == eventNames().mousedownEvent && isLeftButton) {
+ startDragging();
+ return;
+ } else if (eventType == eventNames().mouseupEvent && isLeftButton) {
+ stopDragging();
+ return;
+ } else if (eventType == eventNames().mousemoveEvent) {
+ if (m_inDragMode)
+ setPositionFromPoint(mouseEvent->absoluteLocation());
+ return;
+ }
+
+ HTMLDivElement::defaultEventHandler(event);
+}
+
+bool SliderThumbElement::willRespondToMouseMoveEvents()
+{
+ const HTMLInputElement* input = hostInput();
+ if (input && !input->isDisabledOrReadOnly() && m_inDragMode)
+ return true;
+
+ return HTMLDivElement::willRespondToMouseMoveEvents();
+}
+
+bool SliderThumbElement::willRespondToMouseClickEvents()
+{
+ const HTMLInputElement* input = hostInput();
+ if (input && !input->isDisabledOrReadOnly())
+ return true;
+
+ return HTMLDivElement::willRespondToMouseClickEvents();
+}
+
+void SliderThumbElement::detach()
+{
+ if (m_inDragMode) {
+ if (Frame* frame = document()->frame())
+ frame->eventHandler()->setCapturingMouseEventsNode(0);
+ }
+ HTMLDivElement::detach();
+}
+
+HTMLInputElement* SliderThumbElement::hostInput() const
+{
+ // Only HTMLInputElement creates SliderThumbElement instances as its shadow nodes.
+ // So, shadowHost() must be an HTMLInputElement.
+ return shadowHost()->toInputElement();
+}
+
+static const AtomicString& sliderThumbShadowPseudoId()
+{
+ DEFINE_STATIC_LOCAL(const AtomicString, sliderThumb, ("-webkit-slider-thumb", AtomicString::ConstructFromLiteral));
+ return sliderThumb;
+}
+
+static const AtomicString& mediaSliderThumbShadowPseudoId()
+{
+ DEFINE_STATIC_LOCAL(const AtomicString, mediaSliderThumb, ("-webkit-media-slider-thumb", AtomicString::ConstructFromLiteral));
+ return mediaSliderThumb;
+}
+
+const AtomicString& SliderThumbElement::shadowPseudoId() const
+{
+ HTMLInputElement* input = hostInput();
+ if (!input)
+ return sliderThumbShadowPseudoId();
+
+ RenderStyle* sliderStyle = input->renderer()->style();
+ switch (sliderStyle->appearance()) {
+ case MediaSliderPart:
+ case MediaSliderThumbPart:
+ case MediaVolumeSliderPart:
+ case MediaVolumeSliderThumbPart:
+ case MediaFullScreenVolumeSliderPart:
+ case MediaFullScreenVolumeSliderThumbPart:
+ return mediaSliderThumbShadowPseudoId();
+ default:
+ return sliderThumbShadowPseudoId();
+ }
+}
+
+// --------------------------------
+
+inline SliderContainerElement::SliderContainerElement(Document* document)
+ : HTMLDivElement(HTMLNames::divTag, document)
+{
+}
+
+PassRefPtr<SliderContainerElement> SliderContainerElement::create(Document* document)
+{
+ return adoptRef(new SliderContainerElement(document));
+}
+
+RenderObject* SliderContainerElement::createRenderer(RenderArena* arena, RenderStyle*)
+{
+ return new (arena) RenderSliderContainer(this);
+}
+
+const AtomicString& SliderContainerElement::shadowPseudoId() const
+{
+ DEFINE_STATIC_LOCAL(const AtomicString, mediaSliderContainer, ("-webkit-media-slider-container", AtomicString::ConstructFromLiteral));
+ DEFINE_STATIC_LOCAL(const AtomicString, sliderContainer, ("-webkit-slider-container", AtomicString::ConstructFromLiteral));
+
+ HTMLInputElement* input = shadowHost()->toInputElement();
+ if (!input)
+ return sliderContainer;
+
+ RenderStyle* sliderStyle = input->renderer()->style();
+ switch (sliderStyle->appearance()) {
+ case MediaSliderPart:
+ case MediaSliderThumbPart:
+ case MediaVolumeSliderPart:
+ case MediaVolumeSliderThumbPart:
+ case MediaFullScreenVolumeSliderPart:
+ case MediaFullScreenVolumeSliderThumbPart:
+ return mediaSliderContainer;
+ default:
+ return sliderContainer;
+ }
+}
+
+}
diff --git a/Source/core/html/shadow/SliderThumbElement.h b/Source/core/html/shadow/SliderThumbElement.h
new file mode 100644
index 0000000..1b935e6
--- /dev/null
+++ b/Source/core/html/shadow/SliderThumbElement.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SliderThumbElement_h
+#define SliderThumbElement_h
+
+#include "HTMLNames.h"
+#include "core/html/HTMLDivElement.h"
+#include "core/platform/graphics/FloatPoint.h"
+#include "core/rendering/RenderBlock.h"
+#include "core/rendering/style/RenderStyleConstants.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class HTMLElement;
+class HTMLInputElement;
+class Event;
+class FloatPoint;
+
+class SliderThumbElement FINAL : public HTMLDivElement {
+public:
+ static PassRefPtr<SliderThumbElement> create(Document*);
+
+ void setPositionFromValue();
+
+ void dragFrom(const LayoutPoint&);
+ virtual void defaultEventHandler(Event*);
+ virtual bool willRespondToMouseMoveEvents() OVERRIDE;
+ virtual bool willRespondToMouseClickEvents() OVERRIDE;
+ virtual void detach();
+ virtual const AtomicString& shadowPseudoId() const;
+ HTMLInputElement* hostInput() const;
+ void setPositionFromPoint(const LayoutPoint&);
+
+private:
+ SliderThumbElement(Document*);
+ virtual RenderObject* createRenderer(RenderArena*, RenderStyle*);
+ virtual PassRefPtr<Element> cloneElementWithoutAttributesAndChildren();
+ virtual bool isDisabledFormControl() const OVERRIDE;
+ virtual bool matchesReadOnlyPseudoClass() const OVERRIDE;
+ virtual bool matchesReadWritePseudoClass() const OVERRIDE;
+ virtual Node* focusDelegate();
+ void startDragging();
+ void stopDragging();
+
+ bool m_inDragMode;
+};
+
+inline SliderThumbElement::SliderThumbElement(Document* document)
+ : HTMLDivElement(HTMLNames::divTag, document)
+ , m_inDragMode(false)
+{
+}
+
+inline PassRefPtr<SliderThumbElement> SliderThumbElement::create(Document* document)
+{
+ return adoptRef(new SliderThumbElement(document));
+}
+
+inline PassRefPtr<Element> SliderThumbElement::cloneElementWithoutAttributesAndChildren()
+{
+ return create(document());
+}
+
+inline SliderThumbElement* toSliderThumbElement(Node* node)
+{
+ ASSERT_WITH_SECURITY_IMPLICATION(!node || node->isHTMLElement());
+ return static_cast<SliderThumbElement*>(node);
+}
+
+// This always return a valid pointer.
+// An assertion fails if the specified node is not a range input.
+SliderThumbElement* sliderThumbElementOf(Node*);
+HTMLElement* sliderTrackElementOf(Node*);
+
+// --------------------------------
+
+class RenderSliderThumb FINAL : public RenderBlock {
+public:
+ RenderSliderThumb(SliderThumbElement*);
+ void updateAppearance(RenderStyle* parentStyle);
+
+private:
+ virtual bool isSliderThumb() const;
+};
+
+// --------------------------------
+
+class SliderContainerElement FINAL : public HTMLDivElement {
+public:
+ static PassRefPtr<SliderContainerElement> create(Document*);
+
+private:
+ SliderContainerElement(Document*);
+ virtual RenderObject* createRenderer(RenderArena*, RenderStyle*);
+ virtual const AtomicString& shadowPseudoId() const;
+};
+
+}
+
+#endif
diff --git a/Source/core/html/shadow/SpinButtonElement.cpp b/Source/core/html/shadow/SpinButtonElement.cpp
new file mode 100644
index 0000000..aad5bbf
--- /dev/null
+++ b/Source/core/html/shadow/SpinButtonElement.cpp
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2006, 2008, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "core/html/shadow/SpinButtonElement.h"
+
+#include "HTMLNames.h"
+#include "core/dom/EventNames.h"
+#include "core/dom/MouseEvent.h"
+#include "core/dom/WheelEvent.h"
+#include "core/page/Chrome.h"
+#include "core/page/EventHandler.h"
+#include "core/page/Frame.h"
+#include "core/page/Page.h"
+#include "core/platform/ScrollbarTheme.h"
+#include "core/rendering/RenderBox.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+inline SpinButtonElement::SpinButtonElement(Document* document, SpinButtonOwner& spinButtonOwner)
+ : HTMLDivElement(divTag, document)
+ , m_spinButtonOwner(&spinButtonOwner)
+ , m_capturing(false)
+ , m_upDownState(Indeterminate)
+ , m_pressStartingState(Indeterminate)
+ , m_repeatingTimer(this, &SpinButtonElement::repeatingTimerFired)
+{
+}
+
+PassRefPtr<SpinButtonElement> SpinButtonElement::create(Document* document, SpinButtonOwner& spinButtonOwner)
+{
+ return adoptRef(new SpinButtonElement(document, spinButtonOwner));
+}
+
+const AtomicString& SpinButtonElement::shadowPseudoId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, innerPseudoId, ("-webkit-inner-spin-button", AtomicString::ConstructFromLiteral));
+ return innerPseudoId;
+}
+
+void SpinButtonElement::detach()
+{
+ releaseCapture();
+ HTMLDivElement::detach();
+}
+
+void SpinButtonElement::defaultEventHandler(Event* event)
+{
+ if (!event->isMouseEvent()) {
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+ return;
+ }
+
+ RenderBox* box = renderBox();
+ if (!box) {
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+ return;
+ }
+
+ if (!shouldRespondToMouseEvents()) {
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+ return;
+ }
+
+ MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
+ IntPoint local = roundedIntPoint(box->absoluteToLocal(mouseEvent->absoluteLocation(), UseTransforms));
+ if (mouseEvent->type() == eventNames().mousedownEvent && mouseEvent->button() == LeftButton) {
+ if (box->pixelSnappedBorderBoxRect().contains(local)) {
+ // The following functions of HTMLInputElement may run JavaScript
+ // code which detaches this shadow node. We need to take a reference
+ // and check renderer() after such function calls.
+ RefPtr<Node> protector(this);
+ if (m_spinButtonOwner)
+ m_spinButtonOwner->focusAndSelectSpinButtonOwner();
+ if (renderer()) {
+ if (m_upDownState != Indeterminate) {
+ // A JavaScript event handler called in doStepAction() below
+ // might change the element state and we might need to
+ // cancel the repeating timer by the state change. If we
+ // started the timer after doStepAction(), we would have no
+ // chance to cancel the timer.
+ startRepeatingTimer();
+ doStepAction(m_upDownState == Up ? 1 : -1);
+ }
+ }
+ event->setDefaultHandled();
+ }
+ } else if (mouseEvent->type() == eventNames().mouseupEvent && mouseEvent->button() == LeftButton)
+ stopRepeatingTimer();
+ else if (event->type() == eventNames().mousemoveEvent) {
+ if (box->pixelSnappedBorderBoxRect().contains(local)) {
+ if (!m_capturing) {
+ if (Frame* frame = document()->frame()) {
+ frame->eventHandler()->setCapturingMouseEventsNode(this);
+ m_capturing = true;
+ if (Page* page = document()->page()) {
+ if (page->chrome())
+ page->chrome()->registerPopupOpeningObserver(this);
+ }
+ }
+ }
+ UpDownState oldUpDownState = m_upDownState;
+ m_upDownState = local.y() < box->height() / 2 ? Up : Down;
+ if (m_upDownState != oldUpDownState)
+ renderer()->repaint();
+ } else {
+ releaseCapture();
+ m_upDownState = Indeterminate;
+ }
+ }
+
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+}
+
+void SpinButtonElement::willOpenPopup()
+{
+ releaseCapture();
+ m_upDownState = Indeterminate;
+}
+
+void SpinButtonElement::forwardEvent(Event* event)
+{
+ if (!renderBox())
+ return;
+
+ if (!event->hasInterface(eventNames().interfaceForWheelEvent))
+ return;
+
+ if (!m_spinButtonOwner)
+ return;
+
+ if (!m_spinButtonOwner->shouldSpinButtonRespondToWheelEvents())
+ return;
+
+ doStepAction(static_cast<WheelEvent*>(event)->wheelDeltaY());
+ event->setDefaultHandled();
+}
+
+bool SpinButtonElement::willRespondToMouseMoveEvents()
+{
+ if (renderBox() && shouldRespondToMouseEvents())
+ return true;
+
+ return HTMLDivElement::willRespondToMouseMoveEvents();
+}
+
+bool SpinButtonElement::willRespondToMouseClickEvents()
+{
+ if (renderBox() && shouldRespondToMouseEvents())
+ return true;
+
+ return HTMLDivElement::willRespondToMouseClickEvents();
+}
+
+void SpinButtonElement::doStepAction(int amount)
+{
+ if (!m_spinButtonOwner)
+ return;
+
+ if (amount > 0)
+ m_spinButtonOwner->spinButtonStepUp();
+ else if (amount < 0)
+ m_spinButtonOwner->spinButtonStepDown();
+}
+
+void SpinButtonElement::releaseCapture()
+{
+ stopRepeatingTimer();
+ if (m_capturing) {
+ if (Frame* frame = document()->frame()) {
+ frame->eventHandler()->setCapturingMouseEventsNode(0);
+ m_capturing = false;
+ if (Page* page = document()->page()) {
+ if (page->chrome())
+ page->chrome()->unregisterPopupOpeningObserver(this);
+ }
+ }
+ }
+}
+
+bool SpinButtonElement::matchesReadOnlyPseudoClass() const
+{
+ return shadowHost()->matchesReadOnlyPseudoClass();
+}
+
+bool SpinButtonElement::matchesReadWritePseudoClass() const
+{
+ return shadowHost()->matchesReadWritePseudoClass();
+}
+
+void SpinButtonElement::startRepeatingTimer()
+{
+ m_pressStartingState = m_upDownState;
+ ScrollbarTheme* theme = ScrollbarTheme::theme();
+ m_repeatingTimer.start(theme->initialAutoscrollTimerDelay(), theme->autoscrollTimerDelay());
+}
+
+void SpinButtonElement::stopRepeatingTimer()
+{
+ m_repeatingTimer.stop();
+}
+
+void SpinButtonElement::step(int amount)
+{
+ if (!shouldRespondToMouseEvents())
+ return;
+ // On Mac OS, NSStepper updates the value for the button under the mouse
+ // cursor regardless of the button pressed at the beginning. So the
+ // following check is not needed for Mac OS.
+#if !OS(DARWIN)
+ if (m_upDownState != m_pressStartingState)
+ return;
+#endif
+ doStepAction(amount);
+}
+
+void SpinButtonElement::repeatingTimerFired(Timer<SpinButtonElement>*)
+{
+ if (m_upDownState != Indeterminate)
+ step(m_upDownState == Up ? 1 : -1);
+}
+
+void SpinButtonElement::setHovered(bool flag)
+{
+ if (!flag)
+ m_upDownState = Indeterminate;
+ HTMLDivElement::setHovered(flag);
+}
+
+bool SpinButtonElement::shouldRespondToMouseEvents()
+{
+ return !m_spinButtonOwner || m_spinButtonOwner->shouldSpinButtonRespondToMouseEvents();
+}
+
+}
diff --git a/Source/core/html/shadow/SpinButtonElement.h b/Source/core/html/shadow/SpinButtonElement.h
new file mode 100644
index 0000000..99e4b3e
--- /dev/null
+++ b/Source/core/html/shadow/SpinButtonElement.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2006, 2008, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SpinButtonElement_h
+#define SpinButtonElement_h
+
+#include "core/html/HTMLDivElement.h"
+#include "core/page/PopupOpeningObserver.h"
+#include "core/platform/Timer.h"
+
+namespace WebCore {
+
+class SpinButtonElement FINAL : public HTMLDivElement, public PopupOpeningObserver {
+public:
+ enum UpDownState {
+ Indeterminate, // Hovered, but the event is not handled.
+ Down,
+ Up,
+ };
+
+ class SpinButtonOwner {
+ public:
+ virtual ~SpinButtonOwner() { }
+ virtual void focusAndSelectSpinButtonOwner() = 0;
+ virtual bool shouldSpinButtonRespondToMouseEvents() = 0;
+ virtual bool shouldSpinButtonRespondToWheelEvents() = 0;
+ virtual void spinButtonStepDown() = 0;
+ virtual void spinButtonStepUp() = 0;
+ };
+
+ // The owner of SpinButtonElement must call removeSpinButtonOwner
+ // because SpinButtonElement can be outlive SpinButtonOwner
+ // implementation, e.g. during event handling.
+ static PassRefPtr<SpinButtonElement> create(Document*, SpinButtonOwner&);
+ UpDownState upDownState() const { return m_upDownState; }
+ virtual void releaseCapture();
+ void removeSpinButtonOwner() { m_spinButtonOwner = 0; }
+
+ void step(int amount);
+
+ virtual bool willRespondToMouseMoveEvents() OVERRIDE;
+ virtual bool willRespondToMouseClickEvents() OVERRIDE;
+
+ void forwardEvent(Event*);
+
+private:
+ SpinButtonElement(Document*, SpinButtonOwner&);
+
+ virtual const AtomicString& shadowPseudoId() const;
+ virtual void detach();
+ virtual bool isSpinButtonElement() const { return true; }
+ virtual bool isDisabledFormControl() const OVERRIDE { return shadowHost() && shadowHost()->isDisabledFormControl(); }
+ virtual bool matchesReadOnlyPseudoClass() const OVERRIDE;
+ virtual bool matchesReadWritePseudoClass() const OVERRIDE;
+ virtual void defaultEventHandler(Event*);
+ virtual void willOpenPopup() OVERRIDE;
+ void doStepAction(int);
+ void startRepeatingTimer();
+ void stopRepeatingTimer();
+ void repeatingTimerFired(Timer<SpinButtonElement>*);
+ virtual void setHovered(bool = true);
+ bool shouldRespondToMouseEvents();
+ virtual bool isMouseFocusable() const { return false; }
+
+ SpinButtonOwner* m_spinButtonOwner;
+ bool m_capturing;
+ UpDownState m_upDownState;
+ UpDownState m_pressStartingState;
+ Timer<SpinButtonElement> m_repeatingTimer;
+};
+
+} // namespace
+
+#endif
diff --git a/Source/core/html/shadow/TextControlInnerElements.cpp b/Source/core/html/shadow/TextControlInnerElements.cpp
new file mode 100644
index 0000000..06931b4
--- /dev/null
+++ b/Source/core/html/shadow/TextControlInnerElements.cpp
@@ -0,0 +1,463 @@
+/*
+ * Copyright (C) 2006, 2008, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "core/html/shadow/TextControlInnerElements.h"
+
+#include "HTMLNames.h"
+#include "bindings/v8/ScriptController.h"
+#include "core/dom/BeforeTextInsertedEvent.h"
+#include "core/dom/Document.h"
+#include "core/dom/EventNames.h"
+#include "core/dom/MouseEvent.h"
+#include "core/dom/TextEvent.h"
+#include "core/dom/TextEventInputType.h"
+#include "core/html/HTMLInputElement.h"
+#include "core/html/HTMLTextAreaElement.h"
+#include "core/page/EventHandler.h"
+#include "core/page/Frame.h"
+#include "core/page/Page.h"
+#include "core/page/SpeechInput.h"
+#include "core/page/SpeechInputEvent.h"
+#include "core/rendering/RenderSearchField.h"
+#include "core/rendering/RenderTextControl.h"
+#include "core/rendering/RenderView.h"
+#include "core/rendering/style/StyleInheritedData.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+TextControlInnerContainer::TextControlInnerContainer(Document* document)
+ : HTMLDivElement(divTag, document)
+{
+}
+
+PassRefPtr<TextControlInnerContainer> TextControlInnerContainer::create(Document* document)
+{
+ return adoptRef(new TextControlInnerContainer(document));
+}
+
+RenderObject* TextControlInnerContainer::createRenderer(RenderArena* arena, RenderStyle*)
+{
+ return new (arena) RenderTextControlInnerContainer(this);
+}
+
+TextControlInnerElement::TextControlInnerElement(Document* document)
+ : HTMLDivElement(divTag, document)
+{
+ setHasCustomStyleCallbacks();
+}
+
+PassRefPtr<TextControlInnerElement> TextControlInnerElement::create(Document* document)
+{
+ return adoptRef(new TextControlInnerElement(document));
+}
+
+PassRefPtr<RenderStyle> TextControlInnerElement::customStyleForRenderer()
+{
+ RenderTextControlSingleLine* parentRenderer = toRenderTextControlSingleLine(shadowHost()->renderer());
+ return parentRenderer->createInnerBlockStyle(parentRenderer->style());
+}
+
+// ---------------------------
+
+inline TextControlInnerTextElement::TextControlInnerTextElement(Document* document)
+ : HTMLDivElement(divTag, document)
+{
+ setHasCustomStyleCallbacks();
+}
+
+PassRefPtr<TextControlInnerTextElement> TextControlInnerTextElement::create(Document* document)
+{
+ return adoptRef(new TextControlInnerTextElement(document));
+}
+
+void TextControlInnerTextElement::defaultEventHandler(Event* event)
+{
+ // FIXME: In the future, we should add a way to have default event listeners.
+ // Then we would add one to the text field's inner div, and we wouldn't need this subclass.
+ // Or possibly we could just use a normal event listener.
+ if (event->isBeforeTextInsertedEvent() || event->type() == eventNames().webkitEditableContentChangedEvent) {
+ Element* shadowAncestor = shadowHost();
+ // A TextControlInnerTextElement can have no host if its been detached,
+ // but kept alive by an EditCommand. In this case, an undo/redo can
+ // cause events to be sent to the TextControlInnerTextElement. To
+ // prevent an infinite loop, we must check for this case before sending
+ // the event up the chain.
+ if (shadowAncestor)
+ shadowAncestor->defaultEventHandler(event);
+ }
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+}
+
+RenderObject* TextControlInnerTextElement::createRenderer(RenderArena* arena, RenderStyle*)
+{
+ return new (arena) RenderTextControlInnerBlock(this);
+}
+
+PassRefPtr<RenderStyle> TextControlInnerTextElement::customStyleForRenderer()
+{
+ RenderTextControl* parentRenderer = toRenderTextControl(shadowHost()->renderer());
+ return parentRenderer->createInnerTextStyle(parentRenderer->style());
+}
+
+// ----------------------------
+
+inline SearchFieldResultsButtonElement::SearchFieldResultsButtonElement(Document* document)
+ : HTMLDivElement(divTag, document)
+{
+}
+
+PassRefPtr<SearchFieldResultsButtonElement> SearchFieldResultsButtonElement::create(Document* document)
+{
+ return adoptRef(new SearchFieldResultsButtonElement(document));
+}
+
+const AtomicString& SearchFieldResultsButtonElement::shadowPseudoId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, resultsId, ("-webkit-search-results-button", AtomicString::ConstructFromLiteral));
+ DEFINE_STATIC_LOCAL(AtomicString, resultsDecorationId, ("-webkit-search-results-decoration", AtomicString::ConstructFromLiteral));
+ DEFINE_STATIC_LOCAL(AtomicString, decorationId, ("-webkit-search-decoration", AtomicString::ConstructFromLiteral));
+ Element* host = shadowHost();
+ if (!host)
+ return resultsId;
+ if (HTMLInputElement* input = host->toInputElement()) {
+ if (input->maxResults() < 0)
+ return decorationId;
+ if (input->maxResults() > 0)
+ return resultsId;
+ return resultsDecorationId;
+ }
+ return resultsId;
+}
+
+void SearchFieldResultsButtonElement::defaultEventHandler(Event* event)
+{
+ // On mousedown, bring up a menu, if needed
+ HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowHost());
+ if (input && event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
+ input->focus();
+ input->select();
+ RenderSearchField* renderer = toRenderSearchField(input->renderer());
+ if (renderer->popupIsVisible())
+ renderer->hidePopup();
+ else if (input->maxResults() > 0)
+ renderer->showPopup();
+ event->setDefaultHandled();
+ }
+
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+}
+
+bool SearchFieldResultsButtonElement::willRespondToMouseClickEvents()
+{
+ return true;
+}
+
+// ----------------------------
+
+inline SearchFieldCancelButtonElement::SearchFieldCancelButtonElement(Document* document)
+ : HTMLDivElement(divTag, document)
+ , m_capturing(false)
+{
+}
+
+PassRefPtr<SearchFieldCancelButtonElement> SearchFieldCancelButtonElement::create(Document* document)
+{
+ return adoptRef(new SearchFieldCancelButtonElement(document));
+}
+
+const AtomicString& SearchFieldCancelButtonElement::shadowPseudoId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-search-cancel-button", AtomicString::ConstructFromLiteral));
+ return pseudoId;
+}
+
+void SearchFieldCancelButtonElement::detach()
+{
+ if (m_capturing) {
+ if (Frame* frame = document()->frame())
+ frame->eventHandler()->setCapturingMouseEventsNode(0);
+ }
+ HTMLDivElement::detach();
+}
+
+
+void SearchFieldCancelButtonElement::defaultEventHandler(Event* event)
+{
+ // If the element is visible, on mouseup, clear the value, and set selection
+ RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowHost()));
+ if (!input || input->isDisabledOrReadOnly()) {
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+ return;
+ }
+
+ if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
+ if (renderer() && renderer()->visibleToHitTesting()) {
+ if (Frame* frame = document()->frame()) {
+ frame->eventHandler()->setCapturingMouseEventsNode(this);
+ m_capturing = true;
+ }
+ }
+ input->focus();
+ input->select();
+ event->setDefaultHandled();
+ }
+ if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
+ if (m_capturing) {
+ if (Frame* frame = document()->frame()) {
+ frame->eventHandler()->setCapturingMouseEventsNode(0);
+ m_capturing = false;
+ }
+ if (hovered()) {
+ String oldValue = input->value();
+ input->setValueForUser("");
+ input->onSearch();
+ event->setDefaultHandled();
+ }
+ }
+ }
+
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+}
+
+bool SearchFieldCancelButtonElement::willRespondToMouseClickEvents()
+{
+ const HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowHost());
+ if (input && !input->isDisabledOrReadOnly())
+ return true;
+
+ return HTMLDivElement::willRespondToMouseClickEvents();
+}
+
+// ----------------------------
+
+#if ENABLE(INPUT_SPEECH)
+
+inline InputFieldSpeechButtonElement::InputFieldSpeechButtonElement(Document* document)
+ : HTMLDivElement(divTag, document)
+ , m_capturing(false)
+ , m_state(Idle)
+ , m_listenerId(0)
+{
+}
+
+InputFieldSpeechButtonElement::~InputFieldSpeechButtonElement()
+{
+ SpeechInput* speech = speechInput();
+ if (speech && m_listenerId) { // Could be null when page is unloading.
+ if (m_state != Idle)
+ speech->cancelRecognition(m_listenerId);
+ speech->unregisterListener(m_listenerId);
+ }
+}
+
+PassRefPtr<InputFieldSpeechButtonElement> InputFieldSpeechButtonElement::create(Document* document)
+{
+ return adoptRef(new InputFieldSpeechButtonElement(document));
+}
+
+void InputFieldSpeechButtonElement::defaultEventHandler(Event* event)
+{
+ // For privacy reasons, only allow clicks directly coming from the user.
+ if (!ScriptController::processingUserGesture()) {
+ HTMLDivElement::defaultEventHandler(event);
+ return;
+ }
+
+ // The call to focus() below dispatches a focus event, and an event handler in the page might
+ // remove the input element from DOM. To make sure it remains valid until we finish our work
+ // here, we take a temporary reference.
+ RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowHost()));
+
+ if (!input || input->isDisabledOrReadOnly()) {
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+ return;
+ }
+
+ // On mouse down, select the text and set focus.
+ if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
+ if (renderer() && renderer()->visibleToHitTesting()) {
+ if (Frame* frame = document()->frame()) {
+ frame->eventHandler()->setCapturingMouseEventsNode(this);
+ m_capturing = true;
+ }
+ }
+ RefPtr<InputFieldSpeechButtonElement> holdRefButton(this);
+ input->focus();
+ input->select();
+ event->setDefaultHandled();
+ }
+ // On mouse up, release capture cleanly.
+ if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
+ if (m_capturing && renderer() && renderer()->visibleToHitTesting()) {
+ if (Frame* frame = document()->frame()) {
+ frame->eventHandler()->setCapturingMouseEventsNode(0);
+ m_capturing = false;
+ }
+ }
+ }
+
+ if (event->type() == eventNames().clickEvent && m_listenerId) {
+ switch (m_state) {
+ case Idle:
+ startSpeechInput();
+ break;
+ case Recording:
+ stopSpeechInput();
+ break;
+ case Recognizing:
+ // Nothing to do here, we will continue to wait for results.
+ break;
+ }
+ event->setDefaultHandled();
+ }
+
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+}
+
+bool InputFieldSpeechButtonElement::willRespondToMouseClickEvents()
+{
+ const HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowHost());
+ if (input && !input->isDisabledOrReadOnly())
+ return true;
+
+ return HTMLDivElement::willRespondToMouseClickEvents();
+}
+
+void InputFieldSpeechButtonElement::setState(SpeechInputState state)
+{
+ if (m_state != state) {
+ m_state = state;
+ shadowHost()->renderer()->repaint();
+ }
+}
+
+SpeechInput* InputFieldSpeechButtonElement::speechInput()
+{
+ return SpeechInput::from(document()->page());
+}
+
+void InputFieldSpeechButtonElement::didCompleteRecording(int)
+{
+ setState(Recognizing);
+}
+
+void InputFieldSpeechButtonElement::didCompleteRecognition(int)
+{
+ setState(Idle);
+}
+
+void InputFieldSpeechButtonElement::setRecognitionResult(int, const SpeechInputResultArray& results)
+{
+ m_results = results;
+
+ // The call to setValue() below dispatches an event, and an event handler in the page might
+ // remove the input element from DOM. To make sure it remains valid until we finish our work
+ // here, we take a temporary reference.
+ RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowHost()));
+ if (!input || input->isDisabledOrReadOnly())
+ return;
+
+ RefPtr<InputFieldSpeechButtonElement> holdRefButton(this);
+ if (document() && document()->domWindow()) {
+ // Call selectionChanged, causing the element to cache the selection,
+ // so that the text event inserts the text in this element even if
+ // focus has moved away from it.
+ input->selectionChanged(false);
+ input->dispatchEvent(TextEvent::create(document()->domWindow(), results.isEmpty() ? "" : results[0]->utterance(), TextEventInputOther));
+ }
+
+ // This event is sent after the text event so the website can perform actions using the input field content immediately.
+ // It provides alternative recognition hypotheses and notifies that the results come from speech input.
+ input->dispatchEvent(SpeechInputEvent::create(eventNames().webkitspeechchangeEvent, results));
+
+ // Check before accessing the renderer as the above event could have potentially turned off
+ // speech in the input element, hence removing this button and renderer from the hierarchy.
+ if (renderer())
+ renderer()->repaint();
+}
+
+void InputFieldSpeechButtonElement::attach()
+{
+ ASSERT(!m_listenerId);
+ if (SpeechInput* input = SpeechInput::from(document()->page()))
+ m_listenerId = input->registerListener(this);
+ HTMLDivElement::attach();
+}
+
+void InputFieldSpeechButtonElement::detach()
+{
+ if (m_capturing) {
+ if (Frame* frame = document()->frame())
+ frame->eventHandler()->setCapturingMouseEventsNode(0);
+ }
+
+ if (m_listenerId) {
+ if (m_state != Idle)
+ speechInput()->cancelRecognition(m_listenerId);
+ speechInput()->unregisterListener(m_listenerId);
+ m_listenerId = 0;
+ }
+
+ HTMLDivElement::detach();
+}
+
+void InputFieldSpeechButtonElement::startSpeechInput()
+{
+ if (m_state != Idle)
+ return;
+
+ RefPtr<HTMLInputElement> input = static_cast<HTMLInputElement*>(shadowHost());
+ AtomicString language = input->computeInheritedLanguage();
+ String grammar = input->getAttribute(webkitgrammarAttr);
+ IntRect rect = document()->view()->contentsToRootView(pixelSnappedBoundingBox());
+ if (speechInput()->startRecognition(m_listenerId, rect, language, grammar, document()->securityOrigin()))
+ setState(Recording);
+}
+
+void InputFieldSpeechButtonElement::stopSpeechInput()
+{
+ if (m_state == Recording)
+ speechInput()->stopRecording(m_listenerId);
+}
+
+const AtomicString& InputFieldSpeechButtonElement::shadowPseudoId() const
+{
+ DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-input-speech-button", AtomicString::ConstructFromLiteral));
+ return pseudoId;
+}
+
+#endif // ENABLE(INPUT_SPEECH)
+
+}
diff --git a/Source/core/html/shadow/TextControlInnerElements.h b/Source/core/html/shadow/TextControlInnerElements.h
new file mode 100644
index 0000000..6ec0336
--- /dev/null
+++ b/Source/core/html/shadow/TextControlInnerElements.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2006, 2008, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TextControlInnerElements_h
+#define TextControlInnerElements_h
+
+#include "core/html/HTMLDivElement.h"
+#include "core/page/SpeechInputListener.h"
+#include <wtf/Forward.h>
+
+namespace WebCore {
+
+class SpeechInput;
+
+class TextControlInnerContainer FINAL : public HTMLDivElement {
+public:
+ static PassRefPtr<TextControlInnerContainer> create(Document*);
+protected:
+ TextControlInnerContainer(Document*);
+ virtual RenderObject* createRenderer(RenderArena*, RenderStyle*);
+};
+
+class TextControlInnerElement FINAL : public HTMLDivElement {
+public:
+ static PassRefPtr<TextControlInnerElement> create(Document*);
+
+protected:
+ TextControlInnerElement(Document*);
+ virtual PassRefPtr<RenderStyle> customStyleForRenderer() OVERRIDE;
+
+private:
+ virtual bool isMouseFocusable() const { return false; }
+};
+
+class TextControlInnerTextElement FINAL : public HTMLDivElement {
+public:
+ static PassRefPtr<TextControlInnerTextElement> create(Document*);
+
+ virtual void defaultEventHandler(Event*);
+
+private:
+ TextControlInnerTextElement(Document*);
+ virtual RenderObject* createRenderer(RenderArena*, RenderStyle*);
+ virtual PassRefPtr<RenderStyle> customStyleForRenderer() OVERRIDE;
+ virtual bool isMouseFocusable() const { return false; }
+};
+
+class SearchFieldResultsButtonElement FINAL : public HTMLDivElement {
+public:
+ static PassRefPtr<SearchFieldResultsButtonElement> create(Document*);
+
+ virtual void defaultEventHandler(Event*);
+ virtual bool willRespondToMouseClickEvents() OVERRIDE;
+
+private:
+ SearchFieldResultsButtonElement(Document*);
+ virtual const AtomicString& shadowPseudoId() const;
+ virtual bool isMouseFocusable() const { return false; }
+};
+
+class SearchFieldCancelButtonElement FINAL : public HTMLDivElement {
+public:
+ static PassRefPtr<SearchFieldCancelButtonElement> create(Document*);
+
+ virtual void defaultEventHandler(Event*);
+ virtual bool willRespondToMouseClickEvents() OVERRIDE;
+
+private:
+ SearchFieldCancelButtonElement(Document*);
+ virtual const AtomicString& shadowPseudoId() const;
+ virtual void detach();
+ virtual bool isMouseFocusable() const { return false; }
+
+ bool m_capturing;
+};
+
+#if ENABLE(INPUT_SPEECH)
+
+class InputFieldSpeechButtonElement FINAL
+ : public HTMLDivElement,
+ public SpeechInputListener {
+public:
+ enum SpeechInputState {
+ Idle,
+ Recording,
+ Recognizing,
+ };
+
+ static PassRefPtr<InputFieldSpeechButtonElement> create(Document*);
+ virtual ~InputFieldSpeechButtonElement();
+
+ virtual void detach();
+ virtual void defaultEventHandler(Event*);
+ virtual bool willRespondToMouseClickEvents();
+ virtual bool isInputFieldSpeechButtonElement() const { return true; }
+ SpeechInputState state() const { return m_state; }
+ void startSpeechInput();
+ void stopSpeechInput();
+
+ // SpeechInputListener methods.
+ void didCompleteRecording(int);
+ void didCompleteRecognition(int);
+ void setRecognitionResult(int, const SpeechInputResultArray&);
+
+private:
+ InputFieldSpeechButtonElement(Document*);
+ SpeechInput* speechInput();
+ void setState(SpeechInputState state);
+ virtual const AtomicString& shadowPseudoId() const;
+ virtual bool isMouseFocusable() const { return false; }
+ virtual void attach();
+
+ bool m_capturing;
+ SpeechInputState m_state;
+ int m_listenerId;
+ SpeechInputResultArray m_results;
+};
+
+inline InputFieldSpeechButtonElement* toInputFieldSpeechButtonElement(Element* element)
+{
+ ASSERT_WITH_SECURITY_IMPLICATION(!element || element->isInputFieldSpeechButtonElement());
+ return static_cast<InputFieldSpeechButtonElement*>(element);
+}
+
+#endif // ENABLE(INPUT_SPEECH)
+
+} // namespace
+
+#endif
diff --git a/Source/core/html/shadow/TextFieldDecorationElement.cpp b/Source/core/html/shadow/TextFieldDecorationElement.cpp
new file mode 100644
index 0000000..c330767
--- /dev/null
+++ b/Source/core/html/shadow/TextFieldDecorationElement.cpp
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "core/html/shadow/TextFieldDecorationElement.h"
+
+#include "CSSPropertyNames.h"
+#include "CSSValueKeywords.h"
+#include "core/css/StyleResolver.h"
+#include "core/dom/ElementShadow.h"
+#include "core/dom/Event.h"
+#include "core/dom/NodeRenderStyle.h"
+#include "core/dom/ShadowRoot.h"
+#include "core/html/HTMLInputElement.h"
+#include "core/html/shadow/HTMLShadowElement.h"
+#include "core/rendering/RenderImage.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+// TextFieldDecorator ----------------------------------------------------------------
+
+TextFieldDecorator::~TextFieldDecorator()
+{
+}
+
+// TextFieldDecorationElement ----------------------------------------------------------------
+
+// FIXME: This class is only used in Chromium, and has no layout tests!!
+
+TextFieldDecorationElement::TextFieldDecorationElement(Document* document, TextFieldDecorator* decorator)
+ : HTMLDivElement(HTMLNames::divTag, document)
+ , m_textFieldDecorator(decorator)
+ , m_isInHoverState(false)
+{
+ ASSERT(decorator);
+ setHasCustomStyleCallbacks();
+}
+
+PassRefPtr<TextFieldDecorationElement> TextFieldDecorationElement::create(Document* document, TextFieldDecorator* decorator)
+{
+ return adoptRef(new TextFieldDecorationElement(document, decorator));
+}
+
+TextFieldDecorationElement* TextFieldDecorationElement::fromShadowRoot(ShadowRoot* shadowRoot)
+{
+ if (!shadowRoot->firstChild()
+ || !shadowRoot->firstChild()->lastChild()
+ || !shadowRoot->firstChild()->lastChild()->isElementNode()
+ || !toElement(shadowRoot->firstChild()->lastChild())->isTextFieldDecoration())
+ return 0;
+ return toTextFieldDecorationElement(shadowRoot->firstChild()->lastChild());
+}
+
+static inline void getDecorationRootAndDecoratedRoot(HTMLInputElement* input, ShadowRoot*& decorationRoot, ShadowRoot*& decoratedRoot)
+{
+ ShadowRoot* existingRoot = input->youngestShadowRoot();
+ ShadowRoot* newRoot = 0;
+ while (existingRoot->childNodeCount() == 1 && existingRoot->firstChild()->hasTagName(shadowTag)) {
+ newRoot = existingRoot;
+ existingRoot = existingRoot->olderShadowRoot();
+ ASSERT(existingRoot);
+ }
+ if (newRoot)
+ newRoot->removeChild(newRoot->firstChild());
+ else {
+ // FIXME: This interacts really badly with author shadow roots because now
+ // we can interleave user agent and author shadow roots on the element meaning
+ // input.shadowRoot may be inaccessible if the browser has decided to decorate
+ // the input.
+ newRoot = input->ensureShadow()->addShadowRoot(input, ShadowRoot::UserAgentShadowRoot);
+ }
+ decorationRoot = newRoot;
+ decoratedRoot = existingRoot;
+}
+
+void TextFieldDecorationElement::decorate(HTMLInputElement* input, bool visible)
+{
+ ASSERT(input);
+ ShadowRoot* existingRoot;
+ ShadowRoot* decorationRoot;
+ getDecorationRootAndDecoratedRoot(input, decorationRoot, existingRoot);
+ ASSERT(decorationRoot);
+ ASSERT(existingRoot);
+ RefPtr<HTMLDivElement> box = HTMLDivElement::create(input->document());
+ decorationRoot->appendChild(box);
+ box->setInlineStyleProperty(CSSPropertyDisplay, CSSValueWebkitFlex);
+ box->setInlineStyleProperty(CSSPropertyWebkitAlignItems, CSSValueCenter);
+ ASSERT(existingRoot->childNodeCount() == 1);
+ toHTMLElement(existingRoot->firstChild())->setInlineStyleProperty(CSSPropertyWebkitFlexGrow, 1.0, CSSPrimitiveValue::CSS_NUMBER);
+ box->appendChild(HTMLShadowElement::create(HTMLNames::shadowTag, input->document()));
+ setInlineStyleProperty(CSSPropertyDisplay, visible ? CSSValueBlock : CSSValueNone);
+ box->appendChild(this);
+}
+
+inline HTMLInputElement* TextFieldDecorationElement::hostInput()
+{
+ // TextFieldDecorationElement is created only by C++ code, and it is always
+ // in <input> shadow.
+ ASSERT_WITH_SECURITY_IMPLICATION(!shadowHost() || shadowHost()->hasTagName(inputTag));
+ return static_cast<HTMLInputElement*>(shadowHost());
+}
+
+bool TextFieldDecorationElement::isTextFieldDecoration() const
+{
+ return true;
+}
+
+void TextFieldDecorationElement::updateImage()
+{
+ if (!renderer() || !renderer()->isImage())
+ return;
+ RenderImageResource* resource = toRenderImage(renderer())->imageResource();
+ CachedImage* image;
+ if (hostInput()->isDisabledFormControl())
+ image = m_textFieldDecorator->imageForDisabledState();
+ else if (hostInput()->isReadOnly())
+ image = m_textFieldDecorator->imageForReadonlyState();
+ else if (m_isInHoverState)
+ image = m_textFieldDecorator->imageForHoverState();
+ else
+ image = m_textFieldDecorator->imageForNormalState();
+ ASSERT(image);
+ resource->setCachedImage(image);
+}
+
+PassRefPtr<RenderStyle> TextFieldDecorationElement::customStyleForRenderer()
+{
+ RefPtr<RenderStyle> originalStyle = document()->styleResolver()->styleForElement(this);
+ RefPtr<RenderStyle> style = RenderStyle::clone(originalStyle.get());
+ RenderStyle* inputStyle = hostInput()->renderStyle();
+ ASSERT(inputStyle);
+ style->setWidth(Length(inputStyle->fontSize(), Fixed));
+ style->setHeight(Length(inputStyle->fontSize(), Fixed));
+ updateImage();
+ return style.release();
+}
+
+RenderObject* TextFieldDecorationElement::createRenderer(RenderArena* arena, RenderStyle*)
+{
+ RenderImage* image = new (arena) RenderImage(this);
+ image->setImageResource(RenderImageResource::create());
+ return image;
+}
+
+void TextFieldDecorationElement::attach()
+{
+ HTMLDivElement::attach();
+ updateImage();
+}
+
+void TextFieldDecorationElement::detach()
+{
+ m_textFieldDecorator->willDetach(hostInput());
+ HTMLDivElement::detach();
+}
+
+bool TextFieldDecorationElement::isMouseFocusable() const
+{
+ return false;
+}
+
+void TextFieldDecorationElement::defaultEventHandler(Event* event)
+{
+ RefPtr<HTMLInputElement> input(hostInput());
+ if (!input || input->isDisabledOrReadOnly() || !event->isMouseEvent()) {
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+ return;
+ }
+
+ RefPtr<TextFieldDecorationElement> protector(this);
+ if (event->type() == eventNames().clickEvent) {
+ m_textFieldDecorator->handleClick(input.get());
+ event->setDefaultHandled();
+ }
+
+ if (event->type() == eventNames().mouseoverEvent) {
+ m_isInHoverState = true;
+ updateImage();
+ }
+
+ if (event->type() == eventNames().mouseoutEvent) {
+ m_isInHoverState = false;
+ updateImage();
+ }
+
+ if (!event->defaultHandled())
+ HTMLDivElement::defaultEventHandler(event);
+}
+
+bool TextFieldDecorationElement::willRespondToMouseMoveEvents()
+{
+ const HTMLInputElement* input = hostInput();
+ if (!input->isDisabledOrReadOnly())
+ return true;
+
+ return HTMLDivElement::willRespondToMouseMoveEvents();
+}
+
+bool TextFieldDecorationElement::willRespondToMouseClickEvents()
+{
+ const HTMLInputElement* input = hostInput();
+ if (!input->isDisabledOrReadOnly())
+ return true;
+
+ return HTMLDivElement::willRespondToMouseClickEvents();
+}
+
+} // namespace WebCore
diff --git a/Source/core/html/shadow/TextFieldDecorationElement.h b/Source/core/html/shadow/TextFieldDecorationElement.h
new file mode 100644
index 0000000..cf34338
--- /dev/null
+++ b/Source/core/html/shadow/TextFieldDecorationElement.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TextFieldDecorationElement_h
+#define TextFieldDecorationElement_h
+
+#include "core/html/HTMLDivElement.h"
+
+namespace WebCore {
+
+class CachedImage;
+class HTMLInputElement;
+class ShadowRoot;
+
+// A TextFieldDecorator object must live until all of text fields which were
+// decorated by it die.
+class TextFieldDecorator {
+public:
+ // Returns true if this TextFieldDecorator wants to add a
+ // decoration to the specified text field.
+ virtual bool willAddDecorationTo(HTMLInputElement*) = 0;
+ virtual bool visibleByDefault() = 0;
+
+ // A TextFieldDecorator object should own the CachedImage objects.
+ virtual CachedImage* imageForNormalState() = 0;
+ virtual CachedImage* imageForDisabledState() = 0;
+ virtual CachedImage* imageForReadonlyState() = 0;
+ virtual CachedImage* imageForHoverState() = 0;
+
+ virtual void handleClick(HTMLInputElement*) = 0;
+ // This function is called just before detaching the decoration. It must not
+ // call functions which updating state of the specified HTMLInputElement
+ // object.
+ virtual void willDetach(HTMLInputElement*) = 0;
+
+ virtual ~TextFieldDecorator();
+};
+
+// A TextFieldDecorationElement object must be in a shadow tree of an
+// HTMLInputElement.
+class TextFieldDecorationElement FINAL : public HTMLDivElement {
+public:
+ static PassRefPtr<TextFieldDecorationElement> create(Document*, TextFieldDecorator*);
+ static TextFieldDecorationElement* fromShadowRoot(ShadowRoot*);
+ TextFieldDecorator* textFieldDecorator() { return m_textFieldDecorator; }
+ void decorate(HTMLInputElement*, bool visible);
+
+ virtual bool willRespondToMouseMoveEvents() OVERRIDE;
+ virtual bool willRespondToMouseClickEvents() OVERRIDE;
+
+private:
+ TextFieldDecorationElement(Document*, TextFieldDecorator*);
+ virtual bool isTextFieldDecoration() const OVERRIDE;
+ virtual PassRefPtr<RenderStyle> customStyleForRenderer() OVERRIDE;
+ virtual RenderObject* createRenderer(RenderArena*, RenderStyle*) OVERRIDE;
+ virtual void attach() OVERRIDE;
+ virtual void detach() OVERRIDE;
+ virtual bool isMouseFocusable() const OVERRIDE;
+ virtual void defaultEventHandler(Event*) OVERRIDE;
+
+ HTMLInputElement* hostInput();
+ void updateImage();
+
+ TextFieldDecorator* m_textFieldDecorator;
+ bool m_isInHoverState;
+};
+
+inline TextFieldDecorationElement* toTextFieldDecorationElement(Node* node)
+{
+ ASSERT(node);
+ ASSERT_WITH_SECURITY_IMPLICATION(node->isElementNode());
+ ASSERT_WITH_SECURITY_IMPLICATION(toElement(node)->isTextFieldDecoration());
+ return static_cast<TextFieldDecorationElement*>(node);
+}
+
+}
+#endif