Add ASSERT_WITH_SECURITY_IMPLICATION to detect out of bounds access
[WebKit-https.git] / Source / WebCore / html / shadow / DateTimeSymbolicFieldElement.cpp
index 8ef4dac..ccd0e8b 100644 (file)
 #if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
 #include "DateTimeSymbolicFieldElement.h"
 
-#include "FontCache.h"
+#include "Font.h"
 #include "KeyboardEvent.h"
-#include "RenderStyle.h"
-#include "StyleResolver.h"
 #include "TextBreakIterator.h"
-#include "TextRun.h"
 #include <wtf/text/StringBuilder.h>
 #include <wtf/unicode/Unicode.h>
 
@@ -50,26 +47,27 @@ static AtomicString makeVisibleEmptyValue(const Vector<String>& symbols)
     return builder.toAtomicString();
 }
 
-DateTimeSymbolicFieldElement::DateTimeSymbolicFieldElement(Document* document, FieldOwner& fieldOwner, const Vector<String>& symbols)
+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());
-    setHasCustomCallbacks();
+    ASSERT(m_minimumIndex >= 0);
+    ASSERT_WITH_SECURITY_IMPLICATION(m_maximumIndex < static_cast<int>(m_symbols.size()));
+    ASSERT(m_minimumIndex <= m_maximumIndex);
 }
 
-PassRefPtr<RenderStyle> DateTimeSymbolicFieldElement::customStyleForRenderer()
+float DateTimeSymbolicFieldElement::maximumWidth(const Font& font)
 {
-    FontCachePurgePreventer fontCachePurgePreventer;
-    RefPtr<RenderStyle> originalStyle = document()->styleResolver()->styleForElement(this);
-    RefPtr<RenderStyle> style = RenderStyle::clone(originalStyle.get());
-    float maxiumWidth = style->font().width(visibleEmptyValue());
+    float maximumWidth = font.width(visibleEmptyValue());
     for (unsigned index = 0; index < m_symbols.size(); ++index)
-        maxiumWidth = std::max(maxiumWidth, style->font().width(m_symbols[index]));
-    style->setMinWidth(Length(maxiumWidth, Fixed));
-    return style.release();
+        maximumWidth = std::max(maximumWidth, font.width(m_symbols[index]));
+    return maximumWidth + DateTimeFieldElement::maximumWidth(font);
 }
 
 void DateTimeSymbolicFieldElement::handleKeyboardEvent(KeyboardEvent* keyboardEvent)
@@ -82,12 +80,11 @@ void DateTimeSymbolicFieldElement::handleKeyboardEvent(KeyboardEvent* keyboardEv
         return;
 
     keyboardEvent->setDefaultHandled();
-    for (unsigned index = 0; index < m_symbols.size(); ++index) {
-        if (!m_symbols[index].isEmpty() && WTF::Unicode::toLower(m_symbols[index][0]) == charCode) {
-            setValueAsInteger(index, DispatchEvent);
-            return;
-        }
-    }
+
+    int index = m_typeAhead.handleEvent(keyboardEvent, TypeAhead::MatchPrefix | TypeAhead::CycleFirstChar | TypeAhead::MatchIndex);
+    if (index < 0)
+        return;
+    setValueAsInteger(index, DispatchEvent);
 }
 
 bool DateTimeSymbolicFieldElement::hasValue() const
@@ -95,18 +92,18 @@ bool DateTimeSymbolicFieldElement::hasValue() const
     return m_selectedIndex >= 0;
 }
 
-int DateTimeSymbolicFieldElement::maximum() const
-{
-    return static_cast<int>(m_symbols.size());
-}
-
-int DateTimeSymbolicFieldElement::minimum() const
+void DateTimeSymbolicFieldElement::initialize(const AtomicString& pseudo, const String& axHelpText)
 {
-    return 1;
+    // 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);
 }
@@ -119,14 +116,21 @@ void DateTimeSymbolicFieldElement::setValueAsInteger(int newSelectedIndex, Event
 
 void DateTimeSymbolicFieldElement::stepDown()
 {
-    const int size = m_symbols.size();
-    m_selectedIndex = hasValue() ? (m_selectedIndex + size - 1) % size : size - 1;
+    if (hasValue()) {
+        if (!indexIsInRange(--m_selectedIndex))
+            m_selectedIndex = m_maximumIndex;
+    } else
+        m_selectedIndex = m_maximumIndex;
     updateVisibleValue(DispatchEvent);
 }
 
 void DateTimeSymbolicFieldElement::stepUp()
 {
-    m_selectedIndex = hasValue() ? (m_selectedIndex + 1) % m_symbols.size() : 0;
+    if (hasValue()) {
+        if (!indexIsInRange(++m_selectedIndex))
+            m_selectedIndex = m_minimumIndex;
+    } else
+        m_selectedIndex = m_minimumIndex;
     updateVisibleValue(DispatchEvent);
 }
 
@@ -140,6 +144,12 @@ 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;
@@ -150,6 +160,21 @@ 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