diff options
Diffstat (limited to 'Source/WebCore/html')
105 files changed, 2151 insertions, 1086 deletions
diff --git a/Source/WebCore/html/BaseCheckableInputType.cpp b/Source/WebCore/html/BaseCheckableInputType.cpp index f578acbe7..1900ad296 100644 --- a/Source/WebCore/html/BaseCheckableInputType.cpp +++ b/Source/WebCore/html/BaseCheckableInputType.cpp @@ -32,6 +32,7 @@ #include "config.h" #include "BaseCheckableInputType.h" +#include "FormController.h" #include "FormDataList.h" #include "HTMLInputElement.h" #include "HTMLNames.h" @@ -42,15 +43,14 @@ namespace WebCore { using namespace HTMLNames; -bool BaseCheckableInputType::saveFormControlState(String& result) const +FormControlState BaseCheckableInputType::saveFormControlState() const { - result = element()->checked() ? "on" : "off"; - return true; + return FormControlState(element()->checked() ? "on" : "off"); } -void BaseCheckableInputType::restoreFormControlState(const String& state) +void BaseCheckableInputType::restoreFormControlState(const FormControlState& state) { - element()->setChecked(state == "on"); + element()->setChecked(state.value() == "on"); } bool BaseCheckableInputType::appendFormData(FormDataList& encoding, bool) const diff --git a/Source/WebCore/html/BaseCheckableInputType.h b/Source/WebCore/html/BaseCheckableInputType.h index 860d6fd90..5f8a66522 100644 --- a/Source/WebCore/html/BaseCheckableInputType.h +++ b/Source/WebCore/html/BaseCheckableInputType.h @@ -42,8 +42,8 @@ protected: virtual void handleKeydownEvent(KeyboardEvent*); private: - virtual bool saveFormControlState(String&) const OVERRIDE; - virtual void restoreFormControlState(const String&) OVERRIDE; + virtual FormControlState saveFormControlState() const OVERRIDE; + virtual void restoreFormControlState(const FormControlState&) OVERRIDE; virtual bool appendFormData(FormDataList&, bool) const OVERRIDE; virtual void handleKeypressEvent(KeyboardEvent*) OVERRIDE; virtual bool canSetStringValue() const OVERRIDE; diff --git a/Source/WebCore/html/BaseDateAndTimeInputType.cpp b/Source/WebCore/html/BaseDateAndTimeInputType.cpp index 4c7309191..48471cb65 100644 --- a/Source/WebCore/html/BaseDateAndTimeInputType.cpp +++ b/Source/WebCore/html/BaseDateAndTimeInputType.cpp @@ -47,12 +47,12 @@ namespace WebCore { using namespace HTMLNames; using namespace std; -static const double msecPerMinute = 60 * 1000; -static const double msecPerSecond = 1000; +static const int msecPerMinute = 60 * 1000; +static const int msecPerSecond = 1000; double BaseDateAndTimeInputType::valueAsDate() const { - return parseToDouble(element()->value(), DateComponents::invalidMilliseconds()); + return valueAsDouble(); } void BaseDateAndTimeInputType::setValueAsDate(double value, ExceptionCode&) const @@ -60,12 +60,12 @@ void BaseDateAndTimeInputType::setValueAsDate(double value, ExceptionCode&) cons element()->setValue(serializeWithMilliseconds(value)); } -double BaseDateAndTimeInputType::valueAsNumber() const +double BaseDateAndTimeInputType::valueAsDouble() const { - return parseToDouble(element()->value(), numeric_limits<double>::quiet_NaN()); + return parseToDouble(element()->value()); } -void BaseDateAndTimeInputType::setValueAsNumber(double newValue, TextFieldEventBehavior eventBehavior, ExceptionCode&) const +void BaseDateAndTimeInputType::setValueAsDecimal(const Decimal& newValue, TextFieldEventBehavior eventBehavior, ExceptionCode&) const { element()->setValue(serialize(newValue), eventBehavior); } @@ -80,13 +80,13 @@ bool BaseDateAndTimeInputType::typeMismatch() const return typeMismatchFor(element()->value()); } -double BaseDateAndTimeInputType::defaultValueForStepUp() const +Decimal BaseDateAndTimeInputType::defaultValueForStepUp() const { double ms = currentTimeMS(); double utcOffset = calculateUTCOffset(); double dstOffset = calculateDSTOffset(ms, utcOffset); int offset = static_cast<int>((utcOffset + dstOffset) / msPerMinute); - return ms + (offset * msPerMinute); + return Decimal::fromDouble(ms + (offset * msPerMinute)); } bool BaseDateAndTimeInputType::isSteppable() const @@ -108,16 +108,22 @@ void BaseDateAndTimeInputType::handleWheelEvent(WheelEvent* event) handleWheelEventForSpinButton(event); } -double BaseDateAndTimeInputType::parseToDouble(const String& src, double defaultValue) const +double BaseDateAndTimeInputType::parseToDouble(const String& source) const { DateComponents date; - if (!parseToDateComponents(src, &date)) - return defaultValue; + if (!parseToDateComponents(source, &date)) + return DateComponents::invalidMilliseconds(); double msec = date.millisecondsSinceEpoch(); ASSERT(isfinite(msec)); return msec; } +Decimal BaseDateAndTimeInputType::parseToNumber(const String& source, const Decimal& defaultValue) const +{ + const double doubleValue = parseToDouble(source); + return isfinite(doubleValue) ? Decimal::fromDouble(doubleValue) : defaultValue; +} + bool BaseDateAndTimeInputType::parseToDateComponents(const String& source, DateComponents* out) const { if (source.isEmpty()) @@ -128,31 +134,31 @@ bool BaseDateAndTimeInputType::parseToDateComponents(const String& source, DateC return parseToDateComponentsInternal(source.characters(), source.length(), out); } -String BaseDateAndTimeInputType::serialize(double value) const +String BaseDateAndTimeInputType::serialize(const Decimal& value) const { - if (!isfinite(value)) + if (!value.isFinite()) return String(); DateComponents date; - if (!setMillisecondToDateComponents(value, &date)) + if (!setMillisecondToDateComponents(value.toDouble(), &date)) return String(); return serializeWithComponents(date); } String BaseDateAndTimeInputType::serializeWithComponents(const DateComponents& date) const { - double step; + Decimal step; if (!element()->getAllowedValueStep(&step)) return date.toString(); - if (!fmod(step, msecPerMinute)) + if (step.remainder(msecPerMinute).isZero()) return date.toString(DateComponents::None); - if (!fmod(step, msecPerSecond)) + if (step.remainder(msecPerSecond).isZero()) return date.toString(DateComponents::Second); return date.toString(DateComponents::Millisecond); } String BaseDateAndTimeInputType::serializeWithMilliseconds(double value) const { - return serialize(value); + return serialize(Decimal::fromDouble(value)); } String BaseDateAndTimeInputType::localizeValue(const String& proposedValue) const diff --git a/Source/WebCore/html/BaseDateAndTimeInputType.h b/Source/WebCore/html/BaseDateAndTimeInputType.h index da9519101..1658c1fe0 100644 --- a/Source/WebCore/html/BaseDateAndTimeInputType.h +++ b/Source/WebCore/html/BaseDateAndTimeInputType.h @@ -42,7 +42,7 @@ class BaseDateAndTimeInputType : public TextFieldInputType { protected: BaseDateAndTimeInputType(HTMLInputElement* element) : TextFieldInputType(element) { } virtual void handleKeydownEvent(KeyboardEvent*) OVERRIDE; - virtual double parseToDouble(const String&, double) const OVERRIDE; + virtual Decimal parseToNumber(const String&, const Decimal&) const OVERRIDE; virtual bool parseToDateComponents(const String&, DateComponents*) const OVERRIDE; String serializeWithComponents(const DateComponents&) const; @@ -52,19 +52,20 @@ private: virtual DateComponents::Type dateType() const = 0; virtual double valueAsDate() const OVERRIDE; virtual void setValueAsDate(double, ExceptionCode&) const OVERRIDE; - virtual double valueAsNumber() const OVERRIDE; - virtual void setValueAsNumber(double, TextFieldEventBehavior, ExceptionCode&) const OVERRIDE; + virtual double valueAsDouble() const OVERRIDE; + virtual void setValueAsDecimal(const Decimal&, TextFieldEventBehavior, ExceptionCode&) const OVERRIDE; virtual bool typeMismatchFor(const String&) const OVERRIDE; virtual bool typeMismatch() const OVERRIDE; - virtual double defaultValueForStepUp() const OVERRIDE; + virtual Decimal defaultValueForStepUp() const OVERRIDE; virtual bool isSteppable() const OVERRIDE; virtual void handleWheelEvent(WheelEvent*) OVERRIDE; - virtual String serialize(double) const OVERRIDE; + virtual String serialize(const Decimal&) const OVERRIDE; virtual String serializeWithMilliseconds(double) const; virtual String localizeValue(const String&) const OVERRIDE; virtual String visibleValue() const OVERRIDE; virtual String convertFromVisibleValue(const String&) const OVERRIDE; virtual String sanitizeValue(const String&) const OVERRIDE; + double parseToDouble(const String&) const; }; } // namespace WebCore diff --git a/Source/WebCore/html/CollectionType.h b/Source/WebCore/html/CollectionType.h index 5bed4d89a..dd71d918b 100644 --- a/Source/WebCore/html/CollectionType.h +++ b/Source/WebCore/html/CollectionType.h @@ -51,7 +51,6 @@ enum CollectionType { TSectionRows, // all row elements in this table section TRCells, // all cells in this row SelectOptions, - SelectedOptions, DataListOptions, MapAreas, diff --git a/Source/WebCore/html/DOMURL.cpp b/Source/WebCore/html/DOMURL.cpp index 37c6520e6..4f9780dcd 100644 --- a/Source/WebCore/html/DOMURL.cpp +++ b/Source/WebCore/html/DOMURL.cpp @@ -78,7 +78,7 @@ String DOMURL::createObjectURL(ScriptExecutionContext* scriptExecutionContext, B if (publicURL.isEmpty()) return String(); - ThreadableBlobRegistry::registerBlobURL(publicURL, blob->url()); + ThreadableBlobRegistry::registerBlobURL(scriptExecutionContext->securityOrigin(), publicURL, blob->url()); scriptExecutionContext->publicURLManager().blobURLs().add(publicURL.string()); return publicURL.string(); diff --git a/Source/WebCore/html/DateInputType.cpp b/Source/WebCore/html/DateInputType.cpp index 88b312065..f5d1a0293 100644 --- a/Source/WebCore/html/DateInputType.cpp +++ b/Source/WebCore/html/DateInputType.cpp @@ -45,9 +45,9 @@ namespace WebCore { using namespace HTMLNames; -static const double dateDefaultStep = 1.0; -static const double dateDefaultStepBase = 0.0; -static const double dateStepScaleFactor = 86400000.0; +static const int dateDefaultStep = 1; +static const int dateDefaultStepBase = 0; +static const int dateStepScaleFactor = 86400000; inline DateInputType::DateInputType(HTMLInputElement* element) : BaseDateAndTimeInputType(element) @@ -73,10 +73,10 @@ StepRange DateInputType::createStepRange(AnyStepHandling anyStepHandling) const { DEFINE_STATIC_LOCAL(const StepRange::StepDescription, stepDescription, (dateDefaultStep, dateDefaultStepBase, dateStepScaleFactor, StepRange::ParsedStepValueShouldBeInteger)); - double stepBase = parseToDouble(element()->fastGetAttribute(minAttr), 0); - double minimum = parseToDouble(element()->fastGetAttribute(minAttr), DateComponents::minimumDate()); - double maximum = parseToDouble(element()->fastGetAttribute(maxAttr), DateComponents::maximumDate()); - StepRange::DoubleWithDecimalPlacesOrMissing step = StepRange::parseStep(anyStepHandling, stepDescription, element()->fastGetAttribute(stepAttr)); + const Decimal stepBase = parseToNumber(element()->fastGetAttribute(minAttr), 0); + const Decimal minimum = parseToNumber(element()->fastGetAttribute(minAttr), Decimal::fromDouble(DateComponents::minimumDate())); + const Decimal maximum = parseToNumber(element()->fastGetAttribute(maxAttr), Decimal::fromDouble(DateComponents::maximumDate())); + const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element()->fastGetAttribute(stepAttr)); return StepRange(stepBase, minimum, maximum, step, stepDescription); } diff --git a/Source/WebCore/html/DateTimeInputType.cpp b/Source/WebCore/html/DateTimeInputType.cpp index 6cefd9d8d..78a9184f6 100644 --- a/Source/WebCore/html/DateTimeInputType.cpp +++ b/Source/WebCore/html/DateTimeInputType.cpp @@ -43,9 +43,9 @@ namespace WebCore { using namespace HTMLNames; -static const double dateTimeDefaultStep = 60.0; -static const double dateTimeDefaultStepBase = 0.0; -static const double dateTimeStepScaleFactor = 1000.0; +static const int dateTimeDefaultStep = 60; +static const int dateTimeDefaultStepBase = 0; +static const int dateTimeStepScaleFactor = 1000; PassOwnPtr<InputType> DateTimeInputType::create(HTMLInputElement* element) { @@ -62,19 +62,19 @@ DateComponents::Type DateTimeInputType::dateType() const return DateComponents::DateTime; } -double DateTimeInputType::defaultValueForStepUp() const +Decimal DateTimeInputType::defaultValueForStepUp() const { - return currentTimeMS(); + return Decimal::fromDouble(currentTimeMS()); } StepRange DateTimeInputType::createStepRange(AnyStepHandling anyStepHandling) const { DEFINE_STATIC_LOCAL(const StepRange::StepDescription, stepDescription, (dateTimeDefaultStep, dateTimeDefaultStepBase, dateTimeStepScaleFactor, StepRange::ScaledStepValueShouldBeInteger)); - double stepBase = parseToDouble(element()->fastGetAttribute(minAttr), 0); - double minimum = parseToDouble(element()->fastGetAttribute(minAttr), DateComponents::minimumDateTime()); - double maximum = parseToDouble(element()->fastGetAttribute(maxAttr), DateComponents::maximumDateTime()); - StepRange::DoubleWithDecimalPlacesOrMissing step = StepRange::parseStep(anyStepHandling, stepDescription, element()->fastGetAttribute(stepAttr)); + const Decimal stepBase = parseToNumber(element()->fastGetAttribute(minAttr), 0); + const Decimal minimum = parseToNumber(element()->fastGetAttribute(minAttr), Decimal::fromDouble(DateComponents::minimumDateTime())); + const Decimal maximum = parseToNumber(element()->fastGetAttribute(maxAttr), Decimal::fromDouble(DateComponents::maximumDateTime())); + const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element()->fastGetAttribute(stepAttr)); return StepRange(stepBase, minimum, maximum, step, stepDescription); } diff --git a/Source/WebCore/html/DateTimeInputType.h b/Source/WebCore/html/DateTimeInputType.h index e80886f32..d1f4a78aa 100644 --- a/Source/WebCore/html/DateTimeInputType.h +++ b/Source/WebCore/html/DateTimeInputType.h @@ -46,7 +46,7 @@ private: virtual const AtomicString& formControlType() const OVERRIDE; virtual DateComponents::Type dateType() const OVERRIDE; virtual StepRange createStepRange(AnyStepHandling) const OVERRIDE; - virtual double defaultValueForStepUp() const OVERRIDE; + virtual Decimal defaultValueForStepUp() const OVERRIDE; virtual bool parseToDateComponentsInternal(const UChar*, unsigned length, DateComponents*) const OVERRIDE; virtual bool setMillisecondToDateComponents(double, DateComponents*) const OVERRIDE; virtual bool isDateTimeField() const OVERRIDE; diff --git a/Source/WebCore/html/DateTimeLocalInputType.cpp b/Source/WebCore/html/DateTimeLocalInputType.cpp index b657050ed..38e449fba 100644 --- a/Source/WebCore/html/DateTimeLocalInputType.cpp +++ b/Source/WebCore/html/DateTimeLocalInputType.cpp @@ -42,9 +42,9 @@ namespace WebCore { using namespace HTMLNames; -static const double dateTimeLocalDefaultStep = 60.0; -static const double dateTimeLocalDefaultStepBase = 0.0; -static const double dateTimeLocalStepScaleFactor = 1000.0; +static const int dateTimeLocalDefaultStep = 60; +static const int dateTimeLocalDefaultStepBase = 0; +static const int dateTimeLocalStepScaleFactor = 1000; PassOwnPtr<InputType> DateTimeLocalInputType::create(HTMLInputElement* element) { @@ -77,10 +77,10 @@ StepRange DateTimeLocalInputType::createStepRange(AnyStepHandling anyStepHandlin { DEFINE_STATIC_LOCAL(const StepRange::StepDescription, stepDescription, (dateTimeLocalDefaultStep, dateTimeLocalDefaultStepBase, dateTimeLocalStepScaleFactor, StepRange::ScaledStepValueShouldBeInteger)); - double stepBase = parseToDouble(element()->fastGetAttribute(minAttr), 0); - double minimum = parseToDouble(element()->fastGetAttribute(minAttr), DateComponents::minimumDateTime()); - double maximum = parseToDouble(element()->fastGetAttribute(maxAttr), DateComponents::maximumDateTime()); - StepRange::DoubleWithDecimalPlacesOrMissing step = StepRange::parseStep(anyStepHandling, stepDescription, element()->fastGetAttribute(stepAttr)); + const Decimal stepBase = parseToNumber(element()->fastGetAttribute(minAttr), 0); + const Decimal minimum = parseToNumber(element()->fastGetAttribute(minAttr), Decimal::fromDouble(DateComponents::minimumDateTime())); + const Decimal maximum = parseToNumber(element()->fastGetAttribute(maxAttr), Decimal::fromDouble(DateComponents::maximumDateTime())); + const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element()->fastGetAttribute(stepAttr)); return StepRange(stepBase, minimum, maximum, step, stepDescription); } diff --git a/Source/WebCore/html/FileInputType.cpp b/Source/WebCore/html/FileInputType.cpp index 72b675691..f164511d8 100644 --- a/Source/WebCore/html/FileInputType.cpp +++ b/Source/WebCore/html/FileInputType.cpp @@ -23,11 +23,13 @@ #include "FileInputType.h" #include "Chrome.h" +#include "DragData.h" #include "ElementShadow.h" #include "Event.h" #include "File.h" #include "FileList.h" #include "FileSystem.h" +#include "FormController.h" #include "FormDataList.h" #include "Frame.h" #include "HTMLInputElement.h" @@ -99,11 +101,13 @@ const AtomicString& FileInputType::formControlType() const return InputTypeNames::file(); } -bool FileInputType::saveFormControlState(String& result) const +FormControlState FileInputType::saveFormControlState() const { if (m_fileList->isEmpty()) - return false; - result = String(); + return FormControlState(); + // FIXME: FormControlState should be capable to have multiple strings and we + // should stop the following ugly string concatenation. + StringBuilder result; unsigned numFiles = m_fileList->length(); for (unsigned i = 0; i < numFiles; ++i) { result.append(m_fileList->item(i)->path()); @@ -111,14 +115,14 @@ bool FileInputType::saveFormControlState(String& result) const result.append(m_fileList->item(i)->name()); result.append('\0'); } - return true; + return FormControlState(result.toString()); } -void FileInputType::restoreFormControlState(const String& state) +void FileInputType::restoreFormControlState(const FormControlState& state) { Vector<FileChooserFileInfo> files; Vector<String> paths; - state.split('\0', paths); + state.value().split('\0', paths); for (unsigned i = 0; i < paths.size(); ++i) { Vector<String> pathAndName; paths[i].split('\1', pathAndName); @@ -187,6 +191,7 @@ void FileInputType::handleDOMActivateEvent(Event* event) settings.allowsMultipleFiles = input->fastHasAttribute(multipleAttr); #endif settings.acceptMIMETypes = input->acceptMIMETypes(); + settings.acceptFileExtensions = input->acceptFileExtensions(); settings.selectedFiles = m_fileList->paths(); #if ENABLE(MEDIA_CAPTURE) settings.capture = input->capture(); @@ -374,6 +379,7 @@ void FileInputType::receiveDropForDirectoryUpload(const Vector<String>& paths) settings.allowsMultipleFiles = true; settings.selectedFiles.append(paths[0]); settings.acceptMIMETypes = input->acceptMIMETypes(); + settings.acceptFileExtensions = input->acceptFileExtensions(); chrome->enumerateChosenDirectory(newFileChooser(settings)); } } @@ -389,16 +395,25 @@ void FileInputType::updateRendering(PassRefPtr<Icon> icon) element()->renderer()->repaint(); } -void FileInputType::receiveDroppedFiles(const Vector<String>& paths) +bool FileInputType::receiveDroppedFiles(const DragData* dragData) { + Vector<String> paths; + dragData->asFilenames(paths); + if (paths.isEmpty()) + return false; + HTMLInputElement* input = element(); #if ENABLE(DIRECTORY_UPLOAD) if (input->fastHasAttribute(webkitdirectoryAttr)) { receiveDropForDirectoryUpload(paths); - return; + return true; } #endif +#if ENABLE(FILE_SYSTEM) + m_droppedFileSystemId = dragData->droppedFileSystemId(); +#endif + Vector<FileChooserFileInfo> files; for (unsigned i = 0; i < paths.size(); ++i) files.append(FileChooserFileInfo(paths[i])); @@ -410,8 +425,16 @@ void FileInputType::receiveDroppedFiles(const Vector<String>& paths) firstFileOnly.append(files[0]); filesChosen(firstFileOnly); } + return true; } +#if ENABLE(FILE_SYSTEM) +String FileInputType::droppedFileSystemId() +{ + return m_droppedFileSystemId; +} +#endif + Icon* FileInputType::icon() const { return m_icon.get(); diff --git a/Source/WebCore/html/FileInputType.h b/Source/WebCore/html/FileInputType.h index 1f9fbce39..903639ec0 100644 --- a/Source/WebCore/html/FileInputType.h +++ b/Source/WebCore/html/FileInputType.h @@ -39,6 +39,7 @@ namespace WebCore { +class DragData; class FileList; class FileInputType : public BaseClickableWithKeyInputType, private FileChooserClient, private FileIconLoaderClient { @@ -48,8 +49,8 @@ public: private: FileInputType(HTMLInputElement*); virtual const AtomicString& formControlType() const OVERRIDE; - virtual bool saveFormControlState(String&) const OVERRIDE; - virtual void restoreFormControlState(const String&) OVERRIDE; + virtual FormControlState saveFormControlState() const OVERRIDE; + virtual void restoreFormControlState(const FormControlState&) OVERRIDE; virtual bool appendFormData(FormDataList&, bool) const OVERRIDE; virtual bool valueMissing(const String&) const OVERRIDE; virtual String valueMissingText() const OVERRIDE; @@ -62,7 +63,10 @@ private: virtual bool canSetValue(const String&) OVERRIDE; virtual bool getTypeSpecificValue(String&) OVERRIDE; // Checked first, before internal storage or the value attribute. virtual void setValue(const String&, bool valueChanged, TextFieldEventBehavior) OVERRIDE; - virtual void receiveDroppedFiles(const Vector<String>&) OVERRIDE; + virtual bool receiveDroppedFiles(const DragData*) OVERRIDE; +#if ENABLE(FILE_SYSTEM) + virtual String droppedFileSystemId() OVERRIDE; +#endif virtual Icon* icon() const OVERRIDE; virtual bool isFileUpload() const OVERRIDE; virtual void createShadowSubtree() OVERRIDE; @@ -83,6 +87,10 @@ private: RefPtr<FileList> m_fileList; RefPtr<Icon> m_icon; + +#if ENABLE(FILE_SYSTEM) + String m_droppedFileSystemId; +#endif }; } // namespace WebCore diff --git a/Source/WebCore/html/FormAssociatedElement.cpp b/Source/WebCore/html/FormAssociatedElement.cpp index 390c149ba..21791f096 100644 --- a/Source/WebCore/html/FormAssociatedElement.cpp +++ b/Source/WebCore/html/FormAssociatedElement.cpp @@ -25,6 +25,7 @@ #include "config.h" #include "FormAssociatedElement.h" +#include "FormController.h" #include "HTMLFormControlElement.h" #include "HTMLFormElement.h" #include "HTMLNames.h" @@ -57,7 +58,7 @@ void FormAssociatedElement::didMoveToNewDocument(Document* oldDocument) { HTMLElement* element = toHTMLElement(this); if (oldDocument && element->fastHasAttribute(formAttr)) - oldDocument->unregisterFormElementWithFormAttribute(this); + oldDocument->formController()->unregisterFormElementWithFormAttribute(this); } void FormAssociatedElement::insertedInto(ContainerNode* insertionPoint) @@ -68,14 +69,14 @@ void FormAssociatedElement::insertedInto(ContainerNode* insertionPoint) HTMLElement* element = toHTMLElement(this); if (element->fastHasAttribute(formAttr)) - element->document()->registerFormElementWithFormAttribute(this); + element->document()->formController()->registerFormElementWithFormAttribute(this); } void FormAssociatedElement::removedFrom(ContainerNode* insertionPoint) { HTMLElement* element = toHTMLElement(this); if (insertionPoint->inDocument() && element->fastHasAttribute(formAttr)) - element->document()->unregisterFormElementWithFormAttribute(this); + element->document()->formController()->unregisterFormElementWithFormAttribute(this); // If the form and element are both in the same tree, preserve the connection to the form. // Otherwise, null out our form and remove ourselves from the form's list of elements. if (m_form && element->highestAncestor() != m_form->highestAncestor()) @@ -152,7 +153,7 @@ void FormAssociatedElement::formAttributeChanged() if (!element->fastHasAttribute(formAttr)) { // The form attribute removed. We need to reset form owner here. setForm(element->findFormAncestor()); - element->document()->unregisterFormElementWithFormAttribute(this); + element->document()->formController()->unregisterFormElementWithFormAttribute(this); } else resetFormOwner(); } @@ -220,6 +221,12 @@ void FormAssociatedElement::setCustomValidity(const String& error) m_customValidationMessage = error; } +const AtomicString& FormAssociatedElement::name() const +{ + const AtomicString& name = toHTMLElement(this)->getNameAttribute(); + return name.isNull() ? emptyAtom : name; +} + const HTMLElement* toHTMLElement(const FormAssociatedElement* associatedElement) { if (associatedElement->isFormControlElement()) diff --git a/Source/WebCore/html/FormAssociatedElement.h b/Source/WebCore/html/FormAssociatedElement.h index 4c9cda00c..5a47ea076 100644 --- a/Source/WebCore/html/FormAssociatedElement.h +++ b/Source/WebCore/html/FormAssociatedElement.h @@ -48,7 +48,10 @@ public: virtual bool isFormControlElement() const = 0; virtual bool isEnumeratable() const = 0; - const AtomicString& name() const { return formControlName(); } + // Returns the 'name' attribute value. If this element has no name + // attribute, it returns an empty string instead of null string. + // Note that the 'name' IDL attribute doesn't use this function. + virtual const AtomicString& name() const; // Override in derived classes to get the encoded name=value pair for submitting. // Return true for a successful control (see HTML4-17.13.2). @@ -95,8 +98,6 @@ protected: String customValidationMessage() const; private: - virtual const AtomicString& formControlName() const = 0; - virtual void refFormAssociatedElement() = 0; virtual void derefFormAssociatedElement() = 0; diff --git a/Source/WebCore/html/FormController.cpp b/Source/WebCore/html/FormController.cpp new file mode 100644 index 000000000..80d01f2ca --- /dev/null +++ b/Source/WebCore/html/FormController.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2006, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010, 2011, 2012 Google Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "FormController.h" + +#include "HTMLFormControlElementWithState.h" + +namespace WebCore { + +using namespace HTMLNames; + +FormController::FormController() +{ +} + +FormController::~FormController() +{ +} + +Vector<String> FormController::formElementsState() const +{ + Vector<String> stateVector; + stateVector.reserveInitialCapacity(m_formElementsWithState.size() * 3); + typedef FormElementListHashSet::const_iterator Iterator; + Iterator end = m_formElementsWithState.end(); + for (Iterator it = m_formElementsWithState.begin(); it != end; ++it) { + HTMLFormControlElementWithState* elementWithState = *it; + if (!elementWithState->shouldSaveAndRestoreFormControlState()) + continue; + FormControlState state = elementWithState->saveFormControlState(); + if (!state.hasValue()) + continue; + stateVector.append(elementWithState->name().string()); + stateVector.append(elementWithState->formControlType().string()); + stateVector.append(state.value()); + } + return stateVector; +} + +static bool isNotFormControlTypeCharacter(UChar ch) +{ + return ch != '-' && (ch > 'z' || ch < 'a'); +} + +void FormController::setStateForNewFormElements(const Vector<String>& stateVector) +{ + // Walk the state vector backwards so that the value to use for each + // name/type pair first is the one at the end of each individual vector + // in the FormElementStateMap. We're using them like stacks. + typedef FormElementStateMap::iterator Iterator; + m_formElementsWithState.clear(); + + if (stateVector.size() % 3) + return; + for (size_t i = 0; i < stateVector.size(); i += 3) { + if (stateVector[i + 1].find(isNotFormControlTypeCharacter) != notFound) + return; + } + + for (size_t i = stateVector.size() / 3 * 3; i; i -= 3) { + AtomicString name = stateVector[i - 3]; + AtomicString type = stateVector[i - 2]; + const String& value = stateVector[i - 1]; + FormElementKey key(name.impl(), type.impl()); + Iterator it = m_stateForNewFormElements.find(key); + if (it != m_stateForNewFormElements.end()) + it->second.append(value); + else { + Vector<String> valueList(1); + valueList[0] = value; + m_stateForNewFormElements.set(key, valueList); + } + } +} + +bool FormController::hasStateForNewFormElements() const +{ + return !m_stateForNewFormElements.isEmpty(); +} + +FormControlState FormController::takeStateForFormElement(AtomicStringImpl* name, AtomicStringImpl* type) +{ + typedef FormElementStateMap::iterator Iterator; + Iterator it = m_stateForNewFormElements.find(FormElementKey(name, type)); + if (it == m_stateForNewFormElements.end()) + return FormControlState(); + ASSERT(it->second.size()); + FormControlState state(it->second.last()); + if (it->second.size() > 1) + it->second.removeLast(); + else + m_stateForNewFormElements.remove(it); + return state; +} + +void FormController::registerFormElementWithFormAttribute(FormAssociatedElement* element) +{ + ASSERT(toHTMLElement(element)->fastHasAttribute(formAttr)); + m_formElementsWithFormAttribute.add(element); +} + +void FormController::unregisterFormElementWithFormAttribute(FormAssociatedElement* element) +{ + m_formElementsWithFormAttribute.remove(element); +} + +void FormController::resetFormElementsOwner() +{ + typedef FormAssociatedElementListHashSet::iterator Iterator; + Iterator end = m_formElementsWithFormAttribute.end(); + for (Iterator it = m_formElementsWithFormAttribute.begin(); it != end; ++it) + (*it)->resetFormOwner(); +} + +FormElementKey::FormElementKey(AtomicStringImpl* name, AtomicStringImpl* type) + : m_name(name), m_type(type) +{ + ref(); +} + +FormElementKey::~FormElementKey() +{ + deref(); +} + +FormElementKey::FormElementKey(const FormElementKey& other) + : m_name(other.name()), m_type(other.type()) +{ + ref(); +} + +FormElementKey& FormElementKey::operator=(const FormElementKey& other) +{ + other.ref(); + deref(); + m_name = other.name(); + m_type = other.type(); + return *this; +} + +void FormElementKey::ref() const +{ + if (name()) + name()->ref(); + if (type()) + type()->ref(); +} + +void FormElementKey::deref() const +{ + if (name()) + name()->deref(); + if (type()) + type()->deref(); +} + +unsigned FormElementKeyHash::hash(const FormElementKey& key) +{ + return StringHasher::hashMemory<sizeof(FormElementKey)>(&key); +} + +} // namespace WebCore + diff --git a/Source/WebCore/html/FormController.h b/Source/WebCore/html/FormController.h new file mode 100644 index 000000000..0f2207adf --- /dev/null +++ b/Source/WebCore/html/FormController.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2010, 2011, 2012 Google Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef FormController_h +#define FormController_h + +#include "CheckedRadioButtons.h" +#include <wtf/Forward.h> +#include <wtf/ListHashSet.h> +#include <wtf/Vector.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class FormAssociatedElement; +class HTMLFormControlElementWithState; + +class FormElementKey { +public: + FormElementKey(AtomicStringImpl* = 0, AtomicStringImpl* = 0); + ~FormElementKey(); + FormElementKey(const FormElementKey&); + FormElementKey& operator=(const FormElementKey&); + + AtomicStringImpl* name() const { return m_name; } + AtomicStringImpl* type() const { return m_type; } + + // Hash table deleted values, which are only constructed and never copied or destroyed. + FormElementKey(WTF::HashTableDeletedValueType) : m_name(hashTableDeletedValue()) { } + bool isHashTableDeletedValue() const { return m_name == hashTableDeletedValue(); } + +private: + void ref() const; + void deref() const; + + static AtomicStringImpl* hashTableDeletedValue() { return reinterpret_cast<AtomicStringImpl*>(-1); } + + AtomicStringImpl* m_name; + AtomicStringImpl* m_type; +}; + +inline bool operator==(const FormElementKey& a, const FormElementKey& b) +{ + return a.name() == b.name() && a.type() == b.type(); +} + +struct FormElementKeyHash { + static unsigned hash(const FormElementKey&); + static bool equal(const FormElementKey& a, const FormElementKey& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +struct FormElementKeyHashTraits : WTF::GenericHashTraits<FormElementKey> { + static void constructDeletedValue(FormElementKey& slot) { new (NotNull, &slot) FormElementKey(WTF::HashTableDeletedValue); } + static bool isDeletedValue(const FormElementKey& value) { return value.isHashTableDeletedValue(); } +}; + +class FormControlState { +public: + FormControlState() : m_type(TypeSkip) { } + explicit FormControlState(const String& value) : m_type(TypeRestore), m_value(value) { } + FormControlState(const FormControlState& another) : m_type(another.m_type), m_value(another.m_value) { } + FormControlState& operator=(const FormControlState&); + + bool hasValue() const { return m_type == TypeRestore; } + String value() const { return m_value; } + +private: + enum Type { TypeSkip, TypeRestore }; + Type m_type; + String m_value; +}; + +inline FormControlState& FormControlState::operator=(const FormControlState& another) +{ + m_type = another.m_type; + m_value = another.m_value; + return *this; +} + +class FormController { +public: + static PassOwnPtr<FormController> create() + { + return adoptPtr(new FormController); + } + ~FormController(); + + CheckedRadioButtons& checkedRadioButtons() { return m_checkedRadioButtons; } + + void registerFormElementWithState(HTMLFormControlElementWithState* control) { m_formElementsWithState.add(control); } + void unregisterFormElementWithState(HTMLFormControlElementWithState* control) { m_formElementsWithState.remove(control); } + // This should be callled only by Document::formElementsState(). + Vector<String> formElementsState() const; + // This should be callled only by Document::setStateForNewFormElements(). + void setStateForNewFormElements(const Vector<String>&); + bool hasStateForNewFormElements() const; + FormControlState takeStateForFormElement(AtomicStringImpl* name, AtomicStringImpl* type); + + void registerFormElementWithFormAttribute(FormAssociatedElement*); + void unregisterFormElementWithFormAttribute(FormAssociatedElement*); + void resetFormElementsOwner(); + +private: + FormController(); + + CheckedRadioButtons m_checkedRadioButtons; + + typedef ListHashSet<HTMLFormControlElementWithState*, 64> FormElementListHashSet; + FormElementListHashSet m_formElementsWithState; + typedef ListHashSet<RefPtr<FormAssociatedElement>, 32> FormAssociatedElementListHashSet; + FormAssociatedElementListHashSet m_formElementsWithFormAttribute; + + typedef HashMap<FormElementKey, Vector<String>, FormElementKeyHash, FormElementKeyHashTraits> FormElementStateMap; + FormElementStateMap m_stateForNewFormElements; + +}; + +} // namespace WebCore +#endif diff --git a/Source/WebCore/html/HTMLButtonElement.cpp b/Source/WebCore/html/HTMLButtonElement.cpp index f14419d3b..d82a1a737 100644 --- a/Source/WebCore/html/HTMLButtonElement.cpp +++ b/Source/WebCore/html/HTMLButtonElement.cpp @@ -53,6 +53,11 @@ PassRefPtr<HTMLButtonElement> HTMLButtonElement::create(const QualifiedName& tag return adoptRef(new HTMLButtonElement(tagName, document, form)); } +void HTMLButtonElement::setType(const AtomicString& type) +{ + setAttribute(typeAttr, type); +} + RenderObject* HTMLButtonElement::createRenderer(RenderArena* arena, RenderStyle*) { return new (arena) RenderButton(this); diff --git a/Source/WebCore/html/HTMLButtonElement.h b/Source/WebCore/html/HTMLButtonElement.h index c8dd27edf..7bf99b7b8 100644 --- a/Source/WebCore/html/HTMLButtonElement.h +++ b/Source/WebCore/html/HTMLButtonElement.h @@ -32,6 +32,8 @@ class HTMLButtonElement : public HTMLFormControlElement { public: static PassRefPtr<HTMLButtonElement> create(const QualifiedName&, Document*, HTMLFormElement*); + void setType(const AtomicString&); + String value() const; private: diff --git a/Source/WebCore/html/HTMLButtonElement.idl b/Source/WebCore/html/HTMLButtonElement.idl index 692fd612a..02e91abef 100644 --- a/Source/WebCore/html/HTMLButtonElement.idl +++ b/Source/WebCore/html/HTMLButtonElement.idl @@ -30,7 +30,7 @@ module html { attribute [Reflect] boolean formNoValidate; attribute [Reflect] DOMString formTarget; attribute [Reflect] DOMString name; - readonly attribute DOMString type; + attribute [TreatNullAs=NullString] DOMString type; attribute [Reflect] DOMString value; readonly attribute boolean willValidate; diff --git a/Source/WebCore/html/HTMLCollection.cpp b/Source/WebCore/html/HTMLCollection.cpp index a908da0a3..c20c464e8 100644 --- a/Source/WebCore/html/HTMLCollection.cpp +++ b/Source/WebCore/html/HTMLCollection.cpp @@ -61,7 +61,6 @@ bool HTMLCollection::shouldIncludeChildren(CollectionType type) case MapAreas: case OtherCollection: case SelectOptions: - case SelectedOptions: case DataListOptions: case WindowNamedItems: #if ENABLE(MICRODATA) @@ -119,13 +118,6 @@ inline bool HTMLCollection::isAcceptableElement(Element* element) const return element->hasLocalName(trTag); case SelectOptions: return element->hasLocalName(optionTag); - case SelectedOptions: - if (element->hasLocalName(optionTag)) { - HTMLOptionElement* option = static_cast<HTMLOptionElement*>(element); - if (option->selected()) - return true; - } - return false; case DataListOptions: if (element->hasLocalName(optionTag)) { HTMLOptionElement* option = static_cast<HTMLOptionElement*>(element); diff --git a/Source/WebCore/html/HTMLElement.cpp b/Source/WebCore/html/HTMLElement.cpp index 3d4ee8d29..50152cad1 100644 --- a/Source/WebCore/html/HTMLElement.cpp +++ b/Source/WebCore/html/HTMLElement.cpp @@ -578,7 +578,7 @@ void HTMLElement::insertAdjacentHTML(const String& where, const String& markup, Element* contextElement = contextElementForInsertion(where, this, ec); if (!contextElement) return; - RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(markup, this, AllowScriptingContent, ec); + RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(markup, contextElement, AllowScriptingContent, ec); if (!fragment) return; insertAdjacent(where, fragment.get(), ec); diff --git a/Source/WebCore/html/HTMLFormCollection.cpp b/Source/WebCore/html/HTMLFormCollection.cpp index cb3cca954..d9b6a5dff 100644 --- a/Source/WebCore/html/HTMLFormCollection.cpp +++ b/Source/WebCore/html/HTMLFormCollection.cpp @@ -38,7 +38,6 @@ using namespace HTMLNames; HTMLFormCollection::HTMLFormCollection(HTMLElement* base) : HTMLCollection(base, FormControls) - , currentPos(0) { } diff --git a/Source/WebCore/html/HTMLFormCollection.h b/Source/WebCore/html/HTMLFormCollection.h index 32fb1eb0b..315ea68b6 100644 --- a/Source/WebCore/html/HTMLFormCollection.h +++ b/Source/WebCore/html/HTMLFormCollection.h @@ -57,8 +57,6 @@ private: const Vector<FormAssociatedElement*>& formControlElements() const; const Vector<HTMLImageElement*>& formImageElements() const; unsigned numberOfFormControlElements() const; - - mutable int currentPos; }; } //namespace diff --git a/Source/WebCore/html/HTMLFormControlElement.cpp b/Source/WebCore/html/HTMLFormControlElement.cpp index 0a51392e1..d09814865 100644 --- a/Source/WebCore/html/HTMLFormControlElement.cpp +++ b/Source/WebCore/html/HTMLFormControlElement.cpp @@ -241,17 +241,6 @@ void HTMLFormControlElement::removedFrom(ContainerNode* insertionPoint) FormAssociatedElement::removedFrom(insertionPoint); } -const AtomicString& HTMLFormControlElement::formControlName() const -{ - const AtomicString& name = getNameAttribute(); - return name.isNull() ? emptyAtom : name; -} - -void HTMLFormControlElement::setName(const AtomicString& value) -{ - setAttribute(nameAttr, value); -} - bool HTMLFormControlElement::wasChangedSinceLastFormControlChangeEvent() const { return m_wasChangedSinceLastFormControlChangeEvent; diff --git a/Source/WebCore/html/HTMLFormControlElement.h b/Source/WebCore/html/HTMLFormControlElement.h index 5fac956b4..b3a9354ca 100644 --- a/Source/WebCore/html/HTMLFormControlElement.h +++ b/Source/WebCore/html/HTMLFormControlElement.h @@ -77,9 +77,6 @@ public: const AtomicString& type() const { return formControlType(); } - void setName(const AtomicString& name); - - virtual const AtomicString& formControlName() const OVERRIDE; virtual const AtomicString& formControlType() const OVERRIDE = 0; virtual bool isEnabledFormControl() const { return !disabled(); } virtual bool isReadOnlyFormControl() const { return readOnly(); } @@ -109,8 +106,8 @@ public: static HTMLFormControlElement* enclosingFormControlElement(Node*); - using TreeShared<ContainerNode>::ref; - using TreeShared<ContainerNode>::deref; + using Node::ref; + using Node::deref; protected: HTMLFormControlElement(const QualifiedName& tagName, Document*, HTMLFormElement*); diff --git a/Source/WebCore/html/HTMLFormControlElementWithState.cpp b/Source/WebCore/html/HTMLFormControlElementWithState.cpp index bf94c54d9..4152224ee 100644 --- a/Source/WebCore/html/HTMLFormControlElementWithState.cpp +++ b/Source/WebCore/html/HTMLFormControlElementWithState.cpp @@ -25,6 +25,7 @@ #include "config.h" #include "HTMLFormControlElementWithState.h" +#include "FormController.h" #include "HTMLFormElement.h" namespace WebCore { @@ -32,19 +33,19 @@ namespace WebCore { HTMLFormControlElementWithState::HTMLFormControlElementWithState(const QualifiedName& tagName, Document* doc, HTMLFormElement* f) : HTMLFormControlElement(tagName, doc, f) { - document()->registerFormElementWithState(this); + document()->formController()->registerFormElementWithState(this); } HTMLFormControlElementWithState::~HTMLFormControlElementWithState() { - document()->unregisterFormElementWithState(this); + document()->formController()->unregisterFormElementWithState(this); } void HTMLFormControlElementWithState::didMoveToNewDocument(Document* oldDocument) { if (oldDocument) - oldDocument->unregisterFormElementWithState(this); - document()->registerFormElementWithState(this); + oldDocument->formController()->unregisterFormElementWithState(this); + document()->formController()->registerFormElementWithState(this); HTMLFormControlElement::didMoveToNewDocument(oldDocument); } @@ -61,6 +62,11 @@ bool HTMLFormControlElementWithState::shouldSaveAndRestoreFormControlState() con return attached() && shouldAutocomplete(); } +FormControlState HTMLFormControlElementWithState::saveFormControlState() const +{ + return FormControlState(); +} + void HTMLFormControlElementWithState::finishParsingChildren() { HTMLFormControlElement::finishParsingChildren(); @@ -72,9 +78,9 @@ void HTMLFormControlElementWithState::finishParsingChildren() return; Document* doc = document(); - if (doc->hasStateForNewFormElements()) { - String state; - if (doc->takeStateForFormElement(name().impl(), type().impl(), state)) + if (doc->formController()->hasStateForNewFormElements()) { + FormControlState state = doc->formController()->takeStateForFormElement(name().impl(), type().impl()); + if (state.hasValue()) restoreFormControlState(state); } } diff --git a/Source/WebCore/html/HTMLFormControlElementWithState.h b/Source/WebCore/html/HTMLFormControlElementWithState.h index 73de3260b..0af98f8d8 100644 --- a/Source/WebCore/html/HTMLFormControlElementWithState.h +++ b/Source/WebCore/html/HTMLFormControlElementWithState.h @@ -28,6 +28,8 @@ namespace WebCore { +class FormControlState; + class HTMLFormControlElementWithState : public HTMLFormControlElement { public: virtual ~HTMLFormControlElementWithState(); @@ -35,8 +37,9 @@ public: virtual bool canContainRangeEndPoint() const { return false; } bool shouldSaveAndRestoreFormControlState() const; - virtual bool saveFormControlState(String&) const { return false; } - virtual void restoreFormControlState(const String&) { } + virtual FormControlState saveFormControlState() const; + // The specified FormControlState must have one string value. + virtual void restoreFormControlState(const FormControlState&) { } protected: HTMLFormControlElementWithState(const QualifiedName& tagName, Document*, HTMLFormElement*); diff --git a/Source/WebCore/html/HTMLFormElement.cpp b/Source/WebCore/html/HTMLFormElement.cpp index ea54b93ba..719b46eac 100644 --- a/Source/WebCore/html/HTMLFormElement.cpp +++ b/Source/WebCore/html/HTMLFormElement.cpp @@ -34,6 +34,7 @@ #include "EventNames.h" #include "FileList.h" #include "FileSystem.h" +#include "FormController.h" #include "FormData.h" #include "FormDataList.h" #include "FormState.h" @@ -144,7 +145,7 @@ void HTMLFormElement::didNotifyDescendantInsertions(ContainerNode* insertionPoin ASSERT(insertionPoint->inDocument()); HTMLElement::didNotifyDescendantInsertions(insertionPoint); if (hasID()) - document()->resetFormElementsOwner(); + document()->formController()->resetFormElementsOwner(); } static inline Node* findRoot(Node* n) @@ -163,7 +164,7 @@ void HTMLFormElement::removedFrom(ContainerNode* insertionPoint) associatedElements[i]->formRemovedFromTree(root); HTMLElement::removedFrom(insertionPoint); if (insertionPoint->inDocument() && hasID()) - document()->resetFormElementsOwner(); + document()->formController()->resetFormElementsOwner(); } void HTMLFormElement::handleLocalEvents(Event* event) diff --git a/Source/WebCore/html/HTMLInputElement.cpp b/Source/WebCore/html/HTMLInputElement.cpp index 9248f0bc2..4a8107ee5 100644 --- a/Source/WebCore/html/HTMLInputElement.cpp +++ b/Source/WebCore/html/HTMLInputElement.cpp @@ -37,6 +37,7 @@ #include "EventNames.h" #include "ExceptionCode.h" #include "FileList.h" +#include "FormController.h" #include "Frame.h" #include "HTMLCollection.h" #include "HTMLDataListElement.h" @@ -127,10 +128,10 @@ HTMLInputElement::~HTMLInputElement() // setForm(0) may register this to a document-level radio button group. // We should unregister it to avoid accessing a deleted object. if (isRadioButton()) - document()->checkedRadioButtons().removeButton(this); + document()->formController()->checkedRadioButtons().removeButton(this); } -const AtomicString& HTMLInputElement::formControlName() const +const AtomicString& HTMLInputElement::name() const { return m_name.isNull() ? emptyAtom : m_name; } @@ -273,7 +274,7 @@ bool HTMLInputElement::stepMismatch() const return willValidate() && m_inputType->stepMismatch(value()); } -bool HTMLInputElement::getAllowedValueStep(double* step) const +bool HTMLInputElement::getAllowedValueStep(Decimal* step) const { return m_inputType->getAllowedValueStep(step); } @@ -295,15 +296,26 @@ void HTMLInputElement::stepDown(int n, ExceptionCode& ec) bool HTMLInputElement::isKeyboardFocusable(KeyboardEvent* event) const { - if (isTextField()) - return HTMLTextFormControlElement::isFocusable(); - return HTMLTextFormControlElement::isKeyboardFocusable(event) && m_inputType->isKeyboardFocusable(); + return m_inputType->isKeyboardFocusable(event); } bool HTMLInputElement::isMouseFocusable() const { - if (isTextField()) - return HTMLTextFormControlElement::isFocusable(); + return m_inputType->isMouseFocusable(); +} + +bool HTMLInputElement::isTextFormControlFocusable() const +{ + return HTMLTextFormControlElement::isFocusable(); +} + +bool HTMLInputElement::isTextFormControlKeyboardFocusable(KeyboardEvent* event) const +{ + return HTMLTextFormControlElement::isKeyboardFocusable(event); +} + +bool HTMLInputElement::isTextFormControlMouseFocusable() const +{ return HTMLTextFormControlElement::isMouseFocusable(); } @@ -456,7 +468,6 @@ void HTMLInputElement::subtreeHasChanged() { ASSERT(isTextField()); ASSERT(renderer()); - RenderTextControlSingleLine* renderTextControl = toRenderTextControlSingleLine(renderer()); bool wasChanged = wasChangedSinceLastFormControlChangeEvent(); setChangedSinceLastFormControlChangeEvent(true); @@ -472,12 +483,7 @@ void HTMLInputElement::subtreeHasChanged() // Recalc for :invalid and hasUnacceptableValue() change. setNeedsStyleRecalc(); - if (cancelButtonElement()) - renderTextControl->updateCancelButtonVisibility(); - - // If the incremental attribute is set, then dispatch the search event - if (searchEventsShouldBeDispatched() && isSearchField() && m_inputType) - static_cast<SearchInputType*>(m_inputType.get())->startSearchEventTimer(); + m_inputType->subtreeHasChanged(); if (!wasChanged && focused()) { if (Frame* frame = document()->frame()) @@ -498,12 +504,12 @@ const AtomicString& HTMLInputElement::formControlType() const return m_inputType->formControlType(); } -bool HTMLInputElement::saveFormControlState(String& result) const +FormControlState HTMLInputElement::saveFormControlState() const { - return m_inputType->saveFormControlState(result); + return m_inputType->saveFormControlState(); } -void HTMLInputElement::restoreFormControlState(const String& state) +void HTMLInputElement::restoreFormControlState(const FormControlState& state) { m_inputType->restoreFormControlState(state); m_stateRestored = true; @@ -553,7 +559,7 @@ void HTMLInputElement::collectStyleForAttribute(const Attribute& attribute, Styl } else if (attribute.name() == borderAttr && isImageButton()) applyBorderAttributeToStyle(attribute, style); else - return HTMLTextFormControlElement::collectStyleForAttribute(attribute, style); + HTMLTextFormControlElement::collectStyleForAttribute(attribute, style); } void HTMLInputElement::parseAttribute(const Attribute& attribute) @@ -949,7 +955,7 @@ void HTMLInputElement::setValueAsDate(double value, ExceptionCode& ec) double HTMLInputElement::valueAsNumber() const { - return m_inputType->valueAsNumber(); + return m_inputType->valueAsDouble(); } void HTMLInputElement::setValueAsNumber(double newValue, ExceptionCode& ec, TextFieldEventBehavior eventBehavior) @@ -958,7 +964,7 @@ void HTMLInputElement::setValueAsNumber(double newValue, ExceptionCode& ec, Text ec = NOT_SUPPORTED_ERR; return; } - m_inputType->setValueAsNumber(newValue, eventBehavior, ec); + m_inputType->setValueAsDouble(newValue, eventBehavior, ec); } String HTMLInputElement::placeholder() const @@ -971,11 +977,6 @@ void HTMLInputElement::setPlaceholder(const String& value) setAttribute(placeholderAttr, value); } -bool HTMLInputElement::searchEventsShouldBeDispatched() const -{ - return hasAttribute(incrementalAttr); -} - void HTMLInputElement::setValueFromRenderer(const String& value) { // File upload controls will never use this. @@ -1134,7 +1135,7 @@ static inline bool isRFC2616TokenCharacter(UChar ch) return isASCII(ch) && ch > ' ' && ch != '"' && ch != '(' && ch != ')' && ch != ',' && ch != '/' && (ch < ':' || ch > '@') && (ch < '[' || ch > ']') && ch != '{' && ch != '}' && ch != 0x7f; } -static inline bool isValidMIMEType(const String& type) +static bool isValidMIMEType(const String& type) { size_t slashPosition = type.find('/'); if (slashPosition == notFound || !slashPosition || slashPosition == type.length() - 1) @@ -1146,26 +1147,41 @@ static inline bool isValidMIMEType(const String& type) return true; } -Vector<String> HTMLInputElement::acceptMIMETypes() +static bool isValidFileExtension(const String& type) { - Vector<String> mimeTypes; + if (type.length() < 2) + return false; + return type[0] == '.'; +} - String acceptString = accept(); +static Vector<String> parseAcceptAttribute(const String& acceptString, bool (*predicate)(const String&)) +{ + Vector<String> types; if (acceptString.isEmpty()) - return mimeTypes; + return types; Vector<String> splitTypes; acceptString.split(',', false, splitTypes); for (size_t i = 0; i < splitTypes.size(); ++i) { - String trimmedMimeType = stripLeadingAndTrailingHTMLSpaces(splitTypes[i]); - if (trimmedMimeType.isEmpty()) + String trimmedType = stripLeadingAndTrailingHTMLSpaces(splitTypes[i]); + if (trimmedType.isEmpty()) continue; - if (!isValidMIMEType(trimmedMimeType)) + if (!predicate(trimmedType)) continue; - mimeTypes.append(trimmedMimeType.lower()); + types.append(trimmedType.lower()); } - return mimeTypes; + return types; +} + +Vector<String> HTMLInputElement::acceptMIMETypes() +{ + return parseAcceptAttribute(fastGetAttribute(acceptAttr), isValidMIMEType); +} + +Vector<String> HTMLInputElement::acceptFileExtensions() +{ + return parseAcceptAttribute(fastGetAttribute(acceptAttr), isValidFileExtension); } String HTMLInputElement::accept() const @@ -1225,11 +1241,18 @@ void HTMLInputElement::setFiles(PassRefPtr<FileList> files) m_inputType->setFiles(files); } -void HTMLInputElement::receiveDroppedFiles(const Vector<String>& filenames) +bool HTMLInputElement::receiveDroppedFiles(const DragData* dragData) { - m_inputType->receiveDroppedFiles(filenames); + return m_inputType->receiveDroppedFiles(dragData); } +#if ENABLE(FILE_SYSTEM) +String HTMLInputElement::droppedFileSystemId() +{ + return m_inputType->droppedFileSystemId(); +} +#endif + Icon* HTMLInputElement::icon() const { return m_inputType->icon(); @@ -1317,9 +1340,7 @@ bool HTMLInputElement::isRequiredFormControl() const void HTMLInputElement::addSearchResult() { - ASSERT(isSearchField()); - if (renderer()) - toRenderTextControlSingleLine(renderer())->addSearchResult(); + m_inputType->addSearchResult(); } void HTMLInputElement::onSearch() @@ -1351,16 +1372,14 @@ void HTMLInputElement::didChangeForm() Node::InsertionNotificationRequest HTMLInputElement::insertedInto(ContainerNode* insertionPoint) { HTMLTextFormControlElement::insertedInto(insertionPoint); - if (!insertionPoint->inDocument()) - return InsertionDone; - ASSERT(inDocument()); - addToRadioButtonGroup(); + if (insertionPoint->inDocument() && !form()) + addToRadioButtonGroup(); return InsertionDone; } void HTMLInputElement::removedFrom(ContainerNode* insertionPoint) { - if (insertionPoint->inDocument()) + if (insertionPoint->inDocument() && !form()) removeFromRadioButtonGroup(); HTMLTextFormControlElement::removedFrom(insertionPoint); } @@ -1374,7 +1393,7 @@ void HTMLInputElement::didMoveToNewDocument(Document* oldDocument) if (needsSuspensionCallback) oldDocument->unregisterForPageCacheSuspensionCallbacks(this); if (isRadioButton()) - oldDocument->checkedRadioButtons().removeButton(this); + oldDocument->formController()->checkedRadioButtons().removeButton(this); } if (needsSuspensionCallback) @@ -1672,7 +1691,7 @@ CheckedRadioButtons* HTMLInputElement::checkedRadioButtons() const if (HTMLFormElement* formElement = form()) return &formElement->checkedRadioButtons(); if (inDocument()) - return &document()->checkedRadioButtons(); + return &document()->formController()->checkedRadioButtons(); return 0; } diff --git a/Source/WebCore/html/HTMLInputElement.h b/Source/WebCore/html/HTMLInputElement.h index 9756e4fc4..3af2b3525 100644 --- a/Source/WebCore/html/HTMLInputElement.h +++ b/Source/WebCore/html/HTMLInputElement.h @@ -30,6 +30,8 @@ namespace WebCore { +class CheckedRadioButtons; +class DragData; class FileList; class HTMLDataListElement; class HTMLOptionElement; @@ -65,7 +67,7 @@ public: double maximum() const; // Sets the "allowed value step" defined in the HTML spec to the specified double pointer. // Returns false if there is no "allowed value step." - bool getAllowedValueStep(double*) const; + bool getAllowedValueStep(Decimal*) const; StepRange createStepRange(AnyStepHandling) const; // Implementations of HTMLInputElement::stepUp() and stepDown(). @@ -195,6 +197,7 @@ public: void setDefaultValue(const String&); Vector<String> acceptMIMETypes(); + Vector<String> acceptFileExtensions(); String accept() const; String alt() const; @@ -212,7 +215,14 @@ public: FileList* files(); void setFiles(PassRefPtr<FileList>); - void receiveDroppedFiles(const Vector<String>&); + + // Returns true if the given DragData has more than one dropped files. + bool receiveDroppedFiles(const DragData*); + +#if ENABLE(FILE_SYSTEM) + String droppedFileSystemId(); +#endif + Icon* icon() const; // These functions are used for rendering the input active during a // drag-and-drop operation. @@ -221,7 +231,6 @@ public: void addSearchResult(); void onSearch(); - bool searchEventsShouldBeDispatched() const; #if ENABLE(DATALIST) HTMLElement* list() const; @@ -232,6 +241,10 @@ public: void setValueInternal(const String&, TextFieldEventBehavior); + bool isTextFormControlFocusable() const; + bool isTextFormControlKeyboardFocusable(KeyboardEvent*) const; + bool isTextFormControlMouseFocusable() const; + void cacheSelectionInResponseToSetValue(int caretOffset) { cacheSelection(caretOffset, caretOffset, SelectionHasNoDirection); } #if ENABLE(INPUT_TYPE_COLOR) @@ -253,6 +266,8 @@ public: void setHeight(unsigned); void setWidth(unsigned); + virtual const AtomicString& name() const OVERRIDE; + protected: HTMLInputElement(const QualifiedName&, Document*, HTMLFormElement*, bool createdByParser); void createShadowSubtree(); @@ -275,16 +290,14 @@ private: virtual void aboutToUnload(); virtual bool shouldUseInputMethod(); - virtual const AtomicString& formControlName() const; - virtual bool isTextFormControl() const { return isTextField(); } virtual bool canTriggerImplicitSubmission() const { return isTextField(); } virtual const AtomicString& formControlType() const; - virtual bool saveFormControlState(String& value) const; - virtual void restoreFormControlState(const String&); + virtual FormControlState saveFormControlState() const OVERRIDE; + virtual void restoreFormControlState(const FormControlState&) OVERRIDE; virtual bool canStartSelection() const; diff --git a/Source/WebCore/html/HTMLKeygenElement.idl b/Source/WebCore/html/HTMLKeygenElement.idl index 914fb99b0..fc6f25c81 100644 --- a/Source/WebCore/html/HTMLKeygenElement.idl +++ b/Source/WebCore/html/HTMLKeygenElement.idl @@ -36,7 +36,7 @@ module html { attribute [Reflect] boolean disabled; readonly attribute HTMLFormElement form; attribute [Reflect] DOMString keytype; - attribute DOMString name; + attribute [Reflect] DOMString name; readonly attribute DOMString type; diff --git a/Source/WebCore/html/HTMLMediaElement.cpp b/Source/WebCore/html/HTMLMediaElement.cpp index 0af609ff8..c4accadb2 100644 --- a/Source/WebCore/html/HTMLMediaElement.cpp +++ b/Source/WebCore/html/HTMLMediaElement.cpp @@ -86,7 +86,6 @@ #include <wtf/text/CString.h> #if USE(ACCELERATED_COMPOSITING) -#include "RenderView.h" #include "RenderLayerCompositor.h" #endif @@ -143,8 +142,6 @@ static const char* boolString(bool val) #define LOG_CACHED_TIME_WARNINGS 0 #endif -static const float invalidMediaTime = -1; - #if ENABLE(MEDIA_SOURCE) // URL protocol used to signal that the media source API is being used. static const char* mediaSourceURLProtocol = "x-media-source"; @@ -224,11 +221,11 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* docum #if ENABLE(MEDIA_SOURCE) , m_sourceState(SOURCE_CLOSED) #endif - , m_cachedTime(invalidMediaTime) + , m_cachedTime(MediaPlayer::invalidTime()) , m_cachedTimeWallClockUpdateTime(0) , m_minimumWallClockTimeToCacheMediaTime(0) - , m_fragmentStartTime(invalidMediaTime) - , m_fragmentEndTime(invalidMediaTime) + , m_fragmentStartTime(MediaPlayer::invalidTime()) + , m_fragmentEndTime(MediaPlayer::invalidTime()) , m_pendingLoadFlags(0) , m_playing(false) , m_isWaitingUntilMediaCanStart(false) @@ -1101,7 +1098,9 @@ void HTMLMediaElement::updateActiveTextTrackCues(float movieTime) if (!currentCues.contains(previousCues[i]) && previousCues[i].data()->isActive()) activeSetChanged = true; - for (size_t i = 0; !activeSetChanged && i < currentCuesSize; ++i) { + for (size_t i = 0; i < currentCuesSize; ++i) { + currentCues[i].data()->updateDisplayTree(movieTime); + if (!currentCues[i].data()->isActive()) activeSetChanged = true; } @@ -1693,7 +1692,7 @@ void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state) if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA) scheduleEvent(eventNames().playingEvent); - if (m_autoplaying && m_paused && autoplay() && !document()->isSandboxed(SandboxAutomaticFeatures)) { + if (m_autoplaying && m_paused && autoplay() && !document()->isSandboxed(SandboxAutomaticFeatures) && !userGestureRequiredForRateChange()) { m_paused = false; invalidateCachedTime(); scheduleEvent(eventNames().playEvent); @@ -2046,7 +2045,7 @@ void HTMLMediaElement::invalidateCachedTime() static const double minimumTimePlayingBeforeCacheSnapshot = 0.5; m_minimumWallClockTimeToCacheMediaTime = WTF::currentTime() + minimumTimePlayingBeforeCacheSnapshot; - m_cachedTime = invalidMediaTime; + m_cachedTime = MediaPlayer::invalidTime(); } // playback state @@ -2064,7 +2063,7 @@ float HTMLMediaElement::currentTime() const return m_lastSeekTime; } - if (m_cachedTime != invalidMediaTime && m_paused) { + if (m_cachedTime != MediaPlayer::invalidTime() && m_paused) { #if LOG_CACHED_TIME_WARNINGS float delta = m_cachedTime - m_player->currentTime(); if (delta > minCachedDeltaForWarning) @@ -2077,7 +2076,7 @@ float HTMLMediaElement::currentTime() const double now = WTF::currentTime(); double maximumDurationToCacheMediaTime = m_player->maximumDurationToCacheMediaTime(); - if (maximumDurationToCacheMediaTime && m_cachedTime != invalidMediaTime && !m_paused && now > m_minimumWallClockTimeToCacheMediaTime) { + if (maximumDurationToCacheMediaTime && m_cachedTime != MediaPlayer::invalidTime() && !m_paused && now > m_minimumWallClockTimeToCacheMediaTime) { double wallClockDelta = now - m_cachedTimeWallClockUpdateTime; // Not too soon, use the cached time only if it hasn't expired. @@ -2094,7 +2093,7 @@ float HTMLMediaElement::currentTime() const } #if LOG_CACHED_TIME_WARNINGS - if (maximumDurationToCacheMediaTime && now > m_minimumWallClockTimeToCacheMediaTime && m_cachedTime != invalidMediaTime) { + if (maximumDurationToCacheMediaTime && now > m_minimumWallClockTimeToCacheMediaTime && m_cachedTime != MediaPlayer::invalidTime()) { double wallClockDelta = now - m_cachedTimeWallClockUpdateTime; float delta = m_cachedTime + (m_playbackRate * wallClockDelta) - m_player->currentTime(); LOG(Media, "HTMLMediaElement::currentTime - cached time was %f seconds off of media time when it expired", delta); @@ -2124,7 +2123,7 @@ float HTMLMediaElement::startTime() const double HTMLMediaElement::initialTime() const { - if (m_fragmentStartTime != invalidMediaTime) + if (m_fragmentStartTime != MediaPlayer::invalidTime()) return m_fragmentStartTime; if (!m_player) @@ -2724,8 +2723,8 @@ void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>*) { ASSERT(m_player); - if (m_fragmentEndTime != invalidMediaTime && currentTime() >= m_fragmentEndTime && m_playbackRate > 0) { - m_fragmentEndTime = invalidMediaTime; + if (m_fragmentEndTime != MediaPlayer::invalidTime() && currentTime() >= m_fragmentEndTime && m_playbackRate > 0) { + m_fragmentEndTime = MediaPlayer::invalidTime(); if (!m_mediaController && !m_paused) { // changes paused to true and fires a simple event named pause at the media element. pauseInternal(); @@ -2798,7 +2797,7 @@ PassRefPtr<TextTrack> HTMLMediaElement::addTextTrack(const String& kind, const S // 4.8.10.12.4 Text track API // The addTextTrack(kind, label, language) method of media elements, when invoked, must run the following steps: - + // 1. If kind is not one of the following strings, then throw a SyntaxError exception and abort these steps if (!TextTrack::isValidKindKeyword(kind)) { ec = SYNTAX_ERR; @@ -2808,15 +2807,23 @@ PassRefPtr<TextTrack> HTMLMediaElement::addTextTrack(const String& kind, const S // 2. If the label argument was omitted, let label be the empty string. // 3. If the language argument was omitted, let language be the empty string. // 4. Create a new TextTrack object. - RefPtr<TextTrack> textTrack = TextTrack::create(ActiveDOMObject::scriptExecutionContext(), this, kind, label, language); // 5. Create a new text track corresponding to the new object, and set its text track kind to kind, its text - // track label to label, its text track language to language, its text track readiness state to the text track - // loaded state, its text track mode to the text track hidden mode, and its text track list of cues to an empty list. - + // track label to label, its text track language to language... + RefPtr<TextTrack> textTrack = TextTrack::create(ActiveDOMObject::scriptExecutionContext(), this, kind, label, language); + + // Note, due to side effects when changing track parameters, we have to + // first append the track to the text track list. + // 6. Add the new text track to the media element's list of text tracks. textTracks()->append(textTrack); + // ... its text track readiness state to the text track loaded state ... + textTrack->setReadinessState(TextTrack::Loaded); + + // ... its text track mode to the text track hidden mode, and its text track list of cues to an empty list ... + textTrack->setMode(TextTrack::HIDDEN, ec); + return textTrack.release(); } @@ -3723,6 +3730,7 @@ void HTMLMediaElement::userCancelledLoad() stopPeriodicTimers(); m_loadTimer.stop(); m_loadState = WaitingForSource; + m_pendingLoadFlags = 0; // 2 - Set the error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORTED. m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED); @@ -4398,7 +4406,7 @@ void HTMLMediaElement::prepareMediaFragmentURI() if (m_fragmentStartTime > dur) m_fragmentStartTime = dur; } else - m_fragmentStartTime = invalidMediaTime; + m_fragmentStartTime = MediaPlayer::invalidTime(); double end = fragmentParser.endTime(); if (end != MediaFragmentURIParser::invalidTimeValue() && end > 0 && end > m_fragmentStartTime) { @@ -4406,15 +4414,15 @@ void HTMLMediaElement::prepareMediaFragmentURI() if (m_fragmentEndTime > dur) m_fragmentEndTime = dur; } else - m_fragmentEndTime = invalidMediaTime; + m_fragmentEndTime = MediaPlayer::invalidTime(); - if (m_fragmentStartTime != invalidMediaTime && m_readyState < HAVE_FUTURE_DATA) + if (m_fragmentStartTime != MediaPlayer::invalidTime() && m_readyState < HAVE_FUTURE_DATA) prepareToPlay(); } void HTMLMediaElement::applyMediaFragmentURI() { - if (m_fragmentStartTime != invalidMediaTime) { + if (m_fragmentStartTime != MediaPlayer::invalidTime()) { ExceptionCode ignoredException; m_sentEndEvent = false; seek(m_fragmentStartTime, ignoredException); @@ -4455,6 +4463,15 @@ String HTMLMediaElement::mediaPlayerUserAgent() const } +MediaPlayerClient::CORSMode HTMLMediaElement::mediaPlayerCORSMode() const +{ + if (!fastHasAttribute(HTMLNames::crossoriginAttr)) + return Unspecified; + if (equalIgnoringCase(fastGetAttribute(HTMLNames::crossoriginAttr), "use-credentials")) + return UseCredentials; + return Anonymous; +} + bool HTMLMediaElement::mediaPlayerNeedsSiteSpecificHacks() const { Settings* settings = document()->settings(); diff --git a/Source/WebCore/html/HTMLMediaElement.h b/Source/WebCore/html/HTMLMediaElement.h index 5363ae255..1673e1535 100644 --- a/Source/WebCore/html/HTMLMediaElement.h +++ b/Source/WebCore/html/HTMLMediaElement.h @@ -427,6 +427,7 @@ private: virtual String mediaPlayerReferrer() const OVERRIDE; virtual String mediaPlayerUserAgent() const OVERRIDE; + virtual CORSMode mediaPlayerCORSMode() const OVERRIDE; virtual bool mediaPlayerNeedsSiteSpecificHacks() const OVERRIDE; virtual String mediaPlayerDocumentHost() const OVERRIDE; diff --git a/Source/WebCore/html/HTMLMetaElement.cpp b/Source/WebCore/html/HTMLMetaElement.cpp index cf114f67a..0e0d27b7a 100644 --- a/Source/WebCore/html/HTMLMetaElement.cpp +++ b/Source/WebCore/html/HTMLMetaElement.cpp @@ -75,7 +75,7 @@ void HTMLMetaElement::process() document()->processViewport(contentValue, ViewportArguments::ViewportMeta); else if (equalIgnoringCase(name(), "referrer")) document()->processReferrerPolicy(contentValue); -#if USE(LEGACY_VIEWPORT_ADAPTION) +#if ENABLE(LEGACY_VIEWPORT_ADAPTION) else if (equalIgnoringCase(name(), "handheldfriendly") && equalIgnoringCase(contentValue, "true")) document()->processViewport("width=device-width", ViewportArguments::HandheldFriendlyMeta); else if (equalIgnoringCase(name(), "mobileoptimized")) diff --git a/Source/WebCore/html/HTMLObjectElement.cpp b/Source/WebCore/html/HTMLObjectElement.cpp index 024e825bc..8c5b817a2 100644 --- a/Source/WebCore/html/HTMLObjectElement.cpp +++ b/Source/WebCore/html/HTMLObjectElement.cpp @@ -501,12 +501,6 @@ bool HTMLObjectElement::appendFormData(FormDataList& encoding, bool) return true; } -const AtomicString& HTMLObjectElement::formControlName() const -{ - const AtomicString& name = getNameAttribute(); - return name.isNull() ? emptyAtom : name; -} - HTMLFormElement* HTMLObjectElement::virtualForm() const { return FormAssociatedElement::form(); diff --git a/Source/WebCore/html/HTMLObjectElement.h b/Source/WebCore/html/HTMLObjectElement.h index c44da021d..932e5bccd 100644 --- a/Source/WebCore/html/HTMLObjectElement.h +++ b/Source/WebCore/html/HTMLObjectElement.h @@ -58,8 +58,8 @@ public: bool checkValidity() { return true; } virtual void setCustomValidity(const String&) OVERRIDE { } - using TreeShared<ContainerNode>::ref; - using TreeShared<ContainerNode>::deref; + using Node::ref; + using Node::deref; virtual bool canContainRangeEndPoint() const { return useFallbackContent(); } @@ -101,8 +101,6 @@ private: virtual void derefFormAssociatedElement() { deref(); } virtual HTMLFormElement* virtualForm() const; - virtual const AtomicString& formControlName() const; - #if ENABLE(MICRODATA) virtual String itemValueText() const OVERRIDE; virtual void setItemValueText(const String&, ExceptionCode&) OVERRIDE; diff --git a/Source/WebCore/html/HTMLPropertiesCollection.cpp b/Source/WebCore/html/HTMLPropertiesCollection.cpp index f0a141214..b3f3f1c7b 100644 --- a/Source/WebCore/html/HTMLPropertiesCollection.cpp +++ b/Source/WebCore/html/HTMLPropertiesCollection.cpp @@ -128,7 +128,7 @@ Element* HTMLPropertiesCollection::itemAfter(Element* base, Element* previous) c if (!current->isHTMLElement()) continue; HTMLElement* element = toHTMLElement(current); - if (element->fastHasAttribute(itempropAttr)) { + if (element->fastHasAttribute(itempropAttr) && element->itemProp()->length()) { return element; } } diff --git a/Source/WebCore/html/HTMLSelectElement.cpp b/Source/WebCore/html/HTMLSelectElement.cpp index d066c9fb0..a5d8ad2bc 100644 --- a/Source/WebCore/html/HTMLSelectElement.cpp +++ b/Source/WebCore/html/HTMLSelectElement.cpp @@ -33,6 +33,7 @@ #include "Chrome.h" #include "ChromeClient.h" #include "EventNames.h" +#include "FormController.h" #include "FormDataList.h" #include "Frame.h" #include "HTMLFormElement.h" @@ -349,13 +350,6 @@ bool HTMLSelectElement::childShouldCreateRenderer(const NodeRenderingContext& ch return childContext.isOnUpperEncapsulationBoundary() && HTMLFormControlElementWithState::childShouldCreateRenderer(childContext); } -HTMLCollection* HTMLSelectElement::selectedOptions() -{ - if (!m_selectedOptionsCollection) - m_selectedOptionsCollection = HTMLCollection::create(this, SelectedOptions); - return m_selectedOptionsCollection.get(); -} - HTMLOptionsCollection* HTMLSelectElement::options() { if (!m_optionsCollection) @@ -921,7 +915,7 @@ void HTMLSelectElement::deselectItemsWithoutValidation(HTMLElement* excludeEleme } } -bool HTMLSelectElement::saveFormControlState(String& value) const +FormControlState HTMLSelectElement::saveFormControlState() const { const Vector<HTMLElement*>& items = listItems(); size_t length = items.size(); @@ -932,21 +926,21 @@ bool HTMLSelectElement::saveFormControlState(String& value) const bool selected = element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected(); builder.append(selected ? 'X' : '.'); } - value = builder.toString(); - return true; + return FormControlState(builder.toString()); } -void HTMLSelectElement::restoreFormControlState(const String& state) +void HTMLSelectElement::restoreFormControlState(const FormControlState& state) { recalcListItems(); const Vector<HTMLElement*>& items = listItems(); size_t length = items.size(); + String mask = state.value(); for (size_t i = 0; i < length; ++i) { HTMLElement* element = items[i]; if (element->hasTagName(optionTag)) - toHTMLOptionElement(element)->setSelectedState(state[i] == 'X'); + toHTMLOptionElement(element)->setSelectedState(mask[i] == 'X'); } setOptionsChangedOnRenderer(); @@ -964,7 +958,7 @@ void HTMLSelectElement::parseMultipleAttribute(const Attribute& attribute) bool HTMLSelectElement::appendFormData(FormDataList& list, bool) { - const AtomicString& name = formControlName(); + const AtomicString& name = this->name(); if (name.isEmpty()) return false; diff --git a/Source/WebCore/html/HTMLSelectElement.h b/Source/WebCore/html/HTMLSelectElement.h index 3055977ac..e82b117f2 100644 --- a/Source/WebCore/html/HTMLSelectElement.h +++ b/Source/WebCore/html/HTMLSelectElement.h @@ -64,7 +64,6 @@ public: void setValue(const String&); HTMLOptionsCollection* options(); - HTMLCollection* selectedOptions(); void optionElementChildrenChanged(); @@ -121,8 +120,8 @@ private: virtual bool isEnumeratable() const { return true; } virtual bool supportLabels() const OVERRIDE { return true; } - virtual bool saveFormControlState(String& value) const; - virtual void restoreFormControlState(const String&); + virtual FormControlState saveFormControlState() const OVERRIDE; + virtual void restoreFormControlState(const FormControlState&) OVERRIDE; virtual void parseAttribute(const Attribute&) OVERRIDE; virtual bool isPresentationAttribute(const QualifiedName&) const OVERRIDE; @@ -180,7 +179,6 @@ private: virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); OwnPtr<HTMLOptionsCollection> m_optionsCollection; - OwnPtr<HTMLCollection> m_selectedOptionsCollection; // m_listItems contains HTMLOptionElement, HTMLOptGroupElement, and HTMLHRElement objects. mutable Vector<HTMLElement*> m_listItems; diff --git a/Source/WebCore/html/HTMLSelectElement.idl b/Source/WebCore/html/HTMLSelectElement.idl index b86363694..de8d2d6d0 100644 --- a/Source/WebCore/html/HTMLSelectElement.idl +++ b/Source/WebCore/html/HTMLSelectElement.idl @@ -28,7 +28,7 @@ module html { attribute [Reflect] boolean disabled; readonly attribute HTMLFormElement form; attribute boolean multiple; - attribute [TreatNullAs=NullString] DOMString name; + attribute [Reflect] DOMString name; attribute [Reflect] boolean required; attribute long size; @@ -53,7 +53,6 @@ module html { #else void remove(in long index); #endif - readonly attribute HTMLCollection selectedOptions; attribute long selectedIndex; attribute [TreatNullAs=NullString] DOMString value; diff --git a/Source/WebCore/html/HTMLStyleElement.cpp b/Source/WebCore/html/HTMLStyleElement.cpp index 67ddceb4f..e2189e96b 100644 --- a/Source/WebCore/html/HTMLStyleElement.cpp +++ b/Source/WebCore/html/HTMLStyleElement.cpp @@ -25,13 +25,14 @@ #include "HTMLStyleElement.h" #include "Attribute.h" -#include "ContextEnabledFeatures.h" +#include "ContextFeatures.h" #include "Document.h" #include "Event.h" #include "EventSender.h" #include "HTMLNames.h" #include "ScriptEventListener.h" #include "ScriptableDocumentParser.h" +#include "ShadowRoot.h" #include "StyleSheetContents.h" namespace WebCore { @@ -50,7 +51,7 @@ inline HTMLStyleElement::HTMLStyleElement(const QualifiedName& tagName, Document , m_firedLoad(false) , m_loadedSheet(false) #if ENABLE(STYLE_SCOPED) - , m_isRegisteredWithScopingNode(false) + , m_scopedStyleRegistrationState(NotRegistered) #endif { ASSERT(hasTagName(styleTag)); @@ -58,8 +59,8 @@ inline HTMLStyleElement::HTMLStyleElement(const QualifiedName& tagName, Document HTMLStyleElement::~HTMLStyleElement() { - // During tear-down, willRemove isn't called, so m_isRegisteredWithScopingNode may still be set here. - // Therefore we can't ASSERT(!m_isRegisteredWithScopingNode). + // During tear-down, willRemove isn't called, so m_scopedStyleRegistrationState may still be RegisteredAsScoped or RegisteredInShadowRoot here. + // Therefore we can't ASSERT(m_scopedStyleRegistrationState == NotRegistered). StyleElement::clearDocumentData(document(), this); styleLoadEventSender().cancelEvent(this); @@ -79,17 +80,42 @@ void HTMLStyleElement::parseAttribute(const Attribute& attribute) else if (attribute.name() == onerrorAttr) setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, attribute)); #if ENABLE(STYLE_SCOPED) - else if (attribute.name() == scopedAttr) { - if (!attribute.isNull() && !m_isRegisteredWithScopingNode && inDocument()) - registerWithScopingNode(); - else if (attribute.isNull() && m_isRegisteredWithScopingNode) - unregisterWithScopingNode(); - } + else if (attribute.name() == scopedAttr) + scopedAttributeChanged(!attribute.isNull()); #endif else HTMLElement::parseAttribute(attribute); } +#if ENABLE(STYLE_SCOPED) +void HTMLStyleElement::scopedAttributeChanged(bool scoped) +{ + if (!inDocument()) + return; + + if (scoped) { + // As any <style> in a shadow tree is treated as "scoped", + // need to remove the <style> from its shadow root. + if (m_scopedStyleRegistrationState == RegisteredInShadowRoot) + unregisterWithScopingNode(shadowRoot()); + + if (m_scopedStyleRegistrationState != RegisteredAsScoped) + registerWithScopingNode(true); + return; + } + + // If the <style> was scoped, need to remove the <style> from the scoping + // element, i.e. the parent node. + if (m_scopedStyleRegistrationState == RegisteredAsScoped) + unregisterWithScopingNode(parentNode()); + + // As any <style> in a shadow tree is treated as "scoped", + // need to add the <style> to its shadow root. + if (isInShadowTree() && m_scopedStyleRegistrationState != RegisteredInShadowRoot) + registerWithScopingNode(false); +} +#endif + void HTMLStyleElement::finishParsingChildren() { StyleElement::finishParsingChildren(this); @@ -97,18 +123,18 @@ void HTMLStyleElement::finishParsingChildren() } #if ENABLE(STYLE_SCOPED) -void HTMLStyleElement::registerWithScopingNode() +void HTMLStyleElement::registerWithScopingNode(bool scoped) { // Note: We cannot rely on the 'scoped' element already being present when this method is invoked. // Therefore we cannot rely on scoped()! - ASSERT(!m_isRegisteredWithScopingNode); + ASSERT(m_scopedStyleRegistrationState == NotRegistered); ASSERT(inDocument()); - if (m_isRegisteredWithScopingNode) + if (m_scopedStyleRegistrationState != NotRegistered) return; - if (!ContextEnabledFeatures::styleScopedEnabled(document())) + if (!ContextFeatures::styleScopedEnabled(document())) return; - ContainerNode* scope = parentNode(); + ContainerNode* scope = scoped ? parentNode() : shadowRoot(); if (!scope) return; if (!scope->isElementNode() && !scope->isShadowRoot()) { @@ -123,17 +149,17 @@ void HTMLStyleElement::registerWithScopingNode() if (inDocument() && !document()->parsing() && document()->renderer()) document()->styleResolverChanged(DeferRecalcStyle); - m_isRegisteredWithScopingNode = true; + m_scopedStyleRegistrationState = scoped ? RegisteredAsScoped : RegisteredInShadowRoot; } void HTMLStyleElement::unregisterWithScopingNode(ContainerNode* scope) { // Note: We cannot rely on the 'scoped' element still being present when this method is invoked. // Therefore we cannot rely on scoped()! - ASSERT(m_isRegisteredWithScopingNode || !ContextEnabledFeatures::styleScopedEnabled(document())); - if (!m_isRegisteredWithScopingNode) + ASSERT(m_scopedStyleRegistrationState != NotRegistered || !ContextFeatures::styleScopedEnabled(document())); + if (m_scopedStyleRegistrationState == NotRegistered) return; - if (!ContextEnabledFeatures::styleScopedEnabled(document())) + if (!ContextFeatures::styleScopedEnabled(document())) return; ASSERT(scope); @@ -145,7 +171,7 @@ void HTMLStyleElement::unregisterWithScopingNode(ContainerNode* scope) if (inDocument() && !document()->parsing() && document()->renderer()) document()->styleResolverChanged(DeferRecalcStyle); - m_isRegisteredWithScopingNode = false; + m_scopedStyleRegistrationState = NotRegistered; } #endif @@ -155,8 +181,8 @@ Node::InsertionNotificationRequest HTMLStyleElement::insertedInto(ContainerNode* if (insertionPoint->inDocument()) { StyleElement::insertedIntoDocument(document(), this); #if ENABLE(STYLE_SCOPED) - if (scoped() && !m_isRegisteredWithScopingNode) - registerWithScopingNode(); + if (m_scopedStyleRegistrationState == NotRegistered && (scoped() || isInShadowTree())) + registerWithScopingNode(scoped()); #endif } @@ -172,8 +198,12 @@ void HTMLStyleElement::removedFrom(ContainerNode* insertionPoint) // That is, because willRemove() is also called if an ancestor is removed from the document. // Now, if we want to register <style scoped> even if it's not inDocument, // we'd need to find a way to discern whether that is the case, or whether <style scoped> itself is about to be removed. - if (m_isRegisteredWithScopingNode) - unregisterWithScopingNode(parentNode() ? parentNode() : insertionPoint); + if (m_scopedStyleRegistrationState != NotRegistered) { + ContainerNode* scope = parentNode()? parentNode() : insertionPoint; + if (m_scopedStyleRegistrationState == RegisteredInShadowRoot) + scope = scope->shadowRoot(); + unregisterWithScopingNode(scope); + } #endif if (insertionPoint->inDocument()) diff --git a/Source/WebCore/html/HTMLStyleElement.h b/Source/WebCore/html/HTMLStyleElement.h index 7e3125454..63a82c4ee 100644 --- a/Source/WebCore/html/HTMLStyleElement.h +++ b/Source/WebCore/html/HTMLStyleElement.h @@ -76,15 +76,22 @@ private: virtual const AtomicString& media() const; virtual const AtomicString& type() const; - void registerWithScopingNode(); - void unregisterWithScopingNode() { unregisterWithScopingNode(parentNode()); } - void unregisterWithScopingNode(ContainerNode* scope); +#if ENABLE(STYLE_SCOPED) + void scopedAttributeChanged(bool); + void registerWithScopingNode(bool); + void unregisterWithScopingNode(ContainerNode*); +#endif bool m_firedLoad; bool m_loadedSheet; #if ENABLE(STYLE_SCOPED) - bool m_isRegisteredWithScopingNode; + enum ScopedStyleRegistrationState { + NotRegistered, + RegisteredAsScoped, + RegisteredInShadowRoot + }; + ScopedStyleRegistrationState m_scopedStyleRegistrationState; #endif }; diff --git a/Source/WebCore/html/HTMLTextAreaElement.cpp b/Source/WebCore/html/HTMLTextAreaElement.cpp index 643725708..bf5125f8b 100644 --- a/Source/WebCore/html/HTMLTextAreaElement.cpp +++ b/Source/WebCore/html/HTMLTextAreaElement.cpp @@ -34,6 +34,7 @@ #include "Event.h" #include "EventNames.h" #include "ExceptionCode.h" +#include "FormController.h" #include "FormDataList.h" #include "Frame.h" #include "HTMLNames.h" @@ -98,18 +99,17 @@ const AtomicString& HTMLTextAreaElement::formControlType() const return textarea; } -bool HTMLTextAreaElement::saveFormControlState(String& result) const +FormControlState HTMLTextAreaElement::saveFormControlState() const { String currentValue = value(); if (currentValue == defaultValue()) - return false; - result = currentValue; - return true; + return FormControlState(); + return FormControlState(currentValue); } -void HTMLTextAreaElement::restoreFormControlState(const String& state) +void HTMLTextAreaElement::restoreFormControlState(const FormControlState& state) { - setValue(state); + setValue(state.value()); } void HTMLTextAreaElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) diff --git a/Source/WebCore/html/HTMLTextAreaElement.h b/Source/WebCore/html/HTMLTextAreaElement.h index 63ce9ea77..18ebead28 100644 --- a/Source/WebCore/html/HTMLTextAreaElement.h +++ b/Source/WebCore/html/HTMLTextAreaElement.h @@ -90,8 +90,8 @@ private: virtual const AtomicString& formControlType() const; - virtual bool saveFormControlState(String& value) const; - virtual void restoreFormControlState(const String&); + virtual FormControlState saveFormControlState() const OVERRIDE; + virtual void restoreFormControlState(const FormControlState&) OVERRIDE; virtual bool isTextFormControl() const { return true; } diff --git a/Source/WebCore/html/HTMLTextAreaElement.idl b/Source/WebCore/html/HTMLTextAreaElement.idl index f9d80b5ad..729e6dae6 100644 --- a/Source/WebCore/html/HTMLTextAreaElement.idl +++ b/Source/WebCore/html/HTMLTextAreaElement.idl @@ -28,7 +28,7 @@ module html { attribute [Reflect] boolean disabled; readonly attribute HTMLFormElement form; attribute long maxLength setter raises(DOMException); - attribute [TreatNullAs=NullString] DOMString name; + attribute [Reflect] DOMString name; attribute [Reflect] DOMString placeholder; attribute [Reflect] boolean readOnly; attribute [Reflect] boolean required; diff --git a/Source/WebCore/html/HiddenInputType.cpp b/Source/WebCore/html/HiddenInputType.cpp index 4efb8d60f..96d4ce001 100644 --- a/Source/WebCore/html/HiddenInputType.cpp +++ b/Source/WebCore/html/HiddenInputType.cpp @@ -32,6 +32,7 @@ #include "config.h" #include "HiddenInputType.h" +#include "FormController.h" #include "FormDataList.h" #include "HTMLInputElement.h" #include "HTMLNames.h" @@ -51,15 +52,15 @@ const AtomicString& HiddenInputType::formControlType() const return InputTypeNames::hidden(); } -bool HiddenInputType::saveFormControlState(String& result) const +FormControlState HiddenInputType::saveFormControlState() const { - result = element()->value(); - return true; + // FIXME: We should not always save the value. http://webkit.org/b/88685 + return FormControlState(element()->value()); } -void HiddenInputType::restoreFormControlState(const String& string) +void HiddenInputType::restoreFormControlState(const FormControlState& state) { - element()->setAttribute(valueAttr, string); + element()->setAttribute(valueAttr, state.value()); } bool HiddenInputType::supportsValidation() const diff --git a/Source/WebCore/html/HiddenInputType.h b/Source/WebCore/html/HiddenInputType.h index b6be4e995..dbf227883 100644 --- a/Source/WebCore/html/HiddenInputType.h +++ b/Source/WebCore/html/HiddenInputType.h @@ -42,8 +42,8 @@ public: private: HiddenInputType(HTMLInputElement* element) : InputType(element) { } virtual const AtomicString& formControlType() const OVERRIDE; - virtual bool saveFormControlState(String&) const OVERRIDE; - virtual void restoreFormControlState(const String&) OVERRIDE; + virtual FormControlState saveFormControlState() const OVERRIDE; + virtual void restoreFormControlState(const FormControlState&) OVERRIDE; virtual bool supportsValidation() const OVERRIDE; virtual RenderObject* createRenderer(RenderArena*, RenderStyle*) const OVERRIDE; virtual void accessKeyAction(bool sendMouseEvents) OVERRIDE; diff --git a/Source/WebCore/html/InputType.cpp b/Source/WebCore/html/InputType.cpp index e91960d40..514119fea 100644 --- a/Source/WebCore/html/InputType.cpp +++ b/Source/WebCore/html/InputType.cpp @@ -42,6 +42,7 @@ #include "ExceptionCode.h" #include "FileInputType.h" #include "FileList.h" +#include "FormController.h" #include "FormDataList.h" #include "HTMLFormElement.h" #include "HTMLInputElement.h" @@ -168,18 +169,17 @@ bool InputType::isRangeControl() const return false; } -bool InputType::saveFormControlState(String& result) const +FormControlState InputType::saveFormControlState() const { String currentValue = element()->value(); if (currentValue == element()->defaultValue()) - return false; - result = currentValue; - return true; + return FormControlState(); + return FormControlState(currentValue); } -void InputType::restoreFormControlState(const String& state) +void InputType::restoreFormControlState(const FormControlState& state) { - element()->setValue(state); + element()->setValue(state.value()); } bool InputType::isFormDataAppendable() const @@ -205,12 +205,17 @@ void InputType::setValueAsDate(double, ExceptionCode& ec) const ec = INVALID_STATE_ERR; } -double InputType::valueAsNumber() const +double InputType::valueAsDouble() const { return numeric_limits<double>::quiet_NaN(); } -void InputType::setValueAsNumber(double, TextFieldEventBehavior, ExceptionCode& ec) const +void InputType::setValueAsDouble(double doubleValue, TextFieldEventBehavior eventBehavior, ExceptionCode& ec) const +{ + setValueAsDecimal(Decimal::fromDouble(doubleValue), eventBehavior, ec); +} + +void InputType::setValueAsDecimal(const Decimal&, TextFieldEventBehavior, ExceptionCode& ec) const { ec = INVALID_STATE_ERR; } @@ -251,11 +256,11 @@ bool InputType::rangeUnderflow(const String& value) const if (!isSteppable()) return false; - double doubleValue = parseToDouble(value, numeric_limits<double>::quiet_NaN()); - if (isnan(doubleValue)) + const Decimal numericValue = parseToNumberOrNaN(value); + if (!numericValue.isFinite()) return false; - return doubleValue < createStepRange(RejectAny).minimum(); + return numericValue < createStepRange(RejectAny).minimum(); } bool InputType::rangeOverflow(const String& value) const @@ -263,26 +268,26 @@ bool InputType::rangeOverflow(const String& value) const if (!isSteppable()) return false; - double doubleValue = parseToDouble(value, numeric_limits<double>::quiet_NaN()); - if (isnan(doubleValue)) + const Decimal numericValue = parseToNumberOrNaN(value); + if (!numericValue.isFinite()) return false; - return doubleValue > createStepRange(RejectAny).maximum(); + return numericValue > createStepRange(RejectAny).maximum(); } -double InputType::defaultValueForStepUp() const +Decimal InputType::defaultValueForStepUp() const { return 0; } double InputType::minimum() const { - return createStepRange(RejectAny).minimum(); + return createStepRange(RejectAny).minimum().toDouble(); } double InputType::maximum() const { - return createStepRange(RejectAny).maximum(); + return createStepRange(RejectAny).maximum().toDouble(); } bool InputType::sizeShouldIncludeDecoration(int, int& preferredSize) const @@ -296,12 +301,12 @@ bool InputType::isInRange(const String& value) const if (!isSteppable()) return false; - double doubleValue = parseToDouble(value, numeric_limits<double>::quiet_NaN()); - if (isnan(doubleValue)) + const Decimal numericValue = parseToNumberOrNaN(value); + if (!numericValue.isFinite()) return true; StepRange stepRange(createStepRange(RejectAny)); - return doubleValue >= stepRange.minimum() && doubleValue <= stepRange.maximum(); + return numericValue >= stepRange.minimum() && numericValue <= stepRange.maximum(); } bool InputType::isOutOfRange(const String& value) const @@ -309,12 +314,12 @@ bool InputType::isOutOfRange(const String& value) const if (!isSteppable()) return false; - double doubleValue = parseToDouble(value, numeric_limits<double>::quiet_NaN()); - if (isnan(doubleValue)) + const Decimal numericValue = parseToNumberOrNaN(value); + if (!numericValue.isFinite()) return true; StepRange stepRange(createStepRange(RejectAny)); - return doubleValue < stepRange.minimum() || doubleValue > stepRange.maximum(); + return numericValue < stepRange.minimum() || numericValue > stepRange.maximum(); } bool InputType::stepMismatch(const String& value) const @@ -322,11 +327,11 @@ bool InputType::stepMismatch(const String& value) const if (!isSteppable()) return false; - double doubleValue = parseToDouble(value, numeric_limits<double>::quiet_NaN()); - if (isnan(doubleValue)) + const Decimal numericValue = parseToNumberOrNaN(value); + if (!numericValue.isFinite()) return false; - return createStepRange(RejectAny).stepMismatch(doubleValue); + return createStepRange(RejectAny).stepMismatch(numericValue); } String InputType::typeMismatchText() const @@ -360,19 +365,19 @@ String InputType::validationMessage() const if (!isSteppable()) return emptyString(); - double doubleValue = parseToDouble(value, numeric_limits<double>::quiet_NaN()); - if (isnan(doubleValue)) + const Decimal numericValue = parseToNumberOrNaN(value); + if (!numericValue.isFinite()) return emptyString(); StepRange stepRange(createStepRange(RejectAny)); - if (doubleValue < stepRange.minimum()) + if (numericValue < stepRange.minimum()) return validationMessageRangeUnderflowText(serialize(stepRange.minimum())); - if (doubleValue < stepRange.maximum()) + if (numericValue < stepRange.maximum()) return validationMessageRangeOverflowText(serialize(stepRange.maximum())); - if (stepRange.stepMismatch(doubleValue)) { + if (stepRange.stepMismatch(numericValue)) { const String stepString = stepRange.hasStep() ? serializeForNumberType(stepRange.step() / stepRange.stepScaleFactor()) : emptyString(); return validationMessageStepMismatchText(serialize(stepRange.stepBase()), stepString); } @@ -453,17 +458,15 @@ void InputType::destroyShadowSubtree() } } -double InputType::parseToDouble(const String&, double defaultValue) const +Decimal InputType::parseToNumber(const String&, const Decimal& defaultValue) const { ASSERT_NOT_REACHED(); return defaultValue; } -double InputType::parseToDoubleWithDecimalPlaces(const String& src, double defaultValue, unsigned *decimalPlaces) const +Decimal InputType::parseToNumberOrNaN(const String& string) const { - if (decimalPlaces) - *decimalPlaces = 0; - return parseToDouble(src, defaultValue); + return parseToNumber(string, Decimal::nan()); } bool InputType::parseToDateComponents(const String&, DateComponents*) const @@ -472,7 +475,7 @@ bool InputType::parseToDateComponents(const String&, DateComponents*) const return false; } -String InputType::serialize(double) const +String InputType::serialize(const Decimal&) const { ASSERT_NOT_REACHED(); return String(); @@ -497,9 +500,14 @@ bool InputType::canSetStringValue() const return true; } -bool InputType::isKeyboardFocusable() const +bool InputType::isKeyboardFocusable(KeyboardEvent* event) const { - return true; + return element()->isTextFormControlKeyboardFocusable(event); +} + +bool InputType::isMouseFocusable() const +{ + return element()->isTextFormControlMouseFocusable(); } bool InputType::shouldUseInputMethod() const @@ -520,6 +528,10 @@ void InputType::accessKeyAction(bool) element()->focus(false); } +void InputType::addSearchResult() +{ +} + void InputType::attach() { } @@ -664,10 +676,19 @@ bool InputType::hasUnacceptableValue() return false; } -void InputType::receiveDroppedFiles(const Vector<String>&) +bool InputType::receiveDroppedFiles(const DragData*) { ASSERT_NOT_REACHED(); + return false; +} + +#if ENABLE(FILE_SYSTEM) +String InputType::droppedFileSystemId() +{ + ASSERT_NOT_REACHED(); + return String(); } +#endif Icon* InputType::icon() const { @@ -848,6 +869,10 @@ void InputType::readonlyAttributeChanged() { } +void InputType::subtreeHasChanged() +{ +} + String InputType::defaultToolTip() const { return String(); @@ -868,7 +893,7 @@ unsigned InputType::width() const return 0; } -void InputType::applyStep(double count, AnyStepHandling anyStepHandling, TextFieldEventBehavior eventBehavior, ExceptionCode& ec) +void InputType::applyStep(int count, AnyStepHandling anyStepHandling, TextFieldEventBehavior eventBehavior, ExceptionCode& ec) { StepRange stepRange(createStepRange(anyStepHandling)); if (!stepRange.hasStep()) { @@ -876,20 +901,18 @@ void InputType::applyStep(double count, AnyStepHandling anyStepHandling, TextFie return; } - const double nan = numeric_limits<double>::quiet_NaN(); - unsigned currentDecimalPlaces; - double current = parseToDoubleWithDecimalPlaces(element()->value(), nan, ¤tDecimalPlaces); - if (!isfinite(current)) { + const Decimal current = parseToNumberOrNaN(element()->value()); + if (!current.isFinite()) { ec = INVALID_STATE_ERR; return; } - double newValue = current + stepRange.step() * count; - if (isinf(newValue)) { + Decimal newValue = current + stepRange.step() * count; + if (!newValue.isFinite()) { ec = INVALID_STATE_ERR; return; } - double acceptableErrorValue = stepRange.acceptableError(); + const Decimal acceptableErrorValue = stepRange.acceptableError(); if (newValue - stepRange.minimum() < -acceptableErrorValue) { ec = INVALID_STATE_ERR; return; @@ -899,7 +922,7 @@ void InputType::applyStep(double count, AnyStepHandling anyStepHandling, TextFie const AtomicString& stepString = element()->fastGetAttribute(stepAttr); if (!equalIgnoringCase(stepString, "any")) - newValue = stepRange.alignValueForStep(current, currentDecimalPlaces, newValue); + newValue = stepRange.alignValueForStep(current, newValue); if (newValue - stepRange.maximum() > acceptableErrorValue) { ec = INVALID_STATE_ERR; @@ -908,13 +931,13 @@ void InputType::applyStep(double count, AnyStepHandling anyStepHandling, TextFie if (newValue > stepRange.maximum()) newValue = stepRange.maximum(); - element()->setValueAsNumber(newValue, ec, eventBehavior); + setValueAsDecimal(newValue, eventBehavior, ec); if (AXObjectCache::accessibilityEnabled()) element()->document()->axObjectCache()->postNotification(element()->renderer(), AXObjectCache::AXValueChanged, true); } -bool InputType::getAllowedValueStep(double* step) const +bool InputType::getAllowedValueStep(Decimal* step) const { StepRange stepRange(createStepRange(RejectAny)); *step = stepRange.step(); @@ -987,7 +1010,7 @@ void InputType::stepUpFromRenderer(int n) if (!stepRange.hasStep()) return; - double step = stepRange.step(); + const Decimal step = stepRange.step(); int sign; if (step > 0) @@ -997,33 +1020,31 @@ void InputType::stepUpFromRenderer(int n) else sign = 0; - const double nan = numeric_limits<double>::quiet_NaN(); String currentStringValue = element()->value(); - double current = parseToDouble(currentStringValue, nan); - if (!isfinite(current)) { + Decimal current = parseToNumberOrNaN(currentStringValue); + if (!current.isFinite()) { ExceptionCode ec; current = defaultValueForStepUp(); - double nextDiff = step * n; + const Decimal nextDiff = step * n; if (current < stepRange.minimum() - nextDiff) current = stepRange.minimum() - nextDiff; if (current > stepRange.maximum() - nextDiff) current = stepRange.maximum() - nextDiff; - element()->setValueAsNumber(current, ec, DispatchInputAndChangeEvent); + setValueAsDecimal(current, DispatchInputAndChangeEvent, ec); } - if ((sign > 0 && current < stepRange.minimum()) || (sign < 0 && current > stepRange.maximum())) - element()->setValue(serialize(sign > 0 ? stepRange.minimum() : stepRange.maximum()), DispatchInputAndChangeEvent); - else { + if ((sign > 0 && current < stepRange.minimum()) || (sign < 0 && current > stepRange.maximum())) { + ExceptionCode ec; + setValueAsDecimal(sign > 0 ? stepRange.minimum() : stepRange.maximum(), DispatchInputAndChangeEvent, ec); + } else { ExceptionCode ec; if (stepMismatch(element()->value())) { - ASSERT(step); - double newValue; - double scale = pow(10.0, static_cast<double>(max(stepRange.stepDecimalPlaces(), stepRange.stepBaseDecimalPlaces()))); - double base = stepRange.stepBase(); - + ASSERT(!step.isZero()); + const Decimal base = stepRange.stepBase(); + Decimal newValue; if (sign < 0) - newValue = round((base + floor((current - base) / step) * step) * scale) / scale; + newValue = base + ((current - base) / step).floor() * step; else if (sign > 0) - newValue = round((base + ceil((current - base) / step) * step) * scale) / scale; + newValue = base + ((current - base) / step).ceiling() * step; else newValue = current; @@ -1032,8 +1053,7 @@ void InputType::stepUpFromRenderer(int n) if (newValue > stepRange.maximum()) newValue = stepRange.maximum(); - element()->setValueAsNumber(newValue, ec, n == 1 || n == -1 ? DispatchInputAndChangeEvent : DispatchNoEvent); - current = newValue; + setValueAsDecimal(newValue, n == 1 || n == -1 ? DispatchInputAndChangeEvent : DispatchNoEvent, ec); if (n > 1) applyStep(n - 1, AnyIsDefaultStep, DispatchInputAndChangeEvent, ec); else if (n < -1) diff --git a/Source/WebCore/html/InputType.h b/Source/WebCore/html/InputType.h index 83efbdebe..451303d13 100644 --- a/Source/WebCore/html/InputType.h +++ b/Source/WebCore/html/InputType.h @@ -47,6 +47,7 @@ class BeforeTextInsertedEvent; class Chrome; class Color; class DateComponents; +class DragData; class Event; class FileList; class FormDataList; @@ -125,8 +126,8 @@ public: // Form value functions - virtual bool saveFormControlState(String&) const; - virtual void restoreFormControlState(const String&); + virtual FormControlState saveFormControlState() const; + virtual void restoreFormControlState(const FormControlState&); virtual bool isFormDataAppendable() const; virtual bool appendFormData(FormDataList&, bool multipart) const; @@ -137,8 +138,9 @@ public: virtual String defaultValue() const; // Checked after even fallbackValue, only when the valueWithDefault function is called. virtual double valueAsDate() const; virtual void setValueAsDate(double, ExceptionCode&) const; - virtual double valueAsNumber() const; - virtual void setValueAsNumber(double, TextFieldEventBehavior, ExceptionCode&) const; + virtual double valueAsDouble() const; + virtual void setValueAsDouble(double, TextFieldEventBehavior, ExceptionCode&) const; + virtual void setValueAsDecimal(const Decimal&, TextFieldEventBehavior, ExceptionCode&) const; // Validation functions virtual String validationMessage() const; @@ -155,12 +157,12 @@ public: bool rangeOverflow(const String&) const; bool isInRange(const String&) const; bool isOutOfRange(const String&) const; - virtual double defaultValueForStepUp() const; + virtual Decimal defaultValueForStepUp() const; double minimum() const; double maximum() const; virtual bool sizeShouldIncludeDecoration(int defaultSize, int& preferredSize) const; bool stepMismatch(const String&) const; - virtual bool getAllowedValueStep(double*) const; + virtual bool getAllowedValueStep(Decimal*) const; virtual StepRange createStepRange(AnyStepHandling) const; virtual void stepUp(int, ExceptionCode&); virtual void stepUpFromRenderer(int); @@ -192,13 +194,14 @@ public: // Helpers for event handlers. virtual bool shouldSubmitImplicitly(Event*); virtual PassRefPtr<HTMLFormElement> formForSubmission() const; - virtual bool isKeyboardFocusable() const; + virtual bool isKeyboardFocusable(KeyboardEvent*) const; + virtual bool isMouseFocusable() const; virtual bool shouldUseInputMethod() const; virtual void handleFocusEvent(); virtual void handleBlurEvent(); virtual void accessKeyAction(bool sendMouseEvents); virtual bool canBeSuccessfulSubmitButton(); - + virtual void subtreeHasChanged(); // Shadow tree handling @@ -220,6 +223,7 @@ public: virtual bool rendererIsNeeded(); virtual RenderObject* createRenderer(RenderArena*, RenderStyle*) const; + virtual void addSearchResult(); virtual void attach(); virtual void detach(); virtual void minOrMaxAttributeChanged(); @@ -230,7 +234,11 @@ public: virtual bool shouldRespectAlignAttribute(); virtual FileList* files(); virtual void setFiles(PassRefPtr<FileList>); - virtual void receiveDroppedFiles(const Vector<String>&); + // Should return true if the given DragData has more than one dropped files. + virtual bool receiveDroppedFiles(const DragData*); +#if ENABLE(FILE_SYSTEM) + virtual String droppedFileSystemId(); +#endif virtual Icon* icon() const; // Should return true if the corresponding renderer for a type can display a suggested value. virtual bool canSetSuggestedValue(); @@ -260,15 +268,10 @@ public: virtual String defaultToolTip() const; // Parses the specified string for the type, and return - // the double value for the parsing result if the parsing + // the Decimal value for the parsing result if the parsing // succeeds; Returns defaultValue otherwise. This function can // return NaN or Infinity only if defaultValue is NaN or Infinity. - virtual double parseToDouble(const String&, double defaultValue) const; - - // Parses the specified string for the type as parseToDouble() does. - // In addition, it stores the number of digits after the decimal point - // into *decimalPlaces. - virtual double parseToDoubleWithDecimalPlaces(const String&, double defaultValue, unsigned* decimalPlaces) const; + virtual Decimal parseToNumber(const String&, const Decimal& defaultValue) const; // Parses the specified string for this InputType, and returns true if it // is successfully parsed. An instance pointed by the DateComponents* @@ -276,10 +279,10 @@ public: // fails. The DateComponents* parameter may be 0. virtual bool parseToDateComponents(const String&, DateComponents*) const; - // Create a string representation of the specified double value for the + // Create a string representation of the specified Decimal value for the // input type. If NaN or Infinity is specified, this returns an empty // string. This should not be called for types without valueAsNumber. - virtual String serialize(double) const; + virtual String serialize(const Decimal&) const; virtual bool supportsIndeterminateAppearance() const; @@ -293,10 +296,11 @@ protected: HTMLInputElement* element() const { return m_element; } void dispatchSimulatedClickIfActive(KeyboardEvent*) const; Chrome* chrome() const; + Decimal parseToNumberOrNaN(const String&) const; private: // Helper for stepUp()/stepDown(). Adds step value * count to the current value. - void applyStep(double count, AnyStepHandling, TextFieldEventBehavior, ExceptionCode&); + void applyStep(int count, AnyStepHandling, TextFieldEventBehavior, ExceptionCode&); // Raw pointer because the HTMLInputElement object owns this InputType object. HTMLInputElement* m_element; diff --git a/Source/WebCore/html/LabelsNodeList.cpp b/Source/WebCore/html/LabelsNodeList.cpp index 5554c1232..fb3442be6 100644 --- a/Source/WebCore/html/LabelsNodeList.cpp +++ b/Source/WebCore/html/LabelsNodeList.cpp @@ -32,8 +32,9 @@ namespace WebCore { using namespace HTMLNames; -LabelsNodeList::LabelsNodeList(Node* forNode ) - : DynamicSubtreeNodeList(forNode->document()) , m_forNode(forNode) +LabelsNodeList::LabelsNodeList(Node* forNode) + : DynamicSubtreeNodeList(forNode, RootedAtDocument) + , m_forNode(forNode) { m_forNode->document()->registerDynamicSubtreeNodeList(this); } diff --git a/Source/WebCore/html/MediaController.cpp b/Source/WebCore/html/MediaController.cpp index 806af55b9..bba6f0310 100644 --- a/Source/WebCore/html/MediaController.cpp +++ b/Source/WebCore/html/MediaController.cpp @@ -142,8 +142,9 @@ float MediaController::currentTime() const { if (m_mediaElements.isEmpty()) return 0; - - return m_clock->currentTime(); + + // Some clocks may return times outside the range of [0..duration]. + return max(0.0f, min(duration(), m_clock->currentTime())); } void MediaController::setCurrentTime(float time, ExceptionCode& code) diff --git a/Source/WebCore/html/MediaDocument.cpp b/Source/WebCore/html/MediaDocument.cpp index 58ff49633..e5194542a 100644 --- a/Source/WebCore/html/MediaDocument.cpp +++ b/Source/WebCore/html/MediaDocument.cpp @@ -166,6 +166,7 @@ void MediaDocument::defaultEventHandler(Event* event) if (!targetNode) return; +#if !PLATFORM(CHROMIUM) if (HTMLVideoElement* video = ancestorVideoElement(targetNode)) { if (event->type() == eventNames().clickEvent) { if (!video->canPlay()) { @@ -179,7 +180,8 @@ void MediaDocument::defaultEventHandler(Event* event) } } } - +#endif + if (event->type() == eventNames().keydownEvent && event->isKeyboardEvent()) { HTMLVideoElement* video = descendentVideoElement(targetNode); if (!video) diff --git a/Source/WebCore/html/MediaFragmentURIParser.cpp b/Source/WebCore/html/MediaFragmentURIParser.cpp index 9842f60f7..de786ffd4 100644 --- a/Source/WebCore/html/MediaFragmentURIParser.cpp +++ b/Source/WebCore/html/MediaFragmentURIParser.cpp @@ -30,6 +30,7 @@ #include "MediaFragmentURIParser.h" #include "HTMLElement.h" +#include "MediaPlayer.h" #include "ProcessingInstruction.h" #include "SegmentedString.h" #include "Text.h" @@ -41,7 +42,6 @@ namespace WebCore { const int secondsPerHour = 3600; const int secondsPerMinute = 60; -const double invalidMediaTime = -1; const unsigned nptIdentiferLength = 4; // "npt:" static String collectDigits(const LChar* input, unsigned length, unsigned& position) @@ -72,21 +72,21 @@ static String collectFraction(const LChar* input, unsigned length, unsigned& pos double MediaFragmentURIParser::invalidTimeValue() { - return invalidMediaTime; + return MediaPlayer::invalidTime(); } MediaFragmentURIParser::MediaFragmentURIParser(const KURL& url) : m_url(url) , m_timeFormat(None) - , m_startTime(invalidMediaTime) - , m_endTime(invalidMediaTime) + , m_startTime(MediaPlayer::invalidTime()) + , m_endTime(MediaPlayer::invalidTime()) { } double MediaFragmentURIParser::startTime() { if (!m_url.isValid()) - return invalidMediaTime; + return MediaPlayer::invalidTime(); if (m_timeFormat == None) parseTimeFragment(); return m_startTime; @@ -95,7 +95,7 @@ double MediaFragmentURIParser::startTime() double MediaFragmentURIParser::endTime() { if (!m_url.isValid()) - return invalidMediaTime; + return MediaPlayer::invalidTime(); if (m_timeFormat == None) parseTimeFragment(); return m_endTime; @@ -184,8 +184,8 @@ void MediaFragmentURIParser::parseTimeFragment() // in the same format. The format is specified by name, followed by a colon (:), with npt: being // the default. - double start = invalidMediaTime; - double end = invalidMediaTime; + double start = MediaPlayer::invalidTime(); + double end = MediaPlayer::invalidTime(); if (parseNPTFragment(fragment.second.characters8(), fragment.second.length(), start, end)) { m_startTime = start; m_endTime = end; diff --git a/Source/WebCore/html/MonthInputType.cpp b/Source/WebCore/html/MonthInputType.cpp index ebde3b205..c9bebb135 100644 --- a/Source/WebCore/html/MonthInputType.cpp +++ b/Source/WebCore/html/MonthInputType.cpp @@ -45,9 +45,9 @@ namespace WebCore { using namespace HTMLNames; -static const double monthDefaultStep = 1.0; -static const double monthDefaultStepBase = 0.0; -static const double monthStepScaleFactor = 1.0; +static const int monthDefaultStep = 1; +static const int monthDefaultStepBase = 0; +static const int monthStepScaleFactor = 1; PassOwnPtr<InputType> MonthInputType::create(HTMLInputElement* element) { @@ -82,7 +82,7 @@ String MonthInputType::serializeWithMilliseconds(double value) const return serializeWithComponents(date); } -double MonthInputType::defaultValueForStepUp() const +Decimal MonthInputType::defaultValueForStepUp() const { double current = currentTimeMS(); double utcOffset = calculateUTCOffset(); @@ -94,28 +94,28 @@ double MonthInputType::defaultValueForStepUp() const date.setMillisecondsSinceEpochForMonth(current); double months = date.monthsSinceEpoch(); ASSERT(isfinite(months)); - return months; + return Decimal::fromDouble(months); } StepRange MonthInputType::createStepRange(AnyStepHandling anyStepHandling) const { DEFINE_STATIC_LOCAL(const StepRange::StepDescription, stepDescription, (monthDefaultStep, monthDefaultStepBase, monthStepScaleFactor, StepRange::ParsedStepValueShouldBeInteger)); - double stepBase = parseToDouble(element()->fastGetAttribute(minAttr), DateComponents::minimumMonth()); - double minimum = parseToDouble(element()->fastGetAttribute(minAttr), DateComponents::minimumMonth()); - double maximum = parseToDouble(element()->fastGetAttribute(maxAttr), DateComponents::maximumMonth()); - StepRange::DoubleWithDecimalPlacesOrMissing step = StepRange::parseStep(anyStepHandling, stepDescription, element()->fastGetAttribute(stepAttr)); + const Decimal stepBase = parseToNumber(element()->fastGetAttribute(minAttr), Decimal::fromDouble(DateComponents::minimumMonth())); + const Decimal minimum = parseToNumber(element()->fastGetAttribute(minAttr), Decimal::fromDouble(DateComponents::minimumMonth())); + const Decimal maximum = parseToNumber(element()->fastGetAttribute(maxAttr), Decimal::fromDouble(DateComponents::maximumMonth())); + const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element()->fastGetAttribute(stepAttr)); return StepRange(stepBase, minimum, maximum, step, stepDescription); } -double MonthInputType::parseToDouble(const String& src, double defaultValue) const +Decimal MonthInputType::parseToNumber(const String& src, const Decimal& defaultValue) const { DateComponents date; if (!parseToDateComponents(src, &date)) return defaultValue; double months = date.monthsSinceEpoch(); ASSERT(isfinite(months)); - return months; + return Decimal::fromDouble(months); } bool MonthInputType::parseToDateComponentsInternal(const UChar* characters, unsigned length, DateComponents* out) const diff --git a/Source/WebCore/html/MonthInputType.h b/Source/WebCore/html/MonthInputType.h index d07b01bcb..8f24fa785 100644 --- a/Source/WebCore/html/MonthInputType.h +++ b/Source/WebCore/html/MonthInputType.h @@ -47,8 +47,8 @@ private: virtual DateComponents::Type dateType() const OVERRIDE; virtual double valueAsDate() const OVERRIDE; virtual String serializeWithMilliseconds(double) const OVERRIDE; - virtual double parseToDouble(const String&, double) const OVERRIDE; - virtual double defaultValueForStepUp() const OVERRIDE; + virtual Decimal parseToNumber(const String&, const Decimal&) const OVERRIDE; + virtual Decimal defaultValueForStepUp() const OVERRIDE; virtual StepRange createStepRange(AnyStepHandling) const OVERRIDE; virtual bool parseToDateComponentsInternal(const UChar*, unsigned length, DateComponents*) const OVERRIDE; virtual bool setMillisecondToDateComponents(double, DateComponents*) const OVERRIDE; diff --git a/Source/WebCore/html/NumberInputType.cpp b/Source/WebCore/html/NumberInputType.cpp index cfdc7d497..eb157f10c 100644 --- a/Source/WebCore/html/NumberInputType.cpp +++ b/Source/WebCore/html/NumberInputType.cpp @@ -50,23 +50,48 @@ namespace WebCore { using namespace HTMLNames; using namespace std; -static const double numberDefaultStep = 1.0; -static const double numberDefaultStepBase = 0.0; -static const double numberStepScaleFactor = 1.0; +static const int numberDefaultStep = 1; +static const int numberDefaultStepBase = 0; +static const int numberStepScaleFactor = 1; -static unsigned lengthBeforeDecimalPoint(double value) +struct RealNumberRenderSize { - // If value is negative, '-' should be counted. + unsigned sizeBeforeDecimalPoint; + unsigned sizeAfteDecimalPoint; - double absoluteValue = fabs(value); - if (absoluteValue < 1) - return value < 0 ? 2 : 1; + RealNumberRenderSize(unsigned before, unsigned after) + : sizeBeforeDecimalPoint(before) + , sizeAfteDecimalPoint(after) + { + } + + RealNumberRenderSize max(const RealNumberRenderSize& other) const + { + return RealNumberRenderSize( + std::max(sizeBeforeDecimalPoint, other.sizeBeforeDecimalPoint), + std::max(sizeAfteDecimalPoint, other.sizeAfteDecimalPoint)); + } +}; - unsigned length = static_cast<unsigned>(log10(floor(absoluteValue))) + 1; - if (value < 0) - length += 1; +static RealNumberRenderSize calculateRenderSize(const Decimal& value) +{ + ASSERT(value.isFinite()); + const unsigned sizeOfDigits = String::number(value.value().coefficient()).length(); + const unsigned sizeOfSign = value.isNegative() ? 1 : 0; + const int exponent = value.exponent(); + if (exponent >= 0) + return RealNumberRenderSize(sizeOfSign + sizeOfDigits, 0); + + const int sizeBeforeDecimalPoint = exponent + sizeOfDigits; + if (sizeBeforeDecimalPoint > 0) { + // In case of "123.456" + return RealNumberRenderSize(sizeOfSign + sizeBeforeDecimalPoint, sizeOfDigits - sizeBeforeDecimalPoint); + } - return length; + // In case of "0.00012345" + const unsigned sizeOfZero = 1; + const unsigned numberOfZeroAfterDecimalPoint = -sizeBeforeDecimalPoint; + return RealNumberRenderSize(sizeOfSign + sizeOfZero , numberOfZeroAfterDecimalPoint + sizeOfDigits); } PassOwnPtr<InputType> NumberInputType::create(HTMLInputElement* element) @@ -79,22 +104,39 @@ const AtomicString& NumberInputType::formControlType() const return InputTypeNames::number(); } -double NumberInputType::valueAsNumber() const +double NumberInputType::valueAsDouble() const { - return parseToDouble(element()->value(), numeric_limits<double>::quiet_NaN()); + return parseToDoubleForNumberType(element()->value()); +} + +void NumberInputType::setValueAsDouble(double newValue, TextFieldEventBehavior eventBehavior, ExceptionCode& ec) const +{ + // FIXME: We should use numeric_limits<double>::max for number input type. + const double floatMax = numeric_limits<float>::max(); + if (newValue < -floatMax) { + ec = INVALID_STATE_ERR; + return; + } + if (newValue > floatMax) { + ec = INVALID_STATE_ERR; + return; + } + element()->setValue(serializeForNumberType(newValue), eventBehavior); } -void NumberInputType::setValueAsNumber(double newValue, TextFieldEventBehavior eventBehavior, ExceptionCode& ec) const +void NumberInputType::setValueAsDecimal(const Decimal& newValue, TextFieldEventBehavior eventBehavior, ExceptionCode& ec) const { - if (newValue < -numeric_limits<float>::max()) { + // FIXME: We should use numeric_limits<double>::max for number input type. + const Decimal floatMax = Decimal::fromDouble(numeric_limits<float>::max()); + if (newValue < -floatMax) { ec = INVALID_STATE_ERR; return; } - if (newValue > numeric_limits<float>::max()) { + if (newValue > floatMax) { ec = INVALID_STATE_ERR; return; } - element()->setValue(serialize(newValue), eventBehavior); + element()->setValue(serializeForNumberType(newValue), eventBehavior); } bool NumberInputType::typeMismatchFor(const String& value) const @@ -111,14 +153,12 @@ bool NumberInputType::typeMismatch() const StepRange NumberInputType::createStepRange(AnyStepHandling anyStepHandling) const { DEFINE_STATIC_LOCAL(const StepRange::StepDescription, stepDescription, (numberDefaultStep, numberDefaultStepBase, numberStepScaleFactor)); - - unsigned stepBaseDecimalPlaces; - double stepBaseValue = parseToDoubleWithDecimalPlaces(element()->fastGetAttribute(minAttr), numberDefaultStepBase, &stepBaseDecimalPlaces); - StepRange::DoubleWithDecimalPlaces stepBase(stepBaseValue, min(stepBaseDecimalPlaces, 16u)); - double minimum = parseToDouble(element()->fastGetAttribute(minAttr), -numeric_limits<float>::max()); - double maximum = parseToDouble(element()->fastGetAttribute(maxAttr), numeric_limits<float>::max()); - - StepRange::DoubleWithDecimalPlacesOrMissing step = StepRange::parseStep(anyStepHandling, stepDescription, element()->fastGetAttribute(stepAttr)); + const Decimal stepBase = parseToDecimalForNumberType(element()->fastGetAttribute(minAttr), numberDefaultStepBase); + // FIXME: We should use numeric_limits<double>::max for number input type. + const Decimal floatMax = Decimal::fromDouble(numeric_limits<float>::max()); + const Decimal minimum = parseToNumber(element()->fastGetAttribute(minAttr), -floatMax); + const Decimal maximum = parseToNumber(element()->fastGetAttribute(maxAttr), floatMax); + const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element()->fastGetAttribute(stepAttr)); return StepRange(stepBase, minimum, maximum, step, stepDescription); } @@ -126,46 +166,25 @@ bool NumberInputType::sizeShouldIncludeDecoration(int defaultSize, int& preferre { preferredSize = defaultSize; - unsigned minValueDecimalPlaces; - String minValue = element()->fastGetAttribute(minAttr); - double minValueDouble = parseToDoubleForNumberTypeWithDecimalPlaces(minValue, &minValueDecimalPlaces); - if (!isfinite(minValueDouble)) + const String stepString = element()->fastGetAttribute(stepAttr); + if (equalIgnoringCase(stepString, "any")) return false; - unsigned maxValueDecimalPlaces; - String maxValue = element()->fastGetAttribute(maxAttr); - double maxValueDouble = parseToDoubleForNumberTypeWithDecimalPlaces(maxValue, &maxValueDecimalPlaces); - if (!isfinite(maxValueDouble)) + const Decimal minimum = parseToDecimalForNumberType(element()->fastGetAttribute(minAttr)); + if (!minimum.isFinite()) return false; - if (maxValueDouble < minValueDouble) { - maxValueDouble = minValueDouble; - maxValueDecimalPlaces = minValueDecimalPlaces; - } - - String stepValue = element()->fastGetAttribute(stepAttr); - if (equalIgnoringCase(stepValue, "any")) + const Decimal maximum = parseToDecimalForNumberType(element()->fastGetAttribute(maxAttr)); + if (!maximum.isFinite()) return false; - unsigned stepValueDecimalPlaces; - double stepValueDouble = parseToDoubleForNumberTypeWithDecimalPlaces(stepValue, &stepValueDecimalPlaces); - if (!isfinite(stepValueDouble)) { - stepValueDouble = 1; - stepValueDecimalPlaces = 0; - } - unsigned length = lengthBeforeDecimalPoint(minValueDouble); - length = max(length, lengthBeforeDecimalPoint(maxValueDouble)); - length = max(length, lengthBeforeDecimalPoint(stepValueDouble)); + const Decimal step = parseToDecimalForNumberType(stepString, 1); + ASSERT(step.isFinite()); - unsigned lengthAfterDecimalPoint = minValueDecimalPlaces; - lengthAfterDecimalPoint = max(lengthAfterDecimalPoint, maxValueDecimalPlaces); - lengthAfterDecimalPoint = max(lengthAfterDecimalPoint, stepValueDecimalPlaces); + RealNumberRenderSize size = calculateRenderSize(minimum).max(calculateRenderSize(maximum).max(calculateRenderSize(step))); - // '.' should be counted if the value has decimal places. - if (lengthAfterDecimalPoint > 0) - length += lengthAfterDecimalPoint + 1; + preferredSize = size.sizeBeforeDecimalPoint + size.sizeAfteDecimalPoint + (size.sizeAfteDecimalPoint ? 1 : 0); - preferredSize = length; return true; } @@ -186,19 +205,14 @@ void NumberInputType::handleWheelEvent(WheelEvent* event) handleWheelEventForSpinButton(event); } -double NumberInputType::parseToDouble(const String& src, double defaultValue) const -{ - return parseToDoubleForNumberType(src, defaultValue); -} - -double NumberInputType::parseToDoubleWithDecimalPlaces(const String& src, double defaultValue, unsigned *decimalPlaces) const +Decimal NumberInputType::parseToNumber(const String& src, const Decimal& defaultValue) const { - return parseToDoubleForNumberTypeWithDecimalPlaces(src, decimalPlaces, defaultValue); + return parseToDecimalForNumberType(src, defaultValue); } -String NumberInputType::serialize(double value) const +String NumberInputType::serialize(const Decimal& value) const { - if (!isfinite(value)) + if (!value.isFinite()) return String(); return serializeForNumberType(value); } @@ -252,7 +266,7 @@ String NumberInputType::convertFromVisibleValue(const String& visibleValue) cons bool NumberInputType::isAcceptableValue(const String& proposedValue) { String standardValue = convertFromVisibleValue(proposedValue); - return standardValue.isEmpty() || parseToDoubleForNumberType(standardValue, 0); + return standardValue.isEmpty() || isfinite(parseToDoubleForNumberType(standardValue)); } String NumberInputType::sanitizeValue(const String& proposedValue) const diff --git a/Source/WebCore/html/NumberInputType.h b/Source/WebCore/html/NumberInputType.h index ce4177bfc..fe16e5fa0 100644 --- a/Source/WebCore/html/NumberInputType.h +++ b/Source/WebCore/html/NumberInputType.h @@ -42,8 +42,9 @@ public: private: NumberInputType(HTMLInputElement* element) : TextFieldInputType(element) { } virtual const AtomicString& formControlType() const OVERRIDE; - virtual double valueAsNumber() const OVERRIDE; - virtual void setValueAsNumber(double, TextFieldEventBehavior, ExceptionCode&) const OVERRIDE; + virtual double valueAsDouble() const OVERRIDE; + virtual void setValueAsDouble(double, TextFieldEventBehavior, ExceptionCode&) const OVERRIDE; + virtual void setValueAsDecimal(const Decimal&, TextFieldEventBehavior, ExceptionCode&) const OVERRIDE; virtual bool typeMismatchFor(const String&) const OVERRIDE; virtual bool typeMismatch() const OVERRIDE; virtual bool sizeShouldIncludeDecoration(int defaultSize, int& preferredSize) const OVERRIDE; @@ -51,9 +52,8 @@ private: virtual StepRange createStepRange(AnyStepHandling) const OVERRIDE; virtual void handleKeydownEvent(KeyboardEvent*) OVERRIDE; virtual void handleWheelEvent(WheelEvent*) OVERRIDE; - virtual double parseToDouble(const String&, double) const OVERRIDE; - virtual double parseToDoubleWithDecimalPlaces(const String&, double, unsigned*) const OVERRIDE; - virtual String serialize(double) const OVERRIDE; + virtual Decimal parseToNumber(const String&, const Decimal&) const OVERRIDE; + virtual String serialize(const Decimal&) const OVERRIDE; virtual void handleBlurEvent() OVERRIDE; virtual String localizeValue(const String&) const OVERRIDE; virtual String visibleValue() const OVERRIDE; diff --git a/Source/WebCore/html/PasswordInputType.cpp b/Source/WebCore/html/PasswordInputType.cpp index 88bb842b6..10c2d9f9e 100644 --- a/Source/WebCore/html/PasswordInputType.cpp +++ b/Source/WebCore/html/PasswordInputType.cpp @@ -32,6 +32,7 @@ #include "config.h" #include "PasswordInputType.h" +#include "FormController.h" #include "HTMLInputElement.h" #include <wtf/Assertions.h> #include <wtf/PassOwnPtr.h> @@ -48,13 +49,13 @@ const AtomicString& PasswordInputType::formControlType() const return InputTypeNames::password(); } -bool PasswordInputType::saveFormControlState(String&) const +FormControlState PasswordInputType::saveFormControlState() const { // Should never save/restore password fields. - return false; + return FormControlState(); } -void PasswordInputType::restoreFormControlState(const String&) +void PasswordInputType::restoreFormControlState(const FormControlState&) { // Should never save/restore password fields. ASSERT_NOT_REACHED(); diff --git a/Source/WebCore/html/PasswordInputType.h b/Source/WebCore/html/PasswordInputType.h index 5267dde63..83012cf9c 100644 --- a/Source/WebCore/html/PasswordInputType.h +++ b/Source/WebCore/html/PasswordInputType.h @@ -42,8 +42,8 @@ public: private: PasswordInputType(HTMLInputElement* element) : BaseTextInputType(element) { } virtual const AtomicString& formControlType() const OVERRIDE; - virtual bool saveFormControlState(String&) const OVERRIDE; - virtual void restoreFormControlState(const String&) OVERRIDE; + virtual FormControlState saveFormControlState() const OVERRIDE; + virtual void restoreFormControlState(const FormControlState&) OVERRIDE; virtual bool shouldUseInputMethod() const OVERRIDE; virtual bool shouldResetOnDocumentActivation() OVERRIDE; virtual bool shouldRespectListAttribute() OVERRIDE; diff --git a/Source/WebCore/html/RadioInputType.cpp b/Source/WebCore/html/RadioInputType.cpp index 155be891d..2c16b1a16 100644 --- a/Source/WebCore/html/RadioInputType.cpp +++ b/Source/WebCore/html/RadioInputType.cpp @@ -114,8 +114,11 @@ void RadioInputType::handleKeyupEvent(KeyboardEvent* event) dispatchSimulatedClickIfActive(event); } -bool RadioInputType::isKeyboardFocusable() const +bool RadioInputType::isKeyboardFocusable(KeyboardEvent* event) const { + if (!InputType::isKeyboardFocusable(event)) + return false; + // When using Spatial Navigation, every radio button should be focusable. if (isSpatialNavigationEnabled(element()->document()->frame())) return true; diff --git a/Source/WebCore/html/RadioInputType.h b/Source/WebCore/html/RadioInputType.h index 7790b504c..24e27bc57 100644 --- a/Source/WebCore/html/RadioInputType.h +++ b/Source/WebCore/html/RadioInputType.h @@ -47,7 +47,7 @@ private: virtual void handleClickEvent(MouseEvent*) OVERRIDE; virtual void handleKeydownEvent(KeyboardEvent*) OVERRIDE; virtual void handleKeyupEvent(KeyboardEvent*) OVERRIDE; - virtual bool isKeyboardFocusable() const OVERRIDE; + virtual bool isKeyboardFocusable(KeyboardEvent*) const OVERRIDE; virtual bool shouldSendChangeEventAfterCheckedChanged() OVERRIDE; virtual PassOwnPtr<ClickHandlingState> willDispatchClick() OVERRIDE; virtual void didDispatchClick(Event*, const ClickHandlingState&) OVERRIDE; diff --git a/Source/WebCore/html/RadioNodeList.cpp b/Source/WebCore/html/RadioNodeList.cpp index 40929bca3..3c3a618ab 100644 --- a/Source/WebCore/html/RadioNodeList.cpp +++ b/Source/WebCore/html/RadioNodeList.cpp @@ -36,8 +36,8 @@ namespace WebCore { using namespace HTMLNames; -RadioNodeList::RadioNodeList(const AtomicString& name, Element* baseElement) - : DynamicSubtreeNodeList(baseElement->hasTagName(formTag) ? static_cast<Node*>(baseElement->document()) : baseElement) +RadioNodeList::RadioNodeList(Element* baseElement, const AtomicString& name) + : DynamicSubtreeNodeList(baseElement, baseElement->hasTagName(formTag) ? RootedAtDocument : RootedAtNode) , m_name(name) , m_baseElement(baseElement) { diff --git a/Source/WebCore/html/RadioNodeList.h b/Source/WebCore/html/RadioNodeList.h index 7782e4802..46065b383 100644 --- a/Source/WebCore/html/RadioNodeList.h +++ b/Source/WebCore/html/RadioNodeList.h @@ -36,9 +36,9 @@ namespace WebCore { class RadioNodeList : public DynamicSubtreeNodeList { public: - static PassRefPtr<RadioNodeList> create(const AtomicString& name, Element* baseElement) + static PassRefPtr<RadioNodeList> create(Element* baseElement, const AtomicString& name) { - return adoptRef(new RadioNodeList(name, baseElement)); + return adoptRef(new RadioNodeList(baseElement, name)); } ~RadioNodeList(); @@ -50,7 +50,7 @@ protected: virtual bool nodeMatches(Element*) const; private: - RadioNodeList(const AtomicString& name, Element*); + RadioNodeList(Element*, const AtomicString& name); bool checkElementMatchesRadioNodeListFilter(Element*) const; AtomicString m_name; diff --git a/Source/WebCore/html/RangeInputType.cpp b/Source/WebCore/html/RangeInputType.cpp index b417dc320..fd11cbaa7 100644 --- a/Source/WebCore/html/RangeInputType.cpp +++ b/Source/WebCore/html/RangeInputType.cpp @@ -54,11 +54,16 @@ namespace WebCore { using namespace HTMLNames; using namespace std; -static const double rangeDefaultMinimum = 0.0; -static const double rangeDefaultMaximum = 100.0; -static const double rangeDefaultStep = 1.0; -static const double rangeDefaultStepBase = 0.0; -static const double rangeStepScaleFactor = 1.0; +static const int rangeDefaultMinimum = 0; +static const int rangeDefaultMaximum = 100; +static const int rangeDefaultStep = 1; +static const int rangeDefaultStepBase = 0; +static const int rangeStepScaleFactor = 1; + +static Decimal ensureMaximum(const Decimal& proposedValue, const Decimal& minimum, const Decimal& fallbackValue) +{ + return proposedValue >= minimum ? proposedValue : std::max(minimum, fallbackValue); +} PassOwnPtr<InputType> RangeInputType::create(HTMLInputElement* element) { @@ -75,12 +80,12 @@ const AtomicString& RangeInputType::formControlType() const return InputTypeNames::range(); } -double RangeInputType::valueAsNumber() const +double RangeInputType::valueAsDouble() const { - return parseToDouble(element()->value(), numeric_limits<double>::quiet_NaN()); + return parseToDoubleForNumberType(element()->value()); } -void RangeInputType::setValueAsNumber(double newValue, TextFieldEventBehavior eventBehavior, ExceptionCode&) const +void RangeInputType::setValueAsDecimal(const Decimal& newValue, TextFieldEventBehavior eventBehavior, ExceptionCode&) const { element()->setValue(serialize(newValue), eventBehavior); } @@ -94,18 +99,16 @@ StepRange RangeInputType::createStepRange(AnyStepHandling anyStepHandling) const { DEFINE_STATIC_LOCAL(const StepRange::StepDescription, stepDescription, (rangeDefaultStep, rangeDefaultStepBase, rangeStepScaleFactor)); - double minimum = parseToDouble(element()->fastGetAttribute(minAttr), rangeDefaultMinimum); - double maximum = parseToDouble(element()->fastGetAttribute(maxAttr), rangeDefaultMaximum); - if (maximum < minimum) - maximum = max(minimum, rangeDefaultMaximum); + const Decimal minimum = parseToNumber(element()->fastGetAttribute(minAttr), rangeDefaultMinimum); + const Decimal maximum = ensureMaximum(parseToNumber(element()->fastGetAttribute(maxAttr), rangeDefaultMaximum), minimum, rangeDefaultMaximum); const AtomicString& precisionValue = element()->fastGetAttribute(precisionAttr); if (!precisionValue.isNull()) { - StepRange::DoubleWithDecimalPlacesOrMissing step(1, !equalIgnoringCase(precisionValue, "float")); + const Decimal step = equalIgnoringCase(precisionValue, "float") ? Decimal::nan() : 1; return StepRange(minimum, minimum, maximum, step, stepDescription); } - StepRange::DoubleWithDecimalPlacesOrMissing step = StepRange::parseStep(anyStepHandling, stepDescription, element()->fastGetAttribute(stepAttr)); + const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element()->fastGetAttribute(stepAttr)); return StepRange(minimum, minimum, maximum, step, stepDescription); } @@ -138,25 +141,16 @@ void RangeInputType::handleKeydownEvent(KeyboardEvent* event) const String& key = event->keyIdentifier(); - double current = parseToDouble(element()->value(), numeric_limits<double>::quiet_NaN()); - ASSERT(isfinite(current)); + const Decimal current = parseToNumberOrNaN(element()->value()); + ASSERT(current.isFinite()); StepRange stepRange(createStepRange(RejectAny)); - double step, bigStep; - if (equalIgnoringCase(element()->fastGetAttribute(stepAttr), "any")) { - // FIXME: We can't use stepUp() for the step value "any". So, we increase - // or decrease the value by 1/100 of the value range. Is it reasonable? - step = (stepRange.maximum() - stepRange.minimum()) / 100; - bigStep = step * 10; - } else { - if (!element()->getAllowedValueStep(&step)) - ASSERT_NOT_REACHED(); - - bigStep = (stepRange.maximum() - stepRange.minimum()) / 10; - if (bigStep < step) - bigStep = step; - } + + // FIXME: We can't use stepUp() for the step value "any". So, we increase + // or decrease the value by 1/100 of the value range. Is it reasonable? + const Decimal step = equalIgnoringCase(element()->fastGetAttribute(stepAttr), "any") ? (stepRange.maximum() - stepRange.minimum()) / 100 : stepRange.step(); + const Decimal bigStep = max((stepRange.maximum() - stepRange.minimum()) / 10, step); bool isVertical = false; if (element()->renderer()) { @@ -164,7 +158,7 @@ void RangeInputType::handleKeydownEvent(KeyboardEvent* event) isVertical = part == SliderVerticalPart || part == MediaVolumeSliderPart; } - double newValue; + Decimal newValue; if (key == "Up") newValue = current + step; else if (key == "Down") @@ -189,7 +183,7 @@ void RangeInputType::handleKeydownEvent(KeyboardEvent* event) if (newValue != current) { ExceptionCode ec; TextFieldEventBehavior eventBehavior = DispatchChangeEvent; - setValueAsNumber(newValue, eventBehavior, ec); + setValueAsDecimal(newValue, eventBehavior, ec); if (AXObjectCache::accessibilityEnabled()) element()->document()->axObjectCache()->postNotification(element()->renderer(), AXObjectCache::AXValueChanged, true); @@ -219,14 +213,14 @@ RenderObject* RangeInputType::createRenderer(RenderArena* arena, RenderStyle*) c return new (arena) RenderSlider(element()); } -double RangeInputType::parseToDouble(const String& src, double defaultValue) const +Decimal RangeInputType::parseToNumber(const String& src, const Decimal& defaultValue) const { - return parseToDoubleForNumberType(src, defaultValue); + return parseToDecimalForNumberType(src, defaultValue); } -String RangeInputType::serialize(double value) const +String RangeInputType::serialize(const Decimal& value) const { - if (!isfinite(value)) + if (!value.isFinite()) return String(); return serializeForNumberType(value); } @@ -269,8 +263,8 @@ String RangeInputType::fallbackValue() const String RangeInputType::sanitizeValue(const String& proposedValue) const { StepRange stepRange(createStepRange(RejectAny)); - double proposedDoubleValue = parseToDouble(proposedValue, stepRange.defaultValue()); - return serializeForNumberType(stepRange.clampValue(proposedDoubleValue)); + const Decimal proposedNumericValue = parseToNumber(proposedValue, stepRange.defaultValue()); + return serializeForNumberType(stepRange.clampValue(proposedNumericValue)); } bool RangeInputType::shouldRespectListAttribute() diff --git a/Source/WebCore/html/RangeInputType.h b/Source/WebCore/html/RangeInputType.h index 45fb4afa3..ff8f4028f 100644 --- a/Source/WebCore/html/RangeInputType.h +++ b/Source/WebCore/html/RangeInputType.h @@ -45,8 +45,8 @@ private: RangeInputType(HTMLInputElement* element) : InputType(element) { } virtual bool isRangeControl() const OVERRIDE; virtual const AtomicString& formControlType() const OVERRIDE; - virtual double valueAsNumber() const OVERRIDE; - virtual void setValueAsNumber(double, TextFieldEventBehavior, ExceptionCode&) const OVERRIDE; + virtual double valueAsDouble() const OVERRIDE; + virtual void setValueAsDecimal(const Decimal&, TextFieldEventBehavior, ExceptionCode&) const OVERRIDE; virtual bool supportsRequired() const OVERRIDE; virtual StepRange createStepRange(AnyStepHandling) const OVERRIDE; virtual bool isSteppable() const OVERRIDE; @@ -54,8 +54,8 @@ private: virtual void handleKeydownEvent(KeyboardEvent*) OVERRIDE; virtual RenderObject* createRenderer(RenderArena*, RenderStyle*) const OVERRIDE; virtual void createShadowSubtree() OVERRIDE; - virtual double parseToDouble(const String&, double) const OVERRIDE; - virtual String serialize(double) const OVERRIDE; + virtual Decimal parseToNumber(const String&, const Decimal&) const OVERRIDE; + virtual String serialize(const Decimal&) const OVERRIDE; virtual void accessKeyAction(bool sendMouseEvents) OVERRIDE; virtual void minOrMaxAttributeChanged() OVERRIDE; virtual void setValue(const String&, bool valueChanged, TextFieldEventBehavior) OVERRIDE; diff --git a/Source/WebCore/html/SearchInputType.cpp b/Source/WebCore/html/SearchInputType.cpp index 6f500daf3..958878ced 100644 --- a/Source/WebCore/html/SearchInputType.cpp +++ b/Source/WebCore/html/SearchInputType.cpp @@ -32,14 +32,17 @@ #include "SearchInputType.h" #include "HTMLInputElement.h" +#include "HTMLNames.h" #include "KeyboardEvent.h" -#include "RenderTextControlSingleLine.h" +#include "RenderSearchField.h" #include "ShadowRoot.h" #include "TextControlInnerElements.h" #include <wtf/PassOwnPtr.h> namespace WebCore { +using namespace HTMLNames; + inline SearchInputType::SearchInputType(HTMLInputElement* element) : BaseTextInputType(element) , m_searchEventTimer(this, &SearchInputType::searchEventTimerFired) @@ -51,6 +54,17 @@ PassOwnPtr<InputType> SearchInputType::create(HTMLInputElement* element) return adoptPtr(new SearchInputType(element)); } +void SearchInputType::addSearchResult() +{ + if (RenderObject* renderer = element()->renderer()) + toRenderSearchField(renderer)->addSearchResult(); +} + +RenderObject* SearchInputType::createRenderer(RenderArena* arena, RenderStyle*) const +{ + return new (arena) RenderSearchField(element()); +} + const AtomicString& SearchInputType::formControlType() const { return InputTypeNames::search(); @@ -151,5 +165,19 @@ void SearchInputType::searchEventTimerFired(Timer<SearchInputType>*) element()->onSearch(); } +bool SearchInputType::searchEventsShouldBeDispatched() const +{ + return element()->hasAttribute(incrementalAttr); +} + +void SearchInputType::subtreeHasChanged() +{ + if (m_cancelButton.get()) + toRenderSearchField(element()->renderer())->updateCancelButtonVisibility(); + + // If the incremental attribute is set, then dispatch the search event + if (searchEventsShouldBeDispatched()) + startSearchEventTimer(); +} } // namespace WebCore diff --git a/Source/WebCore/html/SearchInputType.h b/Source/WebCore/html/SearchInputType.h index ab19103af..ece35a259 100644 --- a/Source/WebCore/html/SearchInputType.h +++ b/Source/WebCore/html/SearchInputType.h @@ -43,11 +43,12 @@ class SearchInputType : public BaseTextInputType { public: static PassOwnPtr<InputType> create(HTMLInputElement*); - void startSearchEventTimer(); void stopSearchEventTimer(); private: SearchInputType(HTMLInputElement*); + virtual void addSearchResult() OVERRIDE; + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*) const OVERRIDE; virtual const AtomicString& formControlType() const OVERRIDE; virtual bool shouldRespectSpeechAttribute() OVERRIDE; virtual bool isSearchField() const OVERRIDE; @@ -57,8 +58,11 @@ private: virtual HTMLElement* resultsButtonElement() const OVERRIDE; virtual HTMLElement* cancelButtonElement() const OVERRIDE; virtual void handleKeydownEvent(KeyboardEvent*) OVERRIDE; + virtual void subtreeHasChanged(); void searchEventTimerFired(Timer<SearchInputType>*); + bool searchEventsShouldBeDispatched() const; + void startSearchEventTimer(); RefPtr<HTMLElement> m_resultsButton; RefPtr<HTMLElement> m_cancelButton; diff --git a/Source/WebCore/html/StepRange.cpp b/Source/WebCore/html/StepRange.cpp index 9b9b7dcc9..c7c88fcf9 100644 --- a/Source/WebCore/html/StepRange.cpp +++ b/Source/WebCore/html/StepRange.cpp @@ -37,8 +37,6 @@ StepRange::StepRange() , m_minimum(0) , m_step(1) , m_stepBase(0) - , m_stepBaseDecimalPlaces(0) - , m_stepDecimalPlaces(0) , m_hasStep(false) { } @@ -49,126 +47,122 @@ StepRange::StepRange(const StepRange& stepRange) , m_step(stepRange.m_step) , m_stepBase(stepRange.m_stepBase) , m_stepDescription(stepRange.m_stepDescription) - , m_stepBaseDecimalPlaces(stepRange.m_stepBaseDecimalPlaces) - , m_stepDecimalPlaces(stepRange.m_stepDecimalPlaces) , m_hasStep(stepRange.m_hasStep) { } -StepRange::StepRange(const DoubleWithDecimalPlaces& stepBase, double minimum, double maximum, const DoubleWithDecimalPlacesOrMissing& step, const StepDescription& stepDescription) +StepRange::StepRange(const Decimal& stepBase, const Decimal& minimum, const Decimal& maximum, const Decimal& step, const StepDescription& stepDescription) : m_maximum(maximum) , m_minimum(minimum) - , m_step(step.value.value) - , m_stepBase(stepBase.value) + , m_step(step.isFinite() ? step : 1) + , m_stepBase(stepBase.isFinite() ? stepBase : 1) , m_stepDescription(stepDescription) - , m_stepBaseDecimalPlaces(stepBase.decimalPlaces) - , m_stepDecimalPlaces(step.value.decimalPlaces) - , m_hasStep(step.hasValue) + , m_hasStep(step.isFinite()) { - ASSERT(isfinite(m_maximum)); - ASSERT(isfinite(m_minimum)); - ASSERT(isfinite(m_step)); - ASSERT(isfinite(m_stepBase)); + ASSERT(m_maximum.isFinite()); + ASSERT(m_minimum.isFinite()); + ASSERT(m_step.isFinite()); + ASSERT(m_stepBase.isFinite()); } -double StepRange::acceptableError() const +Decimal StepRange::acceptableError() const { - return m_step / pow(2.0, FLT_MANT_DIG); + // FIXME: We should use DBL_MANT_DIG instead of FLT_MANT_DIG regarding to HTML5 specification. + DEFINE_STATIC_LOCAL(const Decimal, twoPowerOfFloatMantissaBits, (Decimal::Positive, 0, UINT64_C(1) << FLT_MANT_DIG)); + return m_step / twoPowerOfFloatMantissaBits; } -double StepRange::alignValueForStep(double currentValue, unsigned currentDecimalPlaces, double newValue) const +Decimal StepRange::alignValueForStep(const Decimal& currentValue, const Decimal& newValue) const { - if (newValue >= pow(10.0, 21.0)) + DEFINE_STATIC_LOCAL(const Decimal, tenPowerOf21, (Decimal::Positive, 21, 1)); + if (newValue >= tenPowerOf21) return newValue; - if (stepMismatch(currentValue)) { - double scale = pow(10.0, static_cast<double>(max(m_stepDecimalPlaces, currentDecimalPlaces))); - newValue = round(newValue * scale) / scale; - } else { - double scale = pow(10.0, static_cast<double>(max(m_stepDecimalPlaces, m_stepBaseDecimalPlaces))); - newValue = round((m_stepBase + round((newValue - m_stepBase) / m_step) * m_step) * scale) / scale; - } - - return newValue; + return stepMismatch(currentValue) ? newValue : roundByStep(newValue, m_stepBase); } -double StepRange::clampValue(double value) const + +Decimal StepRange::clampValue(const Decimal& value) const { - double clampedValue = max(m_minimum, min(value, m_maximum)); + const Decimal inRangeValue = max(m_minimum, min(value, m_maximum)); if (!m_hasStep) - return clampedValue; - // Rounds clampedValue to minimum + N * step. - clampedValue = m_minimum + round((clampedValue - m_minimum) / m_step) * m_step; - if (clampedValue > m_maximum) - clampedValue -= m_step; + return inRangeValue; + // Rounds inRangeValue to minimum + N * step. + const Decimal roundedValue = roundByStep(inRangeValue, m_minimum); + const Decimal clampedValue = roundedValue > m_maximum ? roundedValue - m_step : roundedValue; ASSERT(clampedValue >= m_minimum); ASSERT(clampedValue <= m_maximum); return clampedValue; } -StepRange::DoubleWithDecimalPlacesOrMissing StepRange::parseStep(AnyStepHandling anyStepHandling, const StepDescription& stepDescription, const String& stepString) +Decimal StepRange::parseStep(AnyStepHandling anyStepHandling, const StepDescription& stepDescription, const String& stepString) { if (stepString.isEmpty()) - return DoubleWithDecimalPlacesOrMissing(stepDescription.defaultValue()); + return stepDescription.defaultValue(); if (equalIgnoringCase(stepString, "any")) { switch (anyStepHandling) { case RejectAny: - return DoubleWithDecimalPlacesOrMissing(DoubleWithDecimalPlaces(1), false); + return Decimal::nan(); case AnyIsDefaultStep: - return DoubleWithDecimalPlacesOrMissing(stepDescription.defaultValue()); + return stepDescription.defaultValue(); default: ASSERT_NOT_REACHED(); } } - DoubleWithDecimalPlacesOrMissing step(0); - step.value.value = parseToDoubleForNumberTypeWithDecimalPlaces(stepString, &step.value.decimalPlaces); - if (!isfinite(step.value.value) || step.value.value <= 0.0) - return DoubleWithDecimalPlacesOrMissing(stepDescription.defaultValue()); + Decimal step = parseToDecimalForNumberType(stepString); + if (!step.isFinite() || step <= 0) + return stepDescription.defaultValue(); switch (stepDescription.stepValueShouldBe) { case StepValueShouldBeReal: - step.value.value *= stepDescription.stepScaleFactor; + step *= stepDescription.stepScaleFactor; break; case ParsedStepValueShouldBeInteger: // For date, month, and week, the parsed value should be an integer for some types. - step.value.value = max(round(step.value.value), 1.0); - step.value.value *= stepDescription.stepScaleFactor; + step = max(step.round(), Decimal(1)); + step *= stepDescription.stepScaleFactor; break; case ScaledStepValueShouldBeInteger: // For datetime, datetime-local, time, the result should be an integer. - step.value.value *= stepDescription.stepScaleFactor; - step.value.value = max(round(step.value.value), 1.0); + step *= stepDescription.stepScaleFactor; + step = max(step.round(), Decimal(1)); break; default: ASSERT_NOT_REACHED(); } - ASSERT(step.value.value > 0); + ASSERT(step > 0); return step; } -bool StepRange::stepMismatch(double doubleValue) const +Decimal StepRange::roundByStep(const Decimal& value, const Decimal& base) const +{ + return base + ((value - base) / m_step).round() * m_step; +} + +bool StepRange::stepMismatch(const Decimal& valueForCheck) const { if (!m_hasStep) return false; - if (!isfinite(doubleValue)) + if (!valueForCheck.isFinite()) return false; - doubleValue = fabs(doubleValue - m_stepBase); - if (isinf(doubleValue)) + const Decimal value = (valueForCheck - m_stepBase).abs(); + if (!value.isFinite()) return false; - // double's fractional part size is DBL_MAN_DIG-bit. If the current value + // Decimal's fractional part size is DBL_MAN_DIG-bit. If the current value // is greater than step*2^DBL_MANT_DIG, the following computation for // remainder makes no sense. - if (doubleValue / pow(2.0, DBL_MANT_DIG) > m_step) + DEFINE_STATIC_LOCAL(const Decimal, twoPowerOfDoubleMantissaBits, (Decimal::Positive, 0, UINT64_C(1) << DBL_MANT_DIG)); + if (value / twoPowerOfDoubleMantissaBits > m_step) return false; // The computation follows HTML5 4.10.7.2.10 `The step attribute' : // ... that number subtracted from the step base is not an integral multiple // of the allowed value step, the element is suffering from a step mismatch. - double remainder = fabs(doubleValue - m_step * round(doubleValue / m_step)); + const Decimal remainder = (value - m_step * (value / m_step).round()).abs(); // Accepts erros in lower fractional part which IEEE 754 single-precision // can't represent. - double computedAcceptableError = acceptableError(); + const Decimal computedAcceptableError = acceptableError(); return computedAcceptableError < remainder && remainder < (m_step - computedAcceptableError); } diff --git a/Source/WebCore/html/StepRange.h b/Source/WebCore/html/StepRange.h index 0adbfd857..63ab7561a 100644 --- a/Source/WebCore/html/StepRange.h +++ b/Source/WebCore/html/StepRange.h @@ -21,6 +21,7 @@ #ifndef StepRange_h #define StepRange_h +#include "Decimal.h" #include <wtf/Forward.h> #include <wtf/Noncopyable.h> @@ -32,28 +33,6 @@ enum AnyStepHandling { RejectAny, AnyIsDefaultStep }; class StepRange { public: - struct DoubleWithDecimalPlaces { - unsigned decimalPlaces; - double value; - - DoubleWithDecimalPlaces(double value = 0, unsigned decimalPlaces = 0) - : decimalPlaces(decimalPlaces) - , value(value) - { - } - }; - - struct DoubleWithDecimalPlacesOrMissing { - bool hasValue; - DoubleWithDecimalPlaces value; - - DoubleWithDecimalPlacesOrMissing(DoubleWithDecimalPlaces value, bool hasValue = true) - : hasValue(hasValue) - , value(value) - { - } - }; - enum StepValueShouldBe { StepValueShouldBeReal, ParsedStepValueShouldBeInteger, @@ -82,7 +61,7 @@ public: { } - double defaultValue() const + Decimal defaultValue() const { return defaultStep * stepScaleFactor; } @@ -90,29 +69,27 @@ public: StepRange(); StepRange(const StepRange&); - StepRange(const DoubleWithDecimalPlaces& stepBase, double minimum, double maximum, const DoubleWithDecimalPlacesOrMissing& step, const StepDescription&); - double acceptableError() const; - double alignValueForStep(double currentValue, unsigned currentDecimalPlaces, double newValue) const; - double clampValue(double value) const; + StepRange(const Decimal& stepBase, const Decimal& minimum, const Decimal& maximum, const Decimal& step, const StepDescription&); + Decimal acceptableError() const; + Decimal alignValueForStep(const Decimal& currentValue, const Decimal& newValue) const; + Decimal clampValue(const Decimal& value) const; bool hasStep() const { return m_hasStep; } - double maximum() const { return m_maximum; } - double minimum() const { return m_minimum; } - static DoubleWithDecimalPlacesOrMissing parseStep(AnyStepHandling, const StepDescription&, const String&); - double step() const { return m_step; } - double stepBase() const { return m_stepBase; } - unsigned stepBaseDecimalPlaces() const { return m_stepBaseDecimalPlaces; } - unsigned stepDecimalPlaces() const { return m_stepDecimalPlaces; } + Decimal maximum() const { return m_maximum; } + Decimal minimum() const { return m_minimum; } + static Decimal parseStep(AnyStepHandling, const StepDescription&, const String&); + Decimal step() const { return m_step; } + Decimal stepBase() const { return m_stepBase; } int stepScaleFactor() const { return m_stepDescription.stepScaleFactor; } - bool stepMismatch(double) const; + bool stepMismatch(const Decimal&) const; // Clamp the middle value according to the step - double defaultValue() const + Decimal defaultValue() const { return clampValue((m_minimum + m_maximum) / 2); } // Map value into 0-1 range - double proportionFromValue(double value) const + Decimal proportionFromValue(const Decimal& value) const { if (m_minimum == m_maximum) return 0; @@ -121,21 +98,20 @@ public: } // Map from 0-1 range to value - double valueFromProportion(double proportion) const + Decimal valueFromProportion(const Decimal& proportion) const { return m_minimum + proportion * (m_maximum - m_minimum); } private: StepRange& operator =(const StepRange&); + Decimal roundByStep(const Decimal& value, const Decimal& base) const; - const double m_maximum; // maximum must be >= minimum. - const double m_minimum; - const double m_step; - const double m_stepBase; + const Decimal m_maximum; // maximum must be >= minimum. + const Decimal m_minimum; + const Decimal m_step; + const Decimal m_stepBase; const StepDescription m_stepDescription; - const unsigned m_stepBaseDecimalPlaces; - const unsigned m_stepDecimalPlaces; const bool m_hasStep; }; diff --git a/Source/WebCore/html/TextFieldInputType.cpp b/Source/WebCore/html/TextFieldInputType.cpp index 8189144a9..af53255bb 100644 --- a/Source/WebCore/html/TextFieldInputType.cpp +++ b/Source/WebCore/html/TextFieldInputType.cpp @@ -65,6 +65,16 @@ TextFieldInputType::~TextFieldInputType() { } +bool TextFieldInputType::isKeyboardFocusable(KeyboardEvent*) const +{ + return element()->isTextFormControlFocusable(); +} + +bool TextFieldInputType::isMouseFocusable() const +{ + return element()->isTextFormControlFocusable(); +} + bool TextFieldInputType::isTextField() const { return true; diff --git a/Source/WebCore/html/TextFieldInputType.h b/Source/WebCore/html/TextFieldInputType.h index 559be25e7..b3629d0d3 100644 --- a/Source/WebCore/html/TextFieldInputType.h +++ b/Source/WebCore/html/TextFieldInputType.h @@ -67,6 +67,8 @@ protected: virtual void handleBlurEvent() OVERRIDE; private: + virtual bool isKeyboardFocusable(KeyboardEvent*) const OVERRIDE; + virtual bool isMouseFocusable() const OVERRIDE; virtual bool isTextField() const OVERRIDE; virtual bool valueMissing(const String&) const OVERRIDE; virtual void handleBeforeTextInsertedEvent(BeforeTextInsertedEvent*) OVERRIDE; diff --git a/Source/WebCore/html/TimeInputType.cpp b/Source/WebCore/html/TimeInputType.cpp index ec527024c..052b88756 100644 --- a/Source/WebCore/html/TimeInputType.cpp +++ b/Source/WebCore/html/TimeInputType.cpp @@ -45,9 +45,9 @@ namespace WebCore { using namespace HTMLNames; -static const double timeDefaultStep = 60.0; -static const double timeDefaultStepBase = 0.0; -static const double timeStepScaleFactor = 1000.0; +static const int timeDefaultStep = 60; +static const int timeDefaultStepBase = 0; +static const int timeStepScaleFactor = 1000; PassOwnPtr<InputType> TimeInputType::create(HTMLInputElement* element) { @@ -64,7 +64,7 @@ DateComponents::Type TimeInputType::dateType() const return DateComponents::Time; } -double TimeInputType::defaultValueForStepUp() const +Decimal TimeInputType::defaultValueForStepUp() const { double current = currentTimeMS(); double utcOffset = calculateUTCOffset(); @@ -76,17 +76,17 @@ double TimeInputType::defaultValueForStepUp() const date.setMillisecondsSinceMidnight(current); double milliseconds = date.millisecondsSinceEpoch(); ASSERT(isfinite(milliseconds)); - return milliseconds; + return Decimal::fromDouble(milliseconds); } StepRange TimeInputType::createStepRange(AnyStepHandling anyStepHandling) const { DEFINE_STATIC_LOCAL(const StepRange::StepDescription, stepDescription, (timeDefaultStep, timeDefaultStepBase, timeStepScaleFactor, StepRange::ScaledStepValueShouldBeInteger)); - double stepBase = parseToDouble(element()->fastGetAttribute(minAttr), 0); - double minimum = parseToDouble(element()->fastGetAttribute(minAttr), DateComponents::minimumTime()); - double maximum = parseToDouble(element()->fastGetAttribute(maxAttr), DateComponents::maximumTime()); - StepRange::DoubleWithDecimalPlacesOrMissing step = StepRange::parseStep(anyStepHandling, stepDescription, element()->fastGetAttribute(stepAttr)); + const Decimal stepBase = parseToNumber(element()->fastGetAttribute(minAttr), 0); + const Decimal minimum = parseToNumber(element()->fastGetAttribute(minAttr), Decimal::fromDouble(DateComponents::minimumTime())); + const Decimal maximum = parseToNumber(element()->fastGetAttribute(maxAttr), Decimal::fromDouble(DateComponents::maximumTime())); + const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element()->fastGetAttribute(stepAttr)); return StepRange(stepBase, minimum, maximum, step, stepDescription); } diff --git a/Source/WebCore/html/TimeInputType.h b/Source/WebCore/html/TimeInputType.h index 1c2c286ec..c9d5b0cdd 100644 --- a/Source/WebCore/html/TimeInputType.h +++ b/Source/WebCore/html/TimeInputType.h @@ -45,7 +45,7 @@ private: TimeInputType(HTMLInputElement* element) : BaseDateAndTimeInputType(element) { } virtual const AtomicString& formControlType() const OVERRIDE; virtual DateComponents::Type dateType() const OVERRIDE; - virtual double defaultValueForStepUp() const OVERRIDE; + virtual Decimal defaultValueForStepUp() const OVERRIDE; virtual StepRange createStepRange(AnyStepHandling) const OVERRIDE; virtual bool parseToDateComponentsInternal(const UChar*, unsigned length, DateComponents*) const OVERRIDE; virtual bool setMillisecondToDateComponents(double, DateComponents*) const OVERRIDE; diff --git a/Source/WebCore/html/WeekInputType.cpp b/Source/WebCore/html/WeekInputType.cpp index 3a6228911..c881ae8c1 100644 --- a/Source/WebCore/html/WeekInputType.cpp +++ b/Source/WebCore/html/WeekInputType.cpp @@ -42,9 +42,9 @@ namespace WebCore { using namespace HTMLNames; -static const double weekDefaultStepBase = -259200000.0; // The first day of 1970-W01. -static const double weekDefaultStep = 1.0; -static const double weekStepScaleFactor = 604800000.0; +static const int weekDefaultStepBase = -259200000; // The first day of 1970-W01. +static const int weekDefaultStep = 1; +static const int weekStepScaleFactor = 604800000; PassOwnPtr<InputType> WeekInputType::create(HTMLInputElement* element) { @@ -65,10 +65,10 @@ StepRange WeekInputType::createStepRange(AnyStepHandling anyStepHandling) const { DEFINE_STATIC_LOCAL(const StepRange::StepDescription, stepDescription, (weekDefaultStep, weekDefaultStepBase, weekStepScaleFactor, StepRange::ParsedStepValueShouldBeInteger)); - double stepBase = parseToDouble(element()->fastGetAttribute(minAttr), weekDefaultStepBase); - double minimum = parseToDouble(element()->fastGetAttribute(minAttr), DateComponents::minimumWeek()); - double maximum = parseToDouble(element()->fastGetAttribute(maxAttr), DateComponents::maximumWeek()); - StepRange::DoubleWithDecimalPlacesOrMissing step = StepRange::parseStep(anyStepHandling, stepDescription, element()->fastGetAttribute(stepAttr)); + const Decimal stepBase = parseToNumber(element()->fastGetAttribute(minAttr), weekDefaultStepBase); + const Decimal minimum = parseToNumber(element()->fastGetAttribute(minAttr), Decimal::fromDouble(DateComponents::minimumWeek())); + const Decimal maximum = parseToNumber(element()->fastGetAttribute(maxAttr), Decimal::fromDouble(DateComponents::maximumWeek())); + const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element()->fastGetAttribute(stepAttr)); return StepRange(stepBase, minimum, maximum, step, stepDescription); } diff --git a/Source/WebCore/html/canvas/CanvasRenderingContext.cpp b/Source/WebCore/html/canvas/CanvasRenderingContext.cpp index c73fc99e4..8813d990d 100644 --- a/Source/WebCore/html/canvas/CanvasRenderingContext.cpp +++ b/Source/WebCore/html/canvas/CanvasRenderingContext.cpp @@ -77,11 +77,12 @@ bool CanvasRenderingContext::wouldTaintOrigin(const HTMLVideoElement* video) if (!video || !canvas()->originClean()) return false; - if (wouldTaintOrigin(video->currentSrc())) + if (!video->hasSingleSecurityOrigin()) return true; - if (!video->hasSingleSecurityOrigin()) + if (!(video->player() && video->player()->didPassCORSAccessCheck()) && wouldTaintOrigin(video->currentSrc())) return true; + #else UNUSED_PARAM(video); #endif diff --git a/Source/WebCore/html/canvas/WebGLDepthTexture.cpp b/Source/WebCore/html/canvas/WebGLDepthTexture.cpp new file mode 100644 index 000000000..3acf46a1b --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLDepthTexture.cpp @@ -0,0 +1,65 @@ +/* + * 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 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" + +#if ENABLE(WEBGL) + +#include "WebGLDepthTexture.h" + +#include "Extensions3D.h" + +namespace WebCore { + +WebGLDepthTexture::WebGLDepthTexture(WebGLRenderingContext* context) + : WebGLExtension(context) +{ +} + +WebGLDepthTexture::~WebGLDepthTexture() +{ +} + +WebGLExtension::ExtensionName WebGLDepthTexture::getName() const +{ + return WebKitWebGLDepthTextureName; +} + +PassOwnPtr<WebGLDepthTexture> WebGLDepthTexture::create(WebGLRenderingContext* context) +{ + return adoptPtr(new WebGLDepthTexture(context)); +} + +bool WebGLDepthTexture::supported(GraphicsContext3D* context) +{ + Extensions3D* extensions = context->getExtensions(); + return extensions->supports("GL_CHROMIUM_depth_texture") + || extensions->supports("GL_OES_depth_texture") + || extensions->supports("GL_ARB_depth_texture"); +} + +} // namespace WebCore + +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/html/canvas/WebGLDepthTexture.h b/Source/WebCore/html/canvas/WebGLDepthTexture.h new file mode 100644 index 000000000..bac6aabff --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLDepthTexture.h @@ -0,0 +1,49 @@ +/* + * 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 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 WebGLDepthTexture_h +#define WebGLDepthTexture_h + +#include "WebGLExtension.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +class WebGLDepthTexture : public WebGLExtension { +public: + static PassOwnPtr<WebGLDepthTexture> create(WebGLRenderingContext*); + + static bool supported(GraphicsContext3D*); + + virtual ~WebGLDepthTexture(); + virtual ExtensionName getName() const; + +private: + WebGLDepthTexture(WebGLRenderingContext*); +}; + +} // namespace WebCore + +#endif // WebGLDepthTexture_h diff --git a/Source/WebCore/html/canvas/WebGLDepthTexture.idl b/Source/WebCore/html/canvas/WebGLDepthTexture.idl new file mode 100644 index 000000000..507450d18 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLDepthTexture.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: + * + * 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 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. + */ + +module html { + interface [ + Conditional=WEBGL, + JSGenerateIsReachable=ImplContext, + OmitConstructor + ] WebGLDepthTexture { + }; +} diff --git a/Source/WebCore/html/canvas/WebGLExtension.h b/Source/WebCore/html/canvas/WebGLExtension.h index a4d397029..c66a60861 100644 --- a/Source/WebCore/html/canvas/WebGLExtension.h +++ b/Source/WebCore/html/canvas/WebGLExtension.h @@ -42,6 +42,7 @@ public: WebGLDebugRendererInfoName, WebGLDebugShadersName, WebKitWebGLCompressedTextureS3TCName, // WEBKIT_ prefix until extension is official + WebKitWebGLDepthTextureName, // WEBKIT_ prefix until extension is official }; void ref() { m_context->ref(); } diff --git a/Source/WebCore/html/canvas/WebGLFramebuffer.cpp b/Source/WebCore/html/canvas/WebGLFramebuffer.cpp index eb07cf06a..e515423e6 100644 --- a/Source/WebCore/html/canvas/WebGLFramebuffer.cpp +++ b/Source/WebCore/html/canvas/WebGLFramebuffer.cpp @@ -36,81 +36,233 @@ namespace WebCore { namespace { - bool isAttachmentComplete(WebGLSharedObject* attachedObject, GC3Denum attachment, const char** reason) + Platform3DObject objectOrZero(WebGLObject* object) { - ASSERT(attachedObject && attachedObject->object()); - ASSERT(attachedObject->isRenderbuffer()); - ASSERT(reason); - WebGLRenderbuffer* buffer = reinterpret_cast<WebGLRenderbuffer*>(attachedObject); - switch (attachment) { - case GraphicsContext3D::DEPTH_ATTACHMENT: - if (buffer->getInternalFormat() != GraphicsContext3D::DEPTH_COMPONENT16) { - *reason = "DEPTH_ATTACHMENT is not a depth format"; - return false; - } - break; - case GraphicsContext3D::STENCIL_ATTACHMENT: - if (buffer->getInternalFormat() != GraphicsContext3D::STENCIL_INDEX8) { - *reason = "STENCIL_ATTACHMENT is not a stencil format"; - return false; - } - break; - case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT: - if (buffer->getInternalFormat() != GraphicsContext3D::DEPTH_STENCIL) { - *reason = "DEPTH_STENCIL_ATTACHMENT is not a depth-stencil format"; - return false; - } - break; - default: - ASSERT_NOT_REACHED(); - return false; - } - if (!buffer->getWidth() || !buffer->getHeight()) { - *reason = "attachment has a 0 dimension"; - return false; - } + return object ? object->object() : 0; + } + + class WebGLRenderbufferAttachment : public WebGLFramebuffer::WebGLAttachment { + public: + static PassRefPtr<WebGLFramebuffer::WebGLAttachment> create(WebGLRenderbuffer*); + + private: + WebGLRenderbufferAttachment(WebGLRenderbuffer*); + virtual GC3Dsizei getWidth() const; + virtual GC3Dsizei getHeight() const; + virtual GC3Denum getFormat() const; + virtual WebGLSharedObject* getObject() const; + virtual bool isSharedObject(WebGLSharedObject*) const; + virtual bool isValid() const; + virtual bool isInitialized() const; + virtual void setInitialized(); + virtual void onDetached(GraphicsContext3D*); + virtual void attach(GraphicsContext3D*, GC3Denum attachment); + virtual void unattach(GraphicsContext3D*, GC3Denum attachment); + + WebGLRenderbufferAttachment() { }; + + RefPtr<WebGLRenderbuffer> m_renderbuffer; + }; + + PassRefPtr<WebGLFramebuffer::WebGLAttachment> WebGLRenderbufferAttachment::create(WebGLRenderbuffer* renderbuffer) + { + return adoptRef(new WebGLRenderbufferAttachment(renderbuffer)); + } + + WebGLRenderbufferAttachment::WebGLRenderbufferAttachment(WebGLRenderbuffer* renderbuffer) + : m_renderbuffer(renderbuffer) + { + } + + GC3Dsizei WebGLRenderbufferAttachment::getWidth() const + { + return m_renderbuffer->getWidth(); + } + + GC3Dsizei WebGLRenderbufferAttachment::getHeight() const + { + return m_renderbuffer->getHeight(); + } + + GC3Denum WebGLRenderbufferAttachment::getFormat() const + { + return m_renderbuffer->getInternalFormat(); + } + + WebGLSharedObject* WebGLRenderbufferAttachment::getObject() const + { + return m_renderbuffer->object() ? m_renderbuffer.get() : 0; + } + + bool WebGLRenderbufferAttachment::isSharedObject(WebGLSharedObject* object) const + { + return object == m_renderbuffer; + } + + bool WebGLRenderbufferAttachment::isValid() const + { + return m_renderbuffer->object(); + } + + bool WebGLRenderbufferAttachment::isInitialized() const + { + return m_renderbuffer->object() && m_renderbuffer->isInitialized(); + } + + void WebGLRenderbufferAttachment::setInitialized() + { + if (m_renderbuffer->object()) + m_renderbuffer->setInitialized(); + } + + void WebGLRenderbufferAttachment::onDetached(GraphicsContext3D* context) + { + m_renderbuffer->onDetached(context); + } + + void WebGLRenderbufferAttachment::attach(GraphicsContext3D* context, GC3Denum attachment) + { + Platform3DObject object = objectOrZero(m_renderbuffer.get()); + context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, attachment, GraphicsContext3D::RENDERBUFFER, object); + } + + void WebGLRenderbufferAttachment::unattach(GraphicsContext3D* context, GC3Denum attachment) + { + if (attachment == GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT) { + context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, 0); + context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, 0); + } else + context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, attachment, GraphicsContext3D::RENDERBUFFER, 0); + } + + class WebGLTextureAttachment : public WebGLFramebuffer::WebGLAttachment { + public: + static PassRefPtr<WebGLFramebuffer::WebGLAttachment> create(WebGLTexture*, GC3Denum target, GC3Dint level); + + private: + WebGLTextureAttachment(WebGLTexture*, GC3Denum target, GC3Dint level); + virtual GC3Dsizei getWidth() const; + virtual GC3Dsizei getHeight() const; + virtual GC3Denum getFormat() const; + virtual WebGLSharedObject* getObject() const; + virtual bool isSharedObject(WebGLSharedObject*) const; + virtual bool isValid() const; + virtual bool isInitialized() const; + virtual void setInitialized(); + virtual void onDetached(GraphicsContext3D*); + virtual void attach(GraphicsContext3D*, GC3Denum attachment); + virtual void unattach(GraphicsContext3D*, GC3Denum attachment); + + WebGLTextureAttachment() { }; + + RefPtr<WebGLTexture> m_texture; + GC3Denum m_target; + GC3Dint m_level; + }; + + PassRefPtr<WebGLFramebuffer::WebGLAttachment> WebGLTextureAttachment::create(WebGLTexture* texture, GC3Denum target, GC3Dint level) + { + return adoptRef(new WebGLTextureAttachment(texture, target, level)); + } + + WebGLTextureAttachment::WebGLTextureAttachment(WebGLTexture* texture, GC3Denum target, GC3Dint level) + : m_texture(texture) + , m_target(target) + , m_level(level) + { + } + + GC3Dsizei WebGLTextureAttachment::getWidth() const + { + return m_texture->getWidth(m_target, m_level); + } + + GC3Dsizei WebGLTextureAttachment::getHeight() const + { + return m_texture->getHeight(m_target, m_level); + } + + GC3Denum WebGLTextureAttachment::getFormat() const + { + return m_texture->getInternalFormat(m_target, m_level); + } + + WebGLSharedObject* WebGLTextureAttachment::getObject() const + { + return m_texture->object() ? m_texture.get() : 0; + } + + bool WebGLTextureAttachment::isSharedObject(WebGLSharedObject* object) const + { + return object == m_texture; + } + + bool WebGLTextureAttachment::isValid() const + { + return m_texture->object(); + } + + bool WebGLTextureAttachment::isInitialized() const + { + // Textures are assumed to be initialized. return true; } - GC3Dsizei getImageWidth(WebGLSharedObject* attachedObject) + void WebGLTextureAttachment::setInitialized() { - ASSERT(attachedObject && attachedObject->object()); - ASSERT(attachedObject->isRenderbuffer()); - WebGLRenderbuffer* buffer = reinterpret_cast<WebGLRenderbuffer*>(attachedObject); - return buffer->getWidth(); + // Textures are assumed to be initialized. } - GC3Dsizei getImageHeight(WebGLSharedObject* attachedObject) + void WebGLTextureAttachment::onDetached(GraphicsContext3D* context) { - ASSERT(attachedObject && attachedObject->object()); - ASSERT(attachedObject->isRenderbuffer()); - WebGLRenderbuffer* buffer = reinterpret_cast<WebGLRenderbuffer*>(attachedObject); - return buffer->getHeight(); + m_texture->onDetached(context); } - bool isUninitialized(WebGLSharedObject* attachedObject) + void WebGLTextureAttachment::attach(GraphicsContext3D* context, GC3Denum attachment) { - if (attachedObject && attachedObject->object() && attachedObject->isRenderbuffer() - && !(reinterpret_cast<WebGLRenderbuffer*>(attachedObject))->isInitialized()) - return true; - return false; + Platform3DObject object = objectOrZero(m_texture.get()); + context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, attachment, m_target, object, m_level); } - void setInitialized(WebGLSharedObject* attachedObject) + void WebGLTextureAttachment::unattach(GraphicsContext3D* context, GC3Denum attachment) { - if (attachedObject && attachedObject->object() && attachedObject->isRenderbuffer()) - (reinterpret_cast<WebGLRenderbuffer*>(attachedObject))->setInitialized(); + context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, attachment, m_target, 0, m_level); } - bool isValidRenderbuffer(WebGLSharedObject* attachedObject) + bool isAttachmentComplete(WebGLFramebuffer::WebGLAttachment* attachedObject, GC3Denum attachment, const char** reason) { - if (!attachedObject || !attachedObject->object() || !attachedObject->isRenderbuffer()) + ASSERT(attachedObject && attachedObject->isValid()); + ASSERT(reason); + GC3Denum format = attachedObject->getFormat(); + unsigned need = GraphicsContext3D::getClearBitsByAttachmentType(attachment); + unsigned have = GraphicsContext3D::getClearBitsByFormat(format); + + if ((need & have) != need) { + *reason = "attachment type is not correct for attachment"; + return false; + } + if (!attachedObject->getWidth() || !attachedObject->getHeight()) { + *reason = "attachment has a 0 dimension"; return false; - return static_cast<WebGLRenderbuffer*>(attachedObject)->isValid(); + } + if ((attachment == GraphicsContext3D::DEPTH_ATTACHMENT || attachment == GraphicsContext3D::STENCIL_ATTACHMENT) + && format == GraphicsContext3D::DEPTH_STENCIL) { + *reason = "attachment DEPTH_STENCIL not allowed on DEPTH or STENCIL attachment"; + return false; + } + return true; } } // anonymous namespace +WebGLFramebuffer::WebGLAttachment::WebGLAttachment() +{ +} + +WebGLFramebuffer::WebGLAttachment::~WebGLAttachment() +{ +} + PassRefPtr<WebGLFramebuffer> WebGLFramebuffer::create(WebGLRenderingContext* ctx) { return adoptRef(new WebGLFramebuffer(ctx)); @@ -119,8 +271,6 @@ PassRefPtr<WebGLFramebuffer> WebGLFramebuffer::create(WebGLRenderingContext* ctx WebGLFramebuffer::WebGLFramebuffer(WebGLRenderingContext* ctx) : WebGLContextObject(ctx) , m_hasEverBeenBound(false) - , m_texTarget(0) - , m_texLevel(-1) { setObject(ctx->graphicsContext3D()->createFramebuffer()); } @@ -133,82 +283,47 @@ WebGLFramebuffer::~WebGLFramebuffer() void WebGLFramebuffer::setAttachmentForBoundFramebuffer(GC3Denum attachment, GC3Denum texTarget, WebGLTexture* texture, GC3Dint level) { ASSERT(isBound()); + removeAttachmentFromBoundFramebuffer(attachment); if (!object()) return; - removeAttachmentFromBoundFramebuffer(attachment); - if (texture && !texture->object()) - texture = 0; - switch (attachment) { - case GraphicsContext3D::COLOR_ATTACHMENT0: - m_colorAttachment = texture; - if (texture) { - m_texTarget = texTarget; - m_texLevel = level; - } - break; - case GraphicsContext3D::DEPTH_ATTACHMENT: - m_depthAttachment = texture; - break; - case GraphicsContext3D::STENCIL_ATTACHMENT: - m_stencilAttachment = texture; - break; - case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT: - m_depthStencilAttachment = texture; - break; - default: - ASSERT_NOT_REACHED(); - break; - } - if (texture) + if (texture && texture->object()) { + m_attachments.add(attachment, WebGLTextureAttachment::create(texture, texTarget, level)); texture->onAttached(); + } } void WebGLFramebuffer::setAttachmentForBoundFramebuffer(GC3Denum attachment, WebGLRenderbuffer* renderbuffer) { ASSERT(isBound()); + removeAttachmentFromBoundFramebuffer(attachment); if (!object()) return; - removeAttachmentFromBoundFramebuffer(attachment); - if (renderbuffer && !renderbuffer->object()) - renderbuffer = 0; - switch (attachment) { - case GraphicsContext3D::COLOR_ATTACHMENT0: - m_colorAttachment = renderbuffer; - break; - case GraphicsContext3D::DEPTH_ATTACHMENT: - m_depthAttachment = renderbuffer; - break; - case GraphicsContext3D::STENCIL_ATTACHMENT: - m_stencilAttachment = renderbuffer; - break; - case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT: - m_depthStencilAttachment = renderbuffer; - break; - default: - ASSERT_NOT_REACHED(); - break; - } - if (renderbuffer) + if (renderbuffer && renderbuffer->object()) { + m_attachments.add(attachment, WebGLRenderbufferAttachment::create(renderbuffer)); renderbuffer->onAttached(); + } } -WebGLSharedObject* WebGLFramebuffer::getAttachment(GC3Denum attachment) const +void WebGLFramebuffer::attach(GC3Denum attachment, GC3Denum attachmentPoint) +{ + ASSERT(isBound()); + WebGLAttachment* attachmentObject = getAttachment(attachment); + if (attachmentObject) + attachmentObject->attach(context()->graphicsContext3D(), attachmentPoint); +} + +WebGLSharedObject* WebGLFramebuffer::getAttachmentObject(GC3Denum attachment) const { if (!object()) return 0; - switch (attachment) { - case GraphicsContext3D::COLOR_ATTACHMENT0: - return m_colorAttachment.get(); - case GraphicsContext3D::DEPTH_ATTACHMENT: - return m_depthAttachment.get(); - case GraphicsContext3D::STENCIL_ATTACHMENT: - return m_stencilAttachment.get(); - case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT: - return m_depthStencilAttachment.get(); - default: - ASSERT_NOT_REACHED(); - return 0; - } + WebGLAttachment* attachmentObject = getAttachment(attachment); + return attachmentObject ? attachmentObject->getObject() : 0; +} + +WebGLFramebuffer::WebGLAttachment* WebGLFramebuffer::getAttachment(GC3Denum attachment) const +{ + const AttachmentMap::const_iterator it = m_attachments.find(attachment); + return (it != m_attachments.end()) ? it->second.get() : 0; } void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(GC3Denum attachment) @@ -217,37 +332,22 @@ void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(GC3Denum attachment) if (!object()) return; - GraphicsContext3D* context3d = context()->graphicsContext3D(); - switch (attachment) { - case GraphicsContext3D::COLOR_ATTACHMENT0: - if (m_colorAttachment) { - m_colorAttachment->onDetached(context3d); - m_colorAttachment = 0; - m_texTarget = 0; - m_texLevel = -1; - } - break; - case GraphicsContext3D::DEPTH_ATTACHMENT: - if (m_depthAttachment) { - m_depthAttachment->onDetached(context3d); - m_depthAttachment = 0; - } - break; - case GraphicsContext3D::STENCIL_ATTACHMENT: - if (m_stencilAttachment) { - m_stencilAttachment->onDetached(context3d); - m_stencilAttachment = 0; - } - break; - case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT: - if (m_depthStencilAttachment) { - m_depthStencilAttachment->onDetached(context3d); - m_depthStencilAttachment = 0; + WebGLAttachment* attachmentObject = getAttachment(attachment); + if (attachmentObject) { + attachmentObject->onDetached(context()->graphicsContext3D()); + m_attachments.remove(attachment); + switch (attachment) { + case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT: + attach(GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::DEPTH_ATTACHMENT); + attach(GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::STENCIL_ATTACHMENT); + break; + case GraphicsContext3D::DEPTH_ATTACHMENT: + attach(GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT, GraphicsContext3D::DEPTH_ATTACHMENT); + break; + case GraphicsContext3D::STENCIL_ATTACHMENT: + attach(GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT, GraphicsContext3D::STENCIL_ATTACHMENT); + break; } - break; - default: - ASSERT_NOT_REACHED(); - break; } } @@ -259,200 +359,148 @@ void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(WebGLSharedObject* a if (!attachment) return; - GraphicsContext3D* gc3d = context()->graphicsContext3D(); - - if (attachment == m_colorAttachment.get()) { - if (attachment->isRenderbuffer()) - gc3d->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::RENDERBUFFER, 0); - else - gc3d->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, m_texTarget, 0, m_texLevel); - removeAttachmentFromBoundFramebuffer(GraphicsContext3D::COLOR_ATTACHMENT0); - } - if (attachment == m_depthAttachment.get()) { - gc3d->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, 0); - removeAttachmentFromBoundFramebuffer(GraphicsContext3D::DEPTH_ATTACHMENT); - } - if (attachment == m_stencilAttachment.get()) { - gc3d->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, 0); - removeAttachmentFromBoundFramebuffer(GraphicsContext3D::STENCIL_ATTACHMENT); - } - if (attachment == m_depthStencilAttachment.get()) { - gc3d->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, 0); - gc3d->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::STENCIL_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, 0); - removeAttachmentFromBoundFramebuffer(GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT); + bool checkMore = true; + while (checkMore) { + checkMore = false; + for (AttachmentMap::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) { + WebGLAttachment* attachmentObject = it->second.get(); + if (attachmentObject->isSharedObject(attachment)) { + GC3Denum attachmentType = it->first; + attachmentObject->unattach(context()->graphicsContext3D(), attachmentType); + removeAttachmentFromBoundFramebuffer(attachmentType); + checkMore = true; + break; + } + } } } GC3Dsizei WebGLFramebuffer::getColorBufferWidth() const { - if (!object() || !isColorAttached()) + if (!object()) return 0; - if (m_colorAttachment->isRenderbuffer()) - return (reinterpret_cast<WebGLRenderbuffer*>(m_colorAttachment.get()))->getWidth(); - if (m_colorAttachment->isTexture()) - return (reinterpret_cast<WebGLTexture*>(m_colorAttachment.get()))->getWidth(m_texTarget, m_texLevel); - ASSERT_NOT_REACHED(); - return 0; + WebGLAttachment* attachment = getAttachment(GraphicsContext3D::COLOR_ATTACHMENT0); + if (!attachment) + return 0; + + return attachment->getWidth(); } GC3Dsizei WebGLFramebuffer::getColorBufferHeight() const { - if (!object() || !isColorAttached()) + if (!object()) + return 0; + WebGLAttachment* attachment = getAttachment(GraphicsContext3D::COLOR_ATTACHMENT0); + if (!attachment) return 0; - if (m_colorAttachment->isRenderbuffer()) - return (reinterpret_cast<WebGLRenderbuffer*>(m_colorAttachment.get()))->getHeight(); - if (m_colorAttachment->isTexture()) - return (reinterpret_cast<WebGLTexture*>(m_colorAttachment.get()))->getHeight(m_texTarget, m_texLevel); - ASSERT_NOT_REACHED(); - return 0; + + return attachment->getHeight(); } GC3Denum WebGLFramebuffer::getColorBufferFormat() const { - if (!object() || !isColorAttached()) + if (!object()) return 0; - if (m_colorAttachment->isRenderbuffer()) { - unsigned long format = (reinterpret_cast<WebGLRenderbuffer*>(m_colorAttachment.get()))->getInternalFormat(); - switch (format) { - case GraphicsContext3D::RGBA4: - case GraphicsContext3D::RGB5_A1: - return GraphicsContext3D::RGBA; - case GraphicsContext3D::RGB565: - return GraphicsContext3D::RGB; - } + WebGLAttachment* attachment = getAttachment(GraphicsContext3D::COLOR_ATTACHMENT0); + if (!attachment) return 0; - } - if (m_colorAttachment->isTexture()) - return (reinterpret_cast<WebGLTexture*>(m_colorAttachment.get()))->getInternalFormat(m_texTarget, m_texLevel); - ASSERT_NOT_REACHED(); - return 0; + return attachment->getFormat(); } GC3Denum WebGLFramebuffer::checkStatus(const char** reason) const { unsigned int count = 0; GC3Dsizei width = 0, height = 0; - if (isDepthAttached()) { - if (!isAttachmentComplete(m_depthAttachment.get(), GraphicsContext3D::DEPTH_ATTACHMENT, reason)) - return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT; - width = getImageWidth(m_depthAttachment.get()); - height = getImageHeight(m_depthAttachment.get()); - count++; - } - if (isStencilAttached()) { - if (!isAttachmentComplete(m_stencilAttachment.get(), GraphicsContext3D::STENCIL_ATTACHMENT, reason)) + bool haveDepth = false; + bool haveStencil = false; + bool haveDepthStencil = false; + for (AttachmentMap::const_iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) { + WebGLAttachment* attachment = it->second.get(); + if (!isAttachmentComplete(attachment, it->first, reason)) return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT; - if (!count) { - width = getImageWidth(m_stencilAttachment.get()); - height = getImageHeight(m_stencilAttachment.get()); - } else { - if (width != getImageWidth(m_stencilAttachment.get()) || height != getImageHeight(m_stencilAttachment.get())) { - *reason = "STENCIL_ATTACHMENT has different dimensions than DEPTH_ATTACHMENT"; - return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_DIMENSIONS; - } + if (!attachment->isValid()) { + *reason = "attachment is not valid"; + return GraphicsContext3D::FRAMEBUFFER_UNSUPPORTED; } - count++; - } - if (isDepthStencilAttached()) { - if (!isAttachmentComplete(m_depthStencilAttachment.get(), GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT, reason)) + if (!attachment->getFormat()) { + *reason = "attachment is an unsupported format"; return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT; - if (!isValidRenderbuffer(m_depthStencilAttachment.get())) { - *reason = "DEPTH_STENCIL_ATTACHMENT is not valid"; - return GraphicsContext3D::FRAMEBUFFER_UNSUPPORTED; + } + switch (it->first) { + case GraphicsContext3D::DEPTH_ATTACHMENT: + haveDepth = true; + break; + case GraphicsContext3D::STENCIL_ATTACHMENT: + haveDepth = true; + break; + case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT: + haveDepthStencil = true; + break; } if (!count) { - width = getImageWidth(m_depthStencilAttachment.get()); - height = getImageHeight(m_depthStencilAttachment.get()); + width = attachment->getWidth(); + height = attachment->getHeight(); } else { - if (width != getImageWidth(m_depthStencilAttachment.get()) || height != getImageHeight(m_depthStencilAttachment.get())) { - *reason = "DEPTH_STENCIL_ATTACHMENT has different dimensions than other attachments"; + if (width != attachment->getWidth() || height != attachment->getHeight()) { + *reason = "attachments do not have the same dimensions"; return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_DIMENSIONS; } } - count++; + ++count; + } + if (!count) { + *reason = "no attachments"; + return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; + } + if (!width || !height) { + *reason = "framebuffer has a 0 dimension"; + return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT; } // WebGL specific: no conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments. - if (count > 1) { + if ((haveDepthStencil && (haveDepth || haveStencil)) || (haveDepth && haveStencil)) { *reason = "conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments"; return GraphicsContext3D::FRAMEBUFFER_UNSUPPORTED; } - if (isColorAttached()) { - // FIXME: if color buffer is texture, is ALPHA, LUMINANCE or LUMINANCE_ALPHA valid? - if (!getColorBufferFormat()) { - *reason = "COLOR_ATTACHMENT0 is an unsupported format"; - return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT; - } - if (!count) { - if (!getColorBufferWidth() || !getColorBufferHeight()) { - *reason = "COLOR_ATTACHMENT0 has a 0 dimension"; - return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT; - } - } else { - if (width != getColorBufferWidth() || height != getColorBufferHeight()) { - *reason = "COLOR_ATTACHMENT0 has different dimensions than other attachments"; - return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_DIMENSIONS; - } - } - - } else { - if (!count) { - *reason = "no attachments"; - return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; - } - } return GraphicsContext3D::FRAMEBUFFER_COMPLETE; } -bool WebGLFramebuffer::onAccess(GraphicsContext3D* context3d, bool needToInitializeRenderbuffers, const char** reason) +bool WebGLFramebuffer::onAccess(GraphicsContext3D* context3d, bool needToInitializeAttachments, const char** reason) { if (checkStatus(reason) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) return false; - if (needToInitializeRenderbuffers) - return initializeRenderbuffers(context3d, reason); + if (needToInitializeAttachments) + return initializeAttachments(context3d, reason); return true; } bool WebGLFramebuffer::hasStencilBuffer() const { - return isValidRenderbuffer(m_depthStencilAttachment.get()) || isValidRenderbuffer(m_stencilAttachment.get()); + WebGLAttachment* attachment = getAttachment(GraphicsContext3D::STENCIL_ATTACHMENT); + if (!attachment) + attachment = getAttachment(GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT); + return attachment && attachment->isValid(); } void WebGLFramebuffer::deleteObjectImpl(GraphicsContext3D* context3d, Platform3DObject object) { - if (m_colorAttachment) - m_colorAttachment->onDetached(context3d); - if (m_depthAttachment) - m_depthAttachment->onDetached(context3d); - if (m_stencilAttachment) - m_stencilAttachment->onDetached(context3d); - if (m_depthStencilAttachment) - m_depthStencilAttachment->onDetached(context3d); + for (AttachmentMap::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) + it->second->onDetached(context3d); + context3d->deleteFramebuffer(object); } -bool WebGLFramebuffer::initializeRenderbuffers(GraphicsContext3D* g3d, const char** reason) +bool WebGLFramebuffer::initializeAttachments(GraphicsContext3D* g3d, const char** reason) { ASSERT(object()); - bool initColor = false, initDepth = false, initStencil = false; GC3Dbitfield mask = 0; - if (isUninitialized(m_colorAttachment.get())) { - initColor = true; - mask |= GraphicsContext3D::COLOR_BUFFER_BIT; - } - if (isUninitialized(m_depthAttachment.get())) { - initDepth = true; - mask |= GraphicsContext3D::DEPTH_BUFFER_BIT; - } - if (isUninitialized(m_stencilAttachment.get())) { - initStencil = true; - mask |= GraphicsContext3D::STENCIL_BUFFER_BIT; - } - if (isUninitialized(m_depthStencilAttachment.get())) { - initDepth = true; - initStencil = true; - mask |= (GraphicsContext3D::DEPTH_BUFFER_BIT | GraphicsContext3D::STENCIL_BUFFER_BIT); + + for (AttachmentMap::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) { + GC3Denum attachmentType = it->first; + WebGLAttachment* attachment = it->second.get(); + if (!attachment->isInitialized()) + mask |= GraphicsContext3D::getClearBitsByAttachmentType(attachmentType); } - if (!initColor && !initDepth && !initStencil) + if (!mask) return true; // We only clear un-initialized renderbuffers when they are ready to be @@ -462,6 +510,10 @@ bool WebGLFramebuffer::initializeRenderbuffers(GraphicsContext3D* g3d, const cha return false; } + bool initColor = mask & GraphicsContext3D::COLOR_BUFFER_BIT; + bool initDepth = mask & GraphicsContext3D::DEPTH_BUFFER_BIT; + bool initStencil = mask & GraphicsContext3D::STENCIL_BUFFER_BIT; + GC3Dfloat colorClearValue[] = {0, 0, 0, 0}, depthClearValue = 0; GC3Dint stencilClearValue = 0; GC3Dboolean colorMask[] = {0, 0, 0, 0}, depthMask = 0; @@ -514,15 +566,12 @@ bool WebGLFramebuffer::initializeRenderbuffers(GraphicsContext3D* g3d, const cha else g3d->disable(GraphicsContext3D::DITHER); - if (initColor) - setInitialized(m_colorAttachment.get()); - if (initDepth && initStencil && m_depthStencilAttachment) - setInitialized(m_depthStencilAttachment.get()); - else { - if (initDepth) - setInitialized(m_depthAttachment.get()); - if (initStencil) - setInitialized(m_stencilAttachment.get()); + for (AttachmentMap::iterator it = m_attachments.begin(); it != m_attachments.end(); ++it) { + GC3Denum attachmentType = it->first; + WebGLAttachment* attachment = it->second.get(); + GC3Dbitfield bits = GraphicsContext3D::getClearBitsByAttachmentType(attachmentType); + if (bits & mask) + attachment->setInitialized(); } return true; } diff --git a/Source/WebCore/html/canvas/WebGLFramebuffer.h b/Source/WebCore/html/canvas/WebGLFramebuffer.h index c69b0cc6a..9c4b519a6 100644 --- a/Source/WebCore/html/canvas/WebGLFramebuffer.h +++ b/Source/WebCore/html/canvas/WebGLFramebuffer.h @@ -39,6 +39,26 @@ class WebGLTexture; class WebGLFramebuffer : public WebGLContextObject { public: + class WebGLAttachment : public RefCounted<WebGLAttachment> { + public: + virtual ~WebGLAttachment(); + + virtual GC3Dsizei getWidth() const = 0; + virtual GC3Dsizei getHeight() const = 0; + virtual GC3Denum getFormat() const = 0; + virtual WebGLSharedObject* getObject() const = 0; + virtual bool isSharedObject(WebGLSharedObject*) const = 0; + virtual bool isValid() const = 0; + virtual bool isInitialized() const = 0; + virtual void setInitialized() = 0; + virtual void onDetached(GraphicsContext3D*) = 0; + virtual void attach(GraphicsContext3D*, GC3Denum attachment) = 0; + virtual void unattach(GraphicsContext3D*, GC3Denum attachment) = 0; + + protected: + WebGLAttachment(); + }; + virtual ~WebGLFramebuffer(); static PassRefPtr<WebGLFramebuffer> create(WebGLRenderingContext*); @@ -49,7 +69,7 @@ public: void removeAttachmentFromBoundFramebuffer(WebGLSharedObject*); // If a given attachment point for the currently bound framebuffer is not null, remove the attached object. void removeAttachmentFromBoundFramebuffer(GC3Denum); - WebGLSharedObject* getAttachment(GC3Denum) const; + WebGLSharedObject* getAttachmentObject(GC3Denum) const; GC3Denum getColorBufferFormat() const; GC3Dsizei getColorBufferWidth() const; @@ -60,8 +80,8 @@ public: // currently bound. // Return false if the framebuffer is incomplete; otherwise initialize // the buffers if they haven't been initialized and - // needToInitializeRenderbuffers is true. - bool onAccess(GraphicsContext3D*, bool needToInitializeRenderbuffers, const char** reason); + // needToInitializeAttachments is true. + bool onAccess(GraphicsContext3D*, bool needToInitializeAttachments, const char** reason); // Software version of glCheckFramebufferStatus(), except that when // FRAMEBUFFER_COMPLETE is returned, it is still possible for @@ -83,26 +103,22 @@ protected: private: virtual bool isFramebuffer() const { return true; } + WebGLAttachment* getAttachment(GC3Denum) const; + // Return false if framebuffer is incomplete. - bool initializeRenderbuffers(GraphicsContext3D*, const char** reason); + bool initializeAttachments(GraphicsContext3D*, const char** reason); // Check if the framebuffer is currently bound. bool isBound() const; - bool isColorAttached() const { return (m_colorAttachment && m_colorAttachment->object()); } - bool isDepthAttached() const { return (m_depthAttachment && m_depthAttachment->object()); } - bool isStencilAttached() const { return (m_stencilAttachment && m_stencilAttachment->object()); } - bool isDepthStencilAttached() const { return (m_depthStencilAttachment && m_depthStencilAttachment->object()); } + // attach 'attachment' at 'attachmentPoint'. + void attach(GC3Denum attachment, GC3Denum attachmentPoint); - RefPtr<WebGLSharedObject> m_colorAttachment; - RefPtr<WebGLSharedObject> m_depthAttachment; - RefPtr<WebGLSharedObject> m_stencilAttachment; - RefPtr<WebGLSharedObject> m_depthStencilAttachment; + typedef WTF::HashMap<GC3Denum, RefPtr<WebGLAttachment> > AttachmentMap; - bool m_hasEverBeenBound; + AttachmentMap m_attachments; - GC3Denum m_texTarget; - GC3Dint m_texLevel; + bool m_hasEverBeenBound; }; } // namespace WebCore diff --git a/Source/WebCore/html/canvas/WebGLRenderingContext.cpp b/Source/WebCore/html/canvas/WebGLRenderingContext.cpp index 038edc22c..5ce428503 100644 --- a/Source/WebCore/html/canvas/WebGLRenderingContext.cpp +++ b/Source/WebCore/html/canvas/WebGLRenderingContext.cpp @@ -59,6 +59,7 @@ #include "WebGLContextGroup.h" #include "WebGLDebugRendererInfo.h" #include "WebGLDebugShaders.h" +#include "WebGLDepthTexture.h" #include "WebGLFramebuffer.h" #include "WebGLLoseContext.h" #include "WebGLProgram.h" @@ -81,7 +82,7 @@ namespace WebCore { const double secondsBetweenRestoreAttempts = 1.0; -const int maxGLErrorsAllowedToConsole = 10; +const int maxGLErrorsAllowedToConsole = 256; namespace { @@ -1320,12 +1321,23 @@ void WebGLRenderingContext::compressedTexSubImage2D(GC3Denum target, GC3Dint lev cleanupAfterGraphicsCall(false); } +bool WebGLRenderingContext::validateSettableTexFormat(const char* functionName, GC3Denum format) +{ + if (GraphicsContext3D::getClearBitsByFormat(format) & (GraphicsContext3D::DEPTH_BUFFER_BIT | GraphicsContext3D::STENCIL_BUFFER_BIT)) { + synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "format can not be set, only rendered to"); + return false; + } + return true; +} + void WebGLRenderingContext::copyTexImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, GC3Dint border) { if (isContextLost()) return; if (!validateTexFuncParameters("copyTexImage2D", target, level, internalformat, width, height, border, internalformat, GraphicsContext3D::UNSIGNED_BYTE)) return; + if (!validateSettableTexFormat("copyTexImage2D", internalformat)) + return; WebGLTexture* tex = validateTextureBinding("copyTexImage2D", target, true); if (!tex) return; @@ -1380,7 +1392,10 @@ void WebGLRenderingContext::copyTexSubImage2D(GC3Denum target, GC3Dint level, GC synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "copyTexSubImage2D", "rectangle out of range"); return; } - if (!isTexInternalFormatColorBufferCombinationValid(tex->getInternalFormat(target, level), getBoundFramebufferColorFormat())) { + GC3Denum internalformat = tex->getInternalFormat(target, level); + if (!validateSettableTexFormat("copyTexSubImage2D", internalformat)) + return; + if (!isTexInternalFormatColorBufferCombinationValid(internalformat, getBoundFramebufferColorFormat())) { synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "copyTexSubImage2D", "framebuffer is incompatible format"); return; } @@ -1865,8 +1880,10 @@ void WebGLRenderingContext::drawArrays(GC3Denum mode, GC3Dint first, GC3Dsizei c return; } - if (!count) + if (!count) { + cleanupAfterGraphicsCall(true); return; + } if (!isErrorGeneratedOnOutOfBoundsAccesses()) { // Ensure we have a valid rendering state @@ -1929,8 +1946,10 @@ void WebGLRenderingContext::drawElements(GC3Denum mode, GC3Dsizei count, GC3Denu return; } - if (!count) + if (!count) { + cleanupAfterGraphicsCall(true); return; + } if (!m_boundVertexArrayObject->getElementArrayBuffer()) { synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "drawElements", "no ELEMENT_ARRAY_BUFFER bound"); @@ -2055,53 +2074,21 @@ void WebGLRenderingContext::framebufferRenderbuffer(GC3Denum target, GC3Denum at return; } Platform3DObject bufferObject = objectOrZero(buffer); - bool reattachDepth = false; - bool reattachStencil = false; - bool reattachDepthStencilDepth = false; - bool reattachDepthStencilStencil = false; switch (attachment) { case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT: m_context->framebufferRenderbuffer(target, GraphicsContext3D::DEPTH_ATTACHMENT, renderbuffertarget, bufferObject); m_context->framebufferRenderbuffer(target, GraphicsContext3D::STENCIL_ATTACHMENT, renderbuffertarget, bufferObject); - if (!bufferObject) { - reattachDepth = true; - reattachStencil = true; - } break; case GraphicsContext3D::DEPTH_ATTACHMENT: - m_context->framebufferRenderbuffer(target, attachment, renderbuffertarget, objectOrZero(buffer)); - if (!bufferObject) - reattachDepthStencilDepth = true; + m_context->framebufferRenderbuffer(target, attachment, renderbuffertarget, bufferObject); break; case GraphicsContext3D::STENCIL_ATTACHMENT: - m_context->framebufferRenderbuffer(target, attachment, renderbuffertarget, objectOrZero(buffer)); - if (!bufferObject) - reattachDepthStencilStencil = true; + m_context->framebufferRenderbuffer(target, attachment, renderbuffertarget, bufferObject); break; default: - m_context->framebufferRenderbuffer(target, attachment, renderbuffertarget, objectOrZero(buffer)); + m_context->framebufferRenderbuffer(target, attachment, renderbuffertarget, bufferObject); } m_framebufferBinding->setAttachmentForBoundFramebuffer(attachment, buffer); - if (reattachDepth) { - Platform3DObject object = objectOrZero(m_framebufferBinding->getAttachment(GraphicsContext3D::DEPTH_ATTACHMENT)); - if (object) - m_context->framebufferRenderbuffer(target, GraphicsContext3D::DEPTH_ATTACHMENT, renderbuffertarget, object); - } - if (reattachStencil) { - Platform3DObject object = objectOrZero(m_framebufferBinding->getAttachment(GraphicsContext3D::STENCIL_ATTACHMENT)); - if (object) - m_context->framebufferRenderbuffer(target, GraphicsContext3D::STENCIL_ATTACHMENT, renderbuffertarget, object); - } - if (reattachDepthStencilDepth) { - Platform3DObject object = objectOrZero(m_framebufferBinding->getAttachment(GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT)); - if (object) - m_context->framebufferRenderbuffer(target, GraphicsContext3D::DEPTH_ATTACHMENT, renderbuffertarget, object); - } - if (reattachDepthStencilStencil) { - Platform3DObject object = objectOrZero(m_framebufferBinding->getAttachment(GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT)); - if (object) - m_context->framebufferRenderbuffer(target, GraphicsContext3D::STENCIL_ATTACHMENT, renderbuffertarget, object); - } applyStencilTest(); cleanupAfterGraphicsCall(false); } @@ -2126,8 +2113,23 @@ void WebGLRenderingContext::framebufferTexture2D(GC3Denum target, GC3Denum attac synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "framebufferTexture2D", "no framebuffer bound"); return; } - m_context->framebufferTexture2D(target, attachment, textarget, objectOrZero(texture), level); + Platform3DObject textureObject = objectOrZero(texture); + switch (attachment) { + case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT: + m_context->framebufferTexture2D(target, GraphicsContext3D::DEPTH_ATTACHMENT, textarget, textureObject, level); + m_context->framebufferTexture2D(target, GraphicsContext3D::STENCIL_ATTACHMENT, textarget, textureObject, level); + break; + case GraphicsContext3D::DEPTH_ATTACHMENT: + m_context->framebufferTexture2D(target, attachment, textarget, textureObject, level); + break; + case GraphicsContext3D::STENCIL_ATTACHMENT: + m_context->framebufferTexture2D(target, attachment, textarget, textureObject, level); + break; + default: + m_context->framebufferTexture2D(target, attachment, textarget, textureObject, level); + } m_framebufferBinding->setAttachmentForBoundFramebuffer(attachment, textarget, texture, level); + applyStencilTest(); cleanupAfterGraphicsCall(false); } @@ -2150,6 +2152,9 @@ void WebGLRenderingContext::generateMipmap(GC3Denum target) synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, "generateMipmap", "level 0 not power of 2 or not all the same size"); return; } + if (!validateSettableTexFormat("generateMipmap", tex->getInternalFormat(target, 0))) + return; + // generateMipmap won't work properly if minFilter is not NEAREST_MIPMAP_LINEAR // on Mac. Remove the hack once this driver bug is fixed. #if OS(DARWIN) @@ -2325,7 +2330,14 @@ WebGLExtension* WebGLRenderingContext::getExtension(const String& name) m_webglCompressedTextureS3TC = WebGLCompressedTextureS3TC::create(this); return m_webglCompressedTextureS3TC.get(); } - + if (equalIgnoringCase(name, "WEBKIT_WEBGL_depth_texture") + && WebGLDepthTexture::supported(graphicsContext3D())) { + if (!m_webglDepthTexture) { + m_context->getExtensions()->ensureEnabled("GL_CHROMIUM_depth_texture"); + m_webglDepthTexture = WebGLDepthTexture::create(this); + } + return m_webglDepthTexture.get(); + } if (allowPrivilegedExtensions()) { if (equalIgnoringCase(name, "WEBGL_debug_renderer_info")) { if (!m_webglDebugRendererInfo) @@ -2354,7 +2366,7 @@ WebGLGetInfo WebGLRenderingContext::getFramebufferAttachmentParameter(GC3Denum t return WebGLGetInfo(); } - WebGLSharedObject* object = m_framebufferBinding->getAttachment(attachment); + WebGLSharedObject* object = m_framebufferBinding->getAttachmentObject(attachment); if (!object) { if (pname == GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE) return WebGLGetInfo(GraphicsContext3D::NONE); @@ -2445,7 +2457,7 @@ WebGLGetInfo WebGLRenderingContext::getParameter(GC3Denum pname, ExceptionCode& case GraphicsContext3D::CURRENT_PROGRAM: return WebGLGetInfo(PassRefPtr<WebGLProgram>(m_currentProgram)); case GraphicsContext3D::DEPTH_BITS: - if (!m_attributes.depth) + if (!m_framebufferBinding && !m_attributes.depth) return WebGLGetInfo(intZero); return getIntParameter(pname); case GraphicsContext3D::DEPTH_CLEAR_VALUE: @@ -2540,7 +2552,7 @@ WebGLGetInfo WebGLRenderingContext::getParameter(GC3Denum pname, ExceptionCode& case GraphicsContext3D::STENCIL_BACK_WRITEMASK: return getUnsignedIntParameter(pname); case GraphicsContext3D::STENCIL_BITS: - if (!m_attributes.stencil) + if (!m_framebufferBinding && !m_attributes.stencil) return WebGLGetInfo(intZero); return getIntParameter(pname); case GraphicsContext3D::STENCIL_CLEAR_VALUE: @@ -2809,6 +2821,8 @@ Vector<String> WebGLRenderingContext::getSupportedExtensions() result.append("WEBKIT_WEBGL_lose_context"); if (WebGLCompressedTextureS3TC::supported(this)) result.append("WEBKIT_WEBGL_compressed_texture_s3tc"); + if (WebGLDepthTexture::supported(graphicsContext3D())) + result.append("WEBKIT_WEBGL_depth_texture"); if (allowPrivilegedExtensions()) { if (m_context->getExtensions()->supports("GL_ANGLE_translated_shader_source")) @@ -3501,11 +3515,21 @@ void WebGLRenderingContext::texImage2DBase(GC3Denum target, GC3Dint level, GC3De } } if (!pixels) { - bool succeed = m_context->texImage2DResourceSafe(target, level, internalformat, width, height, - border, format, type, m_unpackAlignment); - if (!succeed) - return; + // Note: Chromium's OpenGL implementation clears textures and isResourceSafe() is therefore true. + // For other implementations, if they are using ANGLE_depth_texture, ANGLE depth textures + // can not be cleared with texImage2D and must be cleared by binding to an fbo and calling + // clear. + if (isResourceSafe()) + m_context->texImage2D(target, level, internalformat, width, height, border, format, type, 0); + else { + bool succeed = m_context->texImage2DResourceSafe(target, level, internalformat, width, height, + border, format, type, m_unpackAlignment); + if (!succeed) + return; + } } else { + if (!validateSettableTexFormat("texImage2D", internalformat)) + return; m_context->texImage2D(target, level, internalformat, width, height, border, format, type, pixels); } @@ -3518,6 +3542,8 @@ void WebGLRenderingContext::texImage2DImpl(GC3Denum target, GC3Dint level, GC3De bool flipY, bool premultiplyAlpha, ExceptionCode& ec) { ec = 0; + if (!validateSettableTexFormat("texImage2D", internalformat)) + return; Vector<uint8_t> data; if (!m_context->extractImageData(image, format, type, flipY, premultiplyAlpha, m_unpackColorspaceConversion == GraphicsContext3D::NONE, data)) { synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "texImage2D", "bad image data"); @@ -3535,7 +3561,7 @@ void WebGLRenderingContext::texImage2D(GC3Denum target, GC3Dint level, GC3Denum GC3Dsizei width, GC3Dsizei height, GC3Dint border, GC3Denum format, GC3Denum type, ArrayBufferView* pixels, ExceptionCode& ec) { - if (isContextLost() || !validateTexFuncData("texImage2D", width, height, format, type, pixels, NullAllowed)) + if (isContextLost() || !validateTexFuncData("texImage2D", level, width, height, format, type, pixels, NullAllowed)) return; void* data = pixels ? pixels->baseAddress() : 0; Vector<uint8_t> tempData; @@ -3713,6 +3739,8 @@ void WebGLRenderingContext::texSubImage2DBase(GC3Denum target, GC3Dint level, GC return; if (!validateSize("texSubImage2D", xoffset, yoffset)) return; + if (!validateSettableTexFormat("texSubImage2D", format)) + return; WebGLTexture* tex = validateTextureBinding("texSubImage2D", target, true); if (!tex) return; @@ -3752,7 +3780,7 @@ void WebGLRenderingContext::texSubImage2D(GC3Denum target, GC3Dint level, GC3Din GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, ArrayBufferView* pixels, ExceptionCode& ec) { - if (isContextLost() || !validateTexFuncData("texSubImage2D", width, height, format, type, pixels, NullNotAllowed)) + if (isContextLost() || !validateTexFuncData("texSubImage2D", level, width, height, format, type, pixels, NullNotAllowed)) return; void* data = pixels->baseAddress(); Vector<uint8_t> tempData; @@ -4583,20 +4611,9 @@ void WebGLRenderingContext::createFallbackBlackTextures1x1() bool WebGLRenderingContext::isTexInternalFormatColorBufferCombinationValid(GC3Denum texInternalFormat, GC3Denum colorBufferFormat) { - switch (colorBufferFormat) { - case GraphicsContext3D::ALPHA: - if (texInternalFormat == GraphicsContext3D::ALPHA) - return true; - break; - case GraphicsContext3D::RGB: - if (texInternalFormat == GraphicsContext3D::LUMINANCE - || texInternalFormat == GraphicsContext3D::RGB) - return true; - break; - case GraphicsContext3D::RGBA: - return true; - } - return false; + unsigned need = GraphicsContext3D::getChannelBitsByFormat(texInternalFormat); + unsigned have = GraphicsContext3D::getChannelBitsByFormat(colorBufferFormat); + return (need & have) == need; } GC3Denum WebGLRenderingContext::getBoundFramebufferColorFormat() @@ -4687,7 +4704,7 @@ bool WebGLRenderingContext::validateString(const char* functionName, const Strin return true; } -bool WebGLRenderingContext::validateTexFuncFormatAndType(const char* functionName, GC3Denum format, GC3Denum type) +bool WebGLRenderingContext::validateTexFuncFormatAndType(const char* functionName, GC3Denum format, GC3Denum type, GC3Dint level) { switch (format) { case GraphicsContext3D::ALPHA: @@ -4696,6 +4713,11 @@ bool WebGLRenderingContext::validateTexFuncFormatAndType(const char* functionNam case GraphicsContext3D::RGB: case GraphicsContext3D::RGBA: break; + case GraphicsContext3D::DEPTH_COMPONENT: + if (m_webglDepthTexture) + break; + synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "DEPTH_COMPONENT texture format not enabled"); + return false; default: synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid texture format"); return false; @@ -4712,6 +4734,12 @@ bool WebGLRenderingContext::validateTexFuncFormatAndType(const char* functionNam break; synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid texture type"); return false; + case GraphicsContext3D::UNSIGNED_INT: + case GraphicsContext3D::UNSIGNED_SHORT: + if (m_webglDepthTexture) + break; + synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid texture type"); + return false; default: synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid texture type"); return false; @@ -4745,6 +4773,21 @@ bool WebGLRenderingContext::validateTexFuncFormatAndType(const char* functionNam return false; } break; + case GraphicsContext3D::DEPTH_COMPONENT: + if (!m_webglDepthTexture) { + synthesizeGLError(GraphicsContext3D::INVALID_ENUM, functionName, "invalid format. DEPTH_COMPONENT not enabled"); + return false; + } + if (type != GraphicsContext3D::UNSIGNED_SHORT + && type != GraphicsContext3D::UNSIGNED_INT) { + synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "invalid type for DEPTH_COMPONENT format"); + return false; + } + if (level > 0) { + synthesizeGLError(GraphicsContext3D::INVALID_OPERATION, functionName, "level must be 0 for DEPTH_COMPONENT format"); + return false; + } + break; default: ASSERT_NOT_REACHED(); } @@ -4791,7 +4834,7 @@ bool WebGLRenderingContext::validateTexFuncParameters(const char* functionName, // We absolutely have to validate the format and type combination. // The texImage2D entry points taking HTMLImage, etc. will produce // temporary data based on this combination, so it must be legal. - if (!validateTexFuncFormatAndType(functionName, format, type) || !validateTexFuncLevel(functionName, target, level)) + if (!validateTexFuncFormatAndType(functionName, format, type, level) || !validateTexFuncLevel(functionName, target, level)) return false; if (width < 0 || height < 0) { @@ -4835,7 +4878,7 @@ bool WebGLRenderingContext::validateTexFuncParameters(const char* functionName, return true; } -bool WebGLRenderingContext::validateTexFuncData(const char* functionName, +bool WebGLRenderingContext::validateTexFuncData(const char* functionName, GC3Dint level, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, ArrayBufferView* pixels, @@ -4848,7 +4891,9 @@ bool WebGLRenderingContext::validateTexFuncData(const char* functionName, return false; } - if (!validateTexFuncFormatAndType(functionName, format, type)) + if (!validateTexFuncFormatAndType(functionName, format, type, level)) + return false; + if (!validateSettableTexFormat(functionName, format)) return false; switch (type) { diff --git a/Source/WebCore/html/canvas/WebGLRenderingContext.h b/Source/WebCore/html/canvas/WebGLRenderingContext.h index 548a999b1..e4c3f74cd 100644 --- a/Source/WebCore/html/canvas/WebGLRenderingContext.h +++ b/Source/WebCore/html/canvas/WebGLRenderingContext.h @@ -57,6 +57,7 @@ class WebGLCompressedTextureS3TC; class WebGLContextAttributes; class WebGLDebugRendererInfo; class WebGLDebugShaders; +class WebGLDepthTexture; class WebGLExtension; class WebGLFramebuffer; class WebGLLoseContext; @@ -506,6 +507,7 @@ public: OwnPtr<WebGLDebugRendererInfo> m_webglDebugRendererInfo; OwnPtr<WebGLDebugShaders> m_webglDebugShaders; OwnPtr<WebGLCompressedTextureS3TC> m_webglCompressedTextureS3TC; + OwnPtr<WebGLDepthTexture> m_webglDepthTexture; // Helpers for getParameter and others WebGLGetInfo getBooleanParameter(GC3Denum); @@ -574,7 +576,7 @@ public: // Helper function to check input format/type for functions {copy}Tex{Sub}Image. // Generates GL error and returns false if parameters are invalid. - bool validateTexFuncFormatAndType(const char* functionName, GC3Denum format, GC3Denum type); + bool validateTexFuncFormatAndType(const char* functionName, GC3Denum format, GC3Denum type, GC3Dint level); // Helper function to check input level for functions {copy}Tex{Sub}Image. // Generates GL error and returns false if level is invalid. @@ -596,12 +598,18 @@ public: // Helper function to validate that the given ArrayBufferView // is of the correct type and contains enough data for the texImage call. // Generates GL error and returns false if parameters are invalid. - bool validateTexFuncData(const char* functionName, + bool validateTexFuncData(const char* functionName, GC3Dint level, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, ArrayBufferView* pixels, NullDisposition); + // Helper function to validate a given texture format is settable as in + // you can supply data to texImage2D, or call texImage2D, copyTexImage2D and + // copyTexSubImage2D. + // Generates GL error and returns false if the format is not settable. + bool validateSettableTexFormat(const char* functionName, GC3Denum format); + // Helper function to validate compressed texture data is correct size // for the given format and dimensions. bool validateCompressedTexFuncData(const char* functionName, diff --git a/Source/WebCore/html/parser/HTMLConstructionSite.h b/Source/WebCore/html/parser/HTMLConstructionSite.h index 27906643b..367c0fe26 100644 --- a/Source/WebCore/html/parser/HTMLConstructionSite.h +++ b/Source/WebCore/html/parser/HTMLConstructionSite.h @@ -75,6 +75,7 @@ enum WhitespaceMode { class AtomicHTMLToken; class Document; class Element; +class HTMLFormElement; class HTMLConstructionSite { WTF_MAKE_NONCOPYABLE(HTMLConstructionSite); diff --git a/Source/WebCore/html/parser/HTMLParserIdioms.cpp b/Source/WebCore/html/parser/HTMLParserIdioms.cpp index a99a21a08..c3becc360 100644 --- a/Source/WebCore/html/parser/HTMLParserIdioms.cpp +++ b/Source/WebCore/html/parser/HTMLParserIdioms.cpp @@ -25,6 +25,7 @@ #include "config.h" #include "HTMLParserIdioms.h" +#include "Decimal.h" #include <limits> #include <wtf/MathExtras.h> #include <wtf/dtoa.h> @@ -58,6 +59,15 @@ String stripLeadingAndTrailingHTMLSpaces(const String& string) return string.substring(numLeadingSpaces, length - (numLeadingSpaces + numTrailingSpaces)); } +String serializeForNumberType(const Decimal& number) +{ + if (number.isZero()) { + // Decimal::toString appends exponent, e.g. "0e-18" + return number.isNegative() ? "-0" : "0"; + } + return number.toString(); +} + String serializeForNumberType(double number) { // According to HTML5, "the best representation of the number n as a floating @@ -66,6 +76,35 @@ String serializeForNumberType(double number) return String(numberToString(number, buffer)); } +Decimal parseToDecimalForNumberType(const String& string, const Decimal& fallbackValue) +{ + // See HTML5 2.5.4.3 `Real numbers.' and parseToDoubleForNumberType + + // String::toDouble() accepts leading + and whitespace characters, which are not valid here. + const UChar firstCharacter = string[0]; + if (firstCharacter != '-' && firstCharacter != '.' && !isASCIIDigit(firstCharacter)) + return fallbackValue; + + const Decimal value = Decimal::fromString(string); + if (!value.isFinite()) + return fallbackValue; + + // Numbers are considered finite IEEE 754 single-precision floating point values. + // See HTML5 2.5.4.3 `Real numbers.' + // FIXME: We should use numeric_limits<double>::max for number input type. + const Decimal floatMax = Decimal::fromDouble(std::numeric_limits<float>::max()); + if (value < -floatMax || value > floatMax) + return fallbackValue; + + // We return +0 for -0 case. + return value.isZero() ? Decimal(0) : value; +} + +Decimal parseToDecimalForNumberType(const String& string) +{ + return parseToDecimalForNumberType(string, Decimal::nan()); +} + double parseToDoubleForNumberType(const String& string, double fallbackValue) { // See HTML5 2.5.4.3 `Real numbers.' diff --git a/Source/WebCore/html/parser/HTMLParserIdioms.h b/Source/WebCore/html/parser/HTMLParserIdioms.h index 8a774f9ee..5f0186d44 100644 --- a/Source/WebCore/html/parser/HTMLParserIdioms.h +++ b/Source/WebCore/html/parser/HTMLParserIdioms.h @@ -30,6 +30,8 @@ namespace WebCore { +class Decimal; + // Space characters as defined by the HTML specification. bool isHTMLSpace(UChar); bool isHTMLLineBreak(UChar); @@ -39,11 +41,14 @@ bool isNotHTMLSpace(UChar); String stripLeadingAndTrailingHTMLSpaces(const String&); // An implementation of the HTML specification's algorithm to convert a number to a string for number and range types. +String serializeForNumberType(const Decimal&); String serializeForNumberType(double); -// Convert the specified string to a double. If the conversion fails, the return value is false. +// Convert the specified string to a decimal/double. If the conversion fails, the return value is fallback value or NaN if not specified. // Leading or trailing illegal characters cause failure, as does passing an empty string. // The double* parameter may be 0 to check if the string can be parsed without getting the result. +Decimal parseToDecimalForNumberType(const String&); +Decimal parseToDecimalForNumberType(const String&, const Decimal& fallbackValue); double parseToDoubleForNumberType(const String&); double parseToDoubleForNumberType(const String&, double fallbackValue); double parseToDoubleForNumberTypeWithDecimalPlaces(const String&, unsigned*); diff --git a/Source/WebCore/html/parser/HTMLParserScheduler.cpp b/Source/WebCore/html/parser/HTMLParserScheduler.cpp index 2692036f3..a9dd24af6 100644 --- a/Source/WebCore/html/parser/HTMLParserScheduler.cpp +++ b/Source/WebCore/html/parser/HTMLParserScheduler.cpp @@ -95,6 +95,7 @@ void HTMLParserScheduler::checkForYieldBeforeScript(PumpSession& session) bool needsFirstPaint = document->view() && !document->view()->hasEverPainted(); if (needsFirstPaint && document->isLayoutTimerActive()) session.needsYield = true; + session.didSeeScript = true; } void HTMLParserScheduler::scheduleForResume() diff --git a/Source/WebCore/html/parser/HTMLParserScheduler.h b/Source/WebCore/html/parser/HTMLParserScheduler.h index b0e2e85f0..e52f3ef3c 100644 --- a/Source/WebCore/html/parser/HTMLParserScheduler.h +++ b/Source/WebCore/html/parser/HTMLParserScheduler.h @@ -47,12 +47,14 @@ public: , processedTokens(INT_MAX) , startTime(0) , needsYield(false) + , didSeeScript(false) { } int processedTokens; double startTime; bool needsYield; + bool didSeeScript; }; class HTMLParserScheduler { @@ -67,13 +69,15 @@ public: // Inline as this is called after every token in the parser. void checkForYieldBeforeToken(PumpSession& session) { - if (session.processedTokens > m_parserChunkSize) { + if (session.processedTokens > m_parserChunkSize || session.didSeeScript) { // currentTime() can be expensive. By delaying, we avoided calling // currentTime() when constructing non-yielding PumpSessions. if (!session.startTime) session.startTime = currentTime(); session.processedTokens = 0; + session.didSeeScript = false; + double elapsedTime = currentTime() - session.startTime; if (elapsedTime > m_parserTimeLimit) session.needsYield = true; diff --git a/Source/WebCore/html/shadow/CalendarPickerElement.cpp b/Source/WebCore/html/shadow/CalendarPickerElement.cpp index 1a7b49fa6..5358ccaf7 100644 --- a/Source/WebCore/html/shadow/CalendarPickerElement.cpp +++ b/Source/WebCore/html/shadow/CalendarPickerElement.cpp @@ -208,7 +208,7 @@ void CalendarPickerElement::writeDocument(DocumentWriter& writer) String minString = date.toString(); date.setMillisecondsSinceEpochForDate(input->maximum()); String maxString = date.toString(); - double step; + Decimal step; String stepString = input->fastGetAttribute(stepAttr); if (stepString.isEmpty() || !input->getAllowedValueStep(&step)) stepString = "1"; diff --git a/Source/WebCore/html/shadow/HTMLContentElement.cpp b/Source/WebCore/html/shadow/HTMLContentElement.cpp index 08b45d4ab..b0bdd885f 100644 --- a/Source/WebCore/html/shadow/HTMLContentElement.cpp +++ b/Source/WebCore/html/shadow/HTMLContentElement.cpp @@ -29,7 +29,7 @@ #include "ContentDistributor.h" #include "ContentSelectorQuery.h" -#include "ContextEnabledFeatures.h" +#include "ContextFeatures.h" #include "ElementShadow.h" #include "HTMLNames.h" #include "QualifiedName.h" @@ -43,7 +43,7 @@ using HTMLNames::selectAttr; static const QualifiedName& contentTagName(Document* document) { #if ENABLE(SHADOW_DOM) - if (!ContextEnabledFeatures::shadowDOMEnabled(document->domWindow())) + if (!ContextFeatures::shadowDOMEnabled(document)) return HTMLNames::webkitShadowContentTag; return HTMLNames::contentTag; #else diff --git a/Source/WebCore/html/shadow/InsertionPoint.cpp b/Source/WebCore/html/shadow/InsertionPoint.cpp index 1ca1e3101..580512862 100644 --- a/Source/WebCore/html/shadow/InsertionPoint.cpp +++ b/Source/WebCore/html/shadow/InsertionPoint.cpp @@ -49,8 +49,11 @@ void InsertionPoint::attach() { if (ShadowRoot* root = shadowRoot()) root->owner()->ensureDistribution(); - for (size_t i = 0; i < m_distribution.size(); ++i) - m_distribution.at(i)->attach(); + for (size_t i = 0; i < m_distribution.size(); ++i) { + if (!m_distribution.at(i)->attached()) + m_distribution.at(i)->attach(); + } + HTMLElement::attach(); } @@ -60,6 +63,7 @@ void InsertionPoint::detach() root->owner()->ensureDistribution(); for (size_t i = 0; i < m_distribution.size(); ++i) m_distribution.at(i)->detach(); + HTMLElement::detach(); } diff --git a/Source/WebCore/html/shadow/InsertionPoint.h b/Source/WebCore/html/shadow/InsertionPoint.h index f46d3d482..98ca15486 100644 --- a/Source/WebCore/html/shadow/InsertionPoint.h +++ b/Source/WebCore/html/shadow/InsertionPoint.h @@ -57,6 +57,7 @@ public: virtual bool isInsertionPoint() const OVERRIDE { return true; } size_t indexOf(Node* node) const { return m_distribution.find(node); } + bool contains(const Node* node) const { return m_distribution.contains(const_cast<Node*>(node)); } 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(); } diff --git a/Source/WebCore/html/shadow/MediaControlElements.cpp b/Source/WebCore/html/shadow/MediaControlElements.cpp index 212048ab2..236384f16 100644 --- a/Source/WebCore/html/shadow/MediaControlElements.cpp +++ b/Source/WebCore/html/shadow/MediaControlElements.cpp @@ -901,6 +901,7 @@ const AtomicString& MediaControlTimelineElement::shadowPseudoId() const inline MediaControlVolumeSliderElement::MediaControlVolumeSliderElement(Document* document) : MediaControlInputElement(document, MediaVolumeSlider) + , m_clearMutedOnUserInteraction(false) { } @@ -934,6 +935,8 @@ void MediaControlVolumeSliderElement::defaultEventHandler(Event* event) mediaController()->setVolume(volume, ec); ASSERT(!ec); } + if (m_clearMutedOnUserInteraction) + mediaController()->setMuted(false); } void MediaControlVolumeSliderElement::setVolume(float volume) @@ -942,6 +945,11 @@ void MediaControlVolumeSliderElement::setVolume(float volume) setValue(String::number(volume)); } +void MediaControlVolumeSliderElement::setClearMutedOnUserInteraction(bool clearMute) +{ + m_clearMutedOnUserInteraction = clearMute; +} + const AtomicString& MediaControlVolumeSliderElement::shadowPseudoId() const { DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider")); @@ -973,9 +981,8 @@ const AtomicString& MediaControlFullscreenVolumeSliderElement::shadowPseudoId() // ---------------------------- -inline MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(Document* document, MediaControls* controls) +inline MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(Document* document, MediaControls*) : MediaControlInputElement(document, MediaEnterFullscreenButton) - , m_controls(controls) { } diff --git a/Source/WebCore/html/shadow/MediaControlElements.h b/Source/WebCore/html/shadow/MediaControlElements.h index 03570f755..efdb21f0d 100644 --- a/Source/WebCore/html/shadow/MediaControlElements.h +++ b/Source/WebCore/html/shadow/MediaControlElements.h @@ -389,12 +389,14 @@ public: virtual void defaultEventHandler(Event*); void setVolume(float); + void setClearMutedOnUserInteraction(bool); protected: MediaControlVolumeSliderElement(Document*); private: virtual const AtomicString& shadowPseudoId() const; + bool m_clearMutedOnUserInteraction; }; // ---------------------------- @@ -410,8 +412,6 @@ private: MediaControlFullscreenButtonElement(Document*, MediaControls*); virtual const AtomicString& shadowPseudoId() const; - - MediaControls* m_controls; }; // ---------------------------- diff --git a/Source/WebCore/html/shadow/MediaControlRootElementChromium.cpp b/Source/WebCore/html/shadow/MediaControlRootElementChromium.cpp index bdcd9164d..f4ce3d617 100644 --- a/Source/WebCore/html/shadow/MediaControlRootElementChromium.cpp +++ b/Source/WebCore/html/shadow/MediaControlRootElementChromium.cpp @@ -29,6 +29,7 @@ #if ENABLE(VIDEO) #include "MediaControlRootElementChromium.h" +#include "HTMLDivElement.h" #include "HTMLMediaElement.h" #include "HTMLNames.h" #include "MediaControlElements.h" @@ -45,23 +46,46 @@ using namespace std; namespace WebCore { +static const double timeWithoutMouseMovementBeforeHidingControls = 2; + +MediaControlChromiumEnclosureElement::MediaControlChromiumEnclosureElement(Document* document) + : HTMLDivElement(HTMLNames::divTag, document->document()) + , m_mediaController(0) +{ +} + +PassRefPtr<MediaControlChromiumEnclosureElement> MediaControlChromiumEnclosureElement::create(Document* document) +{ + return adoptRef(new MediaControlChromiumEnclosureElement(document)); +} + +const AtomicString& MediaControlChromiumEnclosureElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-enclosure")); + return id; +} + MediaControlRootElementChromium::MediaControlRootElementChromium(Document* document) : MediaControls(document) , m_mediaController(0) , m_playButton(0) , m_currentTimeDisplay(0) + , m_durationDisplay(0) , m_timeline(0) - , m_timelineContainer(0) , m_panelMuteButton(0) , m_volumeSlider(0) - , m_volumeSliderContainer(0) +#if ENABLE(FULLSCREEN_MEDIA_CONTROLS) + , m_fullscreenButton(0) +#endif , m_panel(0) + , m_enclosure(0) #if ENABLE(VIDEO_TRACK) , m_textDisplayContainer(0) - , m_textTrackDisplay(0) #endif , m_opaque(true) + , m_hideFullscreenControlsTimer(this, &MediaControlRootElementChromium::hideFullscreenControlsTimerFired) , m_isMouseOverControls(false) + , m_isFullscreen(false) { } @@ -77,6 +101,9 @@ PassRefPtr<MediaControlRootElementChromium> MediaControlRootElementChromium::cre RefPtr<MediaControlRootElementChromium> controls = adoptRef(new MediaControlRootElementChromium(document)); + // Create an enclosing element for the panel so we can visually offset the controls correctly. + RefPtr<MediaControlChromiumEnclosureElement> enclosure = MediaControlChromiumEnclosureElement::create(document); + RefPtr<MediaControlPanelElement> panel = MediaControlPanelElement::create(document); ExceptionCode ec; @@ -87,52 +114,53 @@ PassRefPtr<MediaControlRootElementChromium> MediaControlRootElementChromium::cre if (ec) return 0; - RefPtr<MediaControlTimelineContainerElement> timelineContainer = MediaControlTimelineContainerElement::create(document); - RefPtr<MediaControlTimelineElement> timeline = MediaControlTimelineElement::create(document, controls.get()); controls->m_timeline = timeline.get(); - timelineContainer->appendChild(timeline.release(), ec, true); + panel->appendChild(timeline.release(), ec, true); if (ec) return 0; RefPtr<MediaControlCurrentTimeDisplayElement> currentTimeDisplay = MediaControlCurrentTimeDisplayElement::create(document); controls->m_currentTimeDisplay = currentTimeDisplay.get(); - timelineContainer->appendChild(currentTimeDisplay.release(), ec, true); + controls->m_currentTimeDisplay->hide(); + panel->appendChild(currentTimeDisplay.release(), ec, true); if (ec) return 0; - controls->m_timelineContainer = timelineContainer.get(); - panel->appendChild(timelineContainer.release(), ec, true); + RefPtr<MediaControlTimeRemainingDisplayElement> durationDisplay = MediaControlTimeRemainingDisplayElement::create(document); + controls->m_durationDisplay = durationDisplay.get(); + panel->appendChild(durationDisplay.release(), ec, true); if (ec) return 0; - RefPtr<HTMLDivElement> panelVolumeControlContainer = HTMLDivElement::create(document); - - RefPtr<MediaControlVolumeSliderContainerElement> volumeSliderContainer = MediaControlVolumeSliderContainerElement::create(document); + RefPtr<MediaControlPanelMuteButtonElement> panelMuteButton = MediaControlPanelMuteButtonElement::create(document, controls.get()); + controls->m_panelMuteButton = panelMuteButton.get(); + panel->appendChild(panelMuteButton.release(), ec, true); + if (ec) + return 0; RefPtr<MediaControlVolumeSliderElement> slider = MediaControlVolumeSliderElement::create(document); controls->m_volumeSlider = slider.get(); - volumeSliderContainer->appendChild(slider.release(), ec, true); + controls->m_volumeSlider->setClearMutedOnUserInteraction(true); + panel->appendChild(slider.release(), ec, true); if (ec) return 0; - controls->m_volumeSliderContainer = volumeSliderContainer.get(); - panelVolumeControlContainer->appendChild(volumeSliderContainer.release(), ec, true); - if (ec) - return 0; - - RefPtr<MediaControlPanelMuteButtonElement> panelMuteButton = MediaControlPanelMuteButtonElement::create(document, controls.get()); - controls->m_panelMuteButton = panelMuteButton.get(); - panelVolumeControlContainer->appendChild(panelMuteButton.release(), ec, true); +#if ENABLE(FULLSCREEN_MEDIA_CONTROLS) + RefPtr<MediaControlFullscreenButtonElement> fullscreenButton = MediaControlFullscreenButtonElement::create(document, controls.get()); + controls->m_fullscreenButton = fullscreenButton.get(); + panel->appendChild(fullscreenButton.release(), ec, true); if (ec) return 0; +#endif - panel->appendChild(panelVolumeControlContainer, ec, true); + controls->m_panel = panel.get(); + enclosure->appendChild(panel.release(), ec, true); if (ec) return 0; - controls->m_panel = panel.get(); - controls->appendChild(panel.release(), ec, true); + controls->m_enclosure = enclosure.get(); + controls->appendChild(enclosure.release(), ec, true); if (ec) return 0; @@ -149,18 +177,22 @@ void MediaControlRootElementChromium::setMediaController(MediaControllerInterfac m_playButton->setMediaController(controller); if (m_currentTimeDisplay) m_currentTimeDisplay->setMediaController(controller); + if (m_durationDisplay) + m_durationDisplay->setMediaController(controller); if (m_timeline) m_timeline->setMediaController(controller); - if (m_timelineContainer) - m_timelineContainer->setMediaController(controller); if (m_panelMuteButton) m_panelMuteButton->setMediaController(controller); if (m_volumeSlider) m_volumeSlider->setMediaController(controller); - if (m_volumeSliderContainer) - m_volumeSliderContainer->setMediaController(controller); +#if ENABLE(FULLSCREEN_MEDIA_CONTROLS) + if (m_fullscreenButton) + m_fullscreenButton->setMediaController(controller); +#endif if (m_panel) m_panel->setMediaController(controller); + if (m_enclosure) + m_enclosure->setMediaController(controller); #if ENABLE(VIDEO_TRACK) if (m_textDisplayContainer) m_textDisplayContainer->setMediaController(controller); @@ -178,7 +210,6 @@ void MediaControlRootElementChromium::hide() { m_panel->setIsDisplayed(false); m_panel->hide(); - m_volumeSliderContainer->hide(); } void MediaControlRootElementChromium::makeOpaque() @@ -189,7 +220,6 @@ void MediaControlRootElementChromium::makeOpaque() void MediaControlRootElementChromium::makeTransparent() { m_panel->makeTransparent(); - m_volumeSliderContainer->hide(); } void MediaControlRootElementChromium::reset() @@ -202,7 +232,11 @@ void MediaControlRootElementChromium::reset() float duration = m_mediaController->duration(); m_timeline->setDuration(duration); - m_timelineContainer->show(); + m_timeline->show(); + + m_durationDisplay->setInnerText(page->theme()->formatMediaControlsTime(duration), ASSERT_NO_EXCEPTION); + m_durationDisplay->setCurrentValue(duration); + m_timeline->setPosition(m_mediaController->currentTime()); updateTimeDisplay(); @@ -211,6 +245,9 @@ void MediaControlRootElementChromium::reset() if (m_volumeSlider) m_volumeSlider->setVolume(m_mediaController->volume()); +#if ENABLE(FULLSCREEN_MEDIA_CONTROLS) + m_fullscreenButton->show(); +#endif makeOpaque(); } @@ -218,7 +255,12 @@ void MediaControlRootElementChromium::playbackStarted() { m_playButton->updateDisplayType(); m_timeline->setPosition(m_mediaController->currentTime()); + m_currentTimeDisplay->show(); + m_durationDisplay->hide(); updateTimeDisplay(); + + if (m_isFullscreen) + startHideFullscreenControlsTimer(); } void MediaControlRootElementChromium::playbackProgressed() @@ -236,6 +278,8 @@ void MediaControlRootElementChromium::playbackStopped() m_timeline->setPosition(m_mediaController->currentTime()); updateTimeDisplay(); makeOpaque(); + + stopHideFullscreenControlsTimer(); } void MediaControlRootElementChromium::updateTimeDisplay() @@ -247,6 +291,12 @@ void MediaControlRootElementChromium::updateTimeDisplay() 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. ExceptionCode ec; m_currentTimeDisplay->setInnerText(page->theme()->formatMediaControlsCurrentTime(now, duration), ec); @@ -259,9 +309,12 @@ void MediaControlRootElementChromium::reportedError() if (!page) return; - m_timelineContainer->hide(); + m_timeline->hide(); m_panelMuteButton->hide(); - m_volumeSliderContainer->hide(); + m_volumeSlider->hide(); +#if ENABLE(FULLSCREEN_MEDIA_CONTROLS) + m_fullscreenButton->hide(); +#endif } void MediaControlRootElementChromium::updateStatusDisplay() @@ -295,15 +348,55 @@ void MediaControlRootElementChromium::defaultEventHandler(Event* event) if (event->type() == eventNames().mouseoverEvent) { if (!containsRelatedTarget(event)) { m_isMouseOverControls = true; - if (!m_mediaController->canPlay()) + if (!m_mediaController->canPlay()) { makeOpaque(); + if (shouldHideControls()) + startHideFullscreenControlsTimer(); + } } } else if (event->type() == eventNames().mouseoutEvent) { - if (!containsRelatedTarget(event)) + if (!containsRelatedTarget(event)) { m_isMouseOverControls = false; + stopHideFullscreenControlsTimer(); + } + } else 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 2 seconds without a mouse move. + makeOpaque(); + if (shouldHideControls()) + startHideFullscreenControlsTimer(); + } } } +void MediaControlRootElementChromium::startHideFullscreenControlsTimer() +{ + if (!m_isFullscreen) + return; + + m_hideFullscreenControlsTimer.startOneShot(timeWithoutMouseMovementBeforeHidingControls); +} + +void MediaControlRootElementChromium::hideFullscreenControlsTimerFired(Timer<MediaControlRootElementChromium>*) +{ + if (m_mediaController->paused()) + return; + + if (!m_isFullscreen) + return; + + if (!shouldHideControls()) + return; + + makeTransparent(); +} + +void MediaControlRootElementChromium::stopHideFullscreenControlsTimer() +{ + m_hideFullscreenControlsTimer.stop(); +} + void MediaControlRootElementChromium::changedClosedCaptionsVisibility() { } @@ -311,6 +404,11 @@ void MediaControlRootElementChromium::changedClosedCaptionsVisibility() void MediaControlRootElementChromium::changedMute() { m_panelMuteButton->changedMute(); + + if (m_mediaController->muted()) + m_volumeSlider->setVolume(0); + else + m_volumeSlider->setVolume(m_mediaController->volume()); } void MediaControlRootElementChromium::changedVolume() @@ -320,10 +418,16 @@ void MediaControlRootElementChromium::changedVolume() void MediaControlRootElementChromium::enteredFullscreen() { + m_isFullscreen = true; + m_fullscreenButton->setIsFullscreen(true); + startHideFullscreenControlsTimer(); } void MediaControlRootElementChromium::exitedFullscreen() { + m_isFullscreen = false; + m_fullscreenButton->setIsFullscreen(false); + stopHideFullscreenControlsTimer(); } void MediaControlRootElementChromium::showVolumeSlider() @@ -331,7 +435,7 @@ void MediaControlRootElementChromium::showVolumeSlider() if (!m_mediaController->hasAudio()) return; - m_volumeSliderContainer->show(); + m_volumeSlider->show(); } #if ENABLE(VIDEO_TRACK) @@ -344,8 +448,7 @@ void MediaControlRootElementChromium::createTextTrackDisplay() m_textDisplayContainer = textDisplayContainer.get(); // Insert it before the first controller element so it always displays behind the controls. - ExceptionCode ec; - insertBefore(textDisplayContainer.release(), m_panel, ec, true); + insertBefore(textDisplayContainer.release(), m_enclosure, ASSERT_NO_EXCEPTION, true); } void MediaControlRootElementChromium::showTextTrackDisplay() diff --git a/Source/WebCore/html/shadow/MediaControlRootElementChromium.h b/Source/WebCore/html/shadow/MediaControlRootElementChromium.h index 366186b65..f10b728e8 100644 --- a/Source/WebCore/html/shadow/MediaControlRootElementChromium.h +++ b/Source/WebCore/html/shadow/MediaControlRootElementChromium.h @@ -40,6 +40,7 @@ class Event; class MediaControlPanelMuteButtonElement; class MediaControlPlayButtonElement; class MediaControlCurrentTimeDisplayElement; +class MediaControlTimeRemainingDisplayElement; class MediaControlTimelineElement; class MediaControlVolumeSliderElement; class MediaControlFullscreenButtonElement; @@ -47,8 +48,8 @@ class MediaControlTimeDisplayElement; class MediaControlTimelineContainerElement; class MediaControlMuteButtonElement; class MediaControlVolumeSliderElement; -class MediaControlVolumeSliderContainerElement; class MediaControlPanelElement; +class MediaControlChromiumEnclosureElement; class MediaControllerInterface; class MediaPlayer; @@ -60,6 +61,24 @@ class MediaControlTextTrackContainerElement; class MediaControlTextTrackDisplayElement; #endif +class MediaControlChromiumEnclosureElement : public HTMLDivElement { +public: + static PassRefPtr<MediaControlChromiumEnclosureElement> create(Document*); + + virtual const AtomicString& shadowPseudoId() const; + + void setMediaController(MediaControllerInterface* controller) { m_mediaController = controller; } + MediaControllerInterface* mediaController() const { return m_mediaController; } + +protected: + MediaControlChromiumEnclosureElement(Document*); + +private: + virtual bool isMediaControlElement() const { return true; } + + MediaControllerInterface* m_mediaController; +}; + class MediaControlRootElementChromium : public MediaControls { public: static PassRefPtr<MediaControlRootElementChromium> create(Document*); @@ -107,6 +126,9 @@ private: MediaControlRootElementChromium(Document*); virtual void defaultEventHandler(Event*); + void hideFullscreenControlsTimerFired(Timer<MediaControlRootElementChromium>*); + void startHideFullscreenControlsTimer(); + void stopHideFullscreenControlsTimer(); virtual const AtomicString& shadowPseudoId() const; @@ -116,19 +138,24 @@ private: MediaControlPlayButtonElement* m_playButton; MediaControlCurrentTimeDisplayElement* m_currentTimeDisplay; + MediaControlTimeRemainingDisplayElement* m_durationDisplay; MediaControlTimelineElement* m_timeline; MediaControlTimelineContainerElement* m_timelineContainer; MediaControlPanelMuteButtonElement* m_panelMuteButton; MediaControlVolumeSliderElement* m_volumeSlider; - MediaControlVolumeSliderContainerElement* m_volumeSliderContainer; +#if ENABLE(FULLSCREEN_MEDIA_CONTROLS) + MediaControlFullscreenButtonElement* m_fullscreenButton; +#endif MediaControlPanelElement* m_panel; + MediaControlChromiumEnclosureElement* m_enclosure; #if ENABLE(VIDEO_TRACK) MediaControlTextTrackContainerElement* m_textDisplayContainer; - MediaControlTextTrackDisplayElement* m_textTrackDisplay; #endif bool m_opaque; + Timer<MediaControlRootElementChromium> m_hideFullscreenControlsTimer; bool m_isMouseOverControls; + bool m_isFullscreen; }; } diff --git a/Source/WebCore/html/shadow/SliderThumbElement.cpp b/Source/WebCore/html/shadow/SliderThumbElement.cpp index 8422a3019..502dae13f 100644 --- a/Source/WebCore/html/shadow/SliderThumbElement.cpp +++ b/Source/WebCore/html/shadow/SliderThumbElement.cpp @@ -51,10 +51,10 @@ using namespace std; namespace WebCore { -inline static double sliderPosition(HTMLInputElement* element) +inline static Decimal sliderPosition(HTMLInputElement* element) { const StepRange stepRange(element->createStepRange(RejectAny)); - const double oldValue = parseToDoubleForNumberType(element->value(), stepRange.defaultValue()); + const Decimal oldValue = parseToDecimalForNumberType(element->value(), stepRange.defaultValue()); return stepRange.proportionFromValue(stepRange.clampValue(oldValue)); } @@ -62,7 +62,13 @@ inline static bool hasVerticalAppearance(HTMLInputElement* input) { ASSERT(input->renderer()); RenderStyle* sliderStyle = input->renderer()->style(); - return sliderStyle->appearance() == SliderVerticalPart || sliderStyle->appearance() == MediaVolumeSliderPart; + +#if ENABLE(VIDEO) + if (sliderStyle->appearance() == MediaVolumeSliderPart && input->renderer()->theme()->usesVerticalVolumeSlider()) + return true; +#endif + + return sliderStyle->appearance() == SliderVerticalPart; } SliderThumbElement* sliderThumbElementOf(Node* node) @@ -95,7 +101,7 @@ void RenderSliderThumb::updateAppearance(RenderStyle* parentStyle) else if (parentStyle->appearance() == MediaFullScreenVolumeSliderPart) style()->setAppearance(MediaFullScreenVolumeSliderThumbPart); if (style()->hasAppearance()) - theme()->adjustSliderThumbSize(style()); + theme()->adjustSliderThumbSize(style(), toElement(node())); } bool RenderSliderThumb::isSliderThumb() const @@ -108,9 +114,9 @@ void RenderSliderThumb::layout() // Do not cast node() to SliderThumbElement. This renderer is used for // TrackLimitElement too. HTMLInputElement* input = node()->shadowAncestorNode()->toInputElement(); - bool isVertical = style()->appearance() == SliderThumbVerticalPart || style()->appearance() == MediaVolumeSliderThumbPart; + bool isVertical = hasVerticalAppearance(input); - double fraction = sliderPosition(input) * 100; + double fraction = (sliderPosition(input) * 100).toDouble(); if (isVertical) style()->setTop(Length(100 - fraction, Percent)); else if (style()->isLeftToRightDirection()) @@ -236,11 +242,10 @@ void SliderThumbElement::setPositionFromPoint(const LayoutPoint& point) if (position == currentPosition) return; - double fraction = static_cast<double>(position) / trackSize; - if (isVertical || !renderBox()->style()->isLeftToRightDirection()) - fraction = 1 - fraction; + const Decimal ratio = Decimal::fromDouble(static_cast<double>(position) / trackSize); + const Decimal fraction = isVertical || !renderBox()->style()->isLeftToRightDirection() ? Decimal(1) - ratio : ratio; StepRange stepRange(input->createStepRange(RejectAny)); - double value = stepRange.clampValue(stepRange.valueFromProportion(fraction)); + const Decimal value = stepRange.clampValue(stepRange.valueFromProportion(fraction)); // FIXME: This is no longer being set from renderer. Consider updating the method name. input->setValueFromRenderer(serializeForNumberType(value)); @@ -322,12 +327,38 @@ HTMLInputElement* SliderThumbElement::hostInput() const return shadowAncestorNode()->toInputElement(); } -const AtomicString& SliderThumbElement::shadowPseudoId() const +static const AtomicString& sliderThumbShadowPseudoId() { - DEFINE_STATIC_LOCAL(AtomicString, sliderThumb, ("-webkit-slider-thumb")); + DEFINE_STATIC_LOCAL(const AtomicString, sliderThumb, ("-webkit-slider-thumb")); return sliderThumb; } +static const AtomicString& mediaSliderThumbShadowPseudoId() +{ + DEFINE_STATIC_LOCAL(const AtomicString, mediaSliderThumb, ("-webkit-media-slider-thumb")); + 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 TrackLimiterElement::TrackLimiterElement(Document* document) @@ -352,8 +383,22 @@ RenderObject* TrackLimiterElement::createRenderer(RenderArena* arena, RenderStyl const AtomicString& TrackLimiterElement::shadowPseudoId() const { - DEFINE_STATIC_LOCAL(AtomicString, sliderThumb, ("-webkit-slider-thumb")); - return sliderThumb; + HTMLInputElement* input = shadowAncestorNode()->toInputElement(); + 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(); + } } TrackLimiterElement* trackLimiterElementOf(Node* node) @@ -386,8 +431,25 @@ RenderObject* SliderContainerElement::createRenderer(RenderArena* arena, RenderS const AtomicString& SliderContainerElement::shadowPseudoId() const { - DEFINE_STATIC_LOCAL(AtomicString, sliderThumb, ("-webkit-slider-container")); - return sliderThumb; + DEFINE_STATIC_LOCAL(const AtomicString, mediaSliderContainer, ("-webkit-media-slider-container")); + DEFINE_STATIC_LOCAL(const AtomicString, sliderContainer, ("-webkit-slider-container")); + + HTMLInputElement* input = shadowAncestorNode()->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/WebCore/html/shadow/TextControlInnerElements.cpp b/Source/WebCore/html/shadow/TextControlInnerElements.cpp index 100daeb64..5d26841c2 100644 --- a/Source/WebCore/html/shadow/TextControlInnerElements.cpp +++ b/Source/WebCore/html/shadow/TextControlInnerElements.cpp @@ -37,7 +37,7 @@ #include "HTMLTextAreaElement.h" #include "MouseEvent.h" #include "Page.h" -#include "RenderTextControlSingleLine.h" +#include "RenderSearchField.h" #include "RenderView.h" #include "ScriptController.h" #include "ScrollbarTheme.h" @@ -151,7 +151,7 @@ void SearchFieldResultsButtonElement::defaultEventHandler(Event* event) if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) { input->focus(); input->select(); - RenderTextControlSingleLine* renderer = toRenderTextControlSingleLine(input->renderer()); + RenderSearchField* renderer = toRenderSearchField(input->renderer()); if (renderer->popupIsVisible()) renderer->hidePopup(); else if (input->maxResults() > 0) diff --git a/Source/WebCore/html/track/LoadableTextTrack.cpp b/Source/WebCore/html/track/LoadableTextTrack.cpp index e03ff4bbe..d95e8ccf9 100644 --- a/Source/WebCore/html/track/LoadableTextTrack.cpp +++ b/Source/WebCore/html/track/LoadableTextTrack.cpp @@ -37,11 +37,10 @@ namespace WebCore { -LoadableTextTrack::LoadableTextTrack(HTMLTrackElement* track, const String& kind, const String& label, const String& language, bool isDefault) +LoadableTextTrack::LoadableTextTrack(HTMLTrackElement* track, const String& kind, const String& label, const String& language, bool) : TextTrack(track->document(), track, kind, label, language, TrackElement) , m_trackElement(track) , m_loadTimer(this, &LoadableTextTrack::loadTimerFired) - , m_isDefault(isDefault) { } diff --git a/Source/WebCore/html/track/LoadableTextTrack.h b/Source/WebCore/html/track/LoadableTextTrack.h index ad6f6addf..33183db9b 100644 --- a/Source/WebCore/html/track/LoadableTextTrack.h +++ b/Source/WebCore/html/track/LoadableTextTrack.h @@ -76,7 +76,6 @@ private: Timer<LoadableTextTrack> m_loadTimer; OwnPtr<TextTrackLoader> m_loader; KURL m_url; - bool m_isDefault; }; } // namespace WebCore diff --git a/Source/WebCore/html/track/TextTrack.cpp b/Source/WebCore/html/track/TextTrack.cpp index 20d53c68c..a6e7f844c 100644 --- a/Source/WebCore/html/track/TextTrack.cpp +++ b/Source/WebCore/html/track/TextTrack.cpp @@ -78,6 +78,7 @@ const AtomicString& TextTrack::metadataKeyword() TextTrack::TextTrack(ScriptExecutionContext* context, TextTrackClient* client, const String& kind, const String& label, const String& language, TextTrackType type) : TrackBase(context, TrackBase::TextTrack) + , m_cues(0) , m_mediaElement(0) , m_label(label) , m_language(language) diff --git a/Source/WebCore/html/track/TextTrackCue.cpp b/Source/WebCore/html/track/TextTrackCue.cpp index 85c7b2c52..d2de4d3e5 100644 --- a/Source/WebCore/html/track/TextTrackCue.cpp +++ b/Source/WebCore/html/track/TextTrackCue.cpp @@ -40,6 +40,7 @@ #include "DocumentFragment.h" #include "Event.h" #include "HTMLDivElement.h" +#include "HTMLMediaElement.h" #include "Text.h" #include "TextTrack.h" #include "TextTrackCueList.h" @@ -99,10 +100,14 @@ TextTrackCue::TextTrackCue(ScriptExecutionContext* context, const String& id, do , m_cueIndex(invalidCueIndex) , m_writingDirection(Horizontal) , m_cueAlignment(Middle) + , m_documentFragment(0) , m_scriptExecutionContext(context) , m_isActive(false) , m_pauseOnExit(pauseOnExit) , m_snapToLines(true) + , m_hasInnerTimestamps(false) + , m_pastDocumentNodes(HTMLDivElement::create(static_cast<Document*>(context))) + , m_futureDocumentNodes(HTMLDivElement::create(static_cast<Document*>(context))) , m_displayTreeShouldChange(true) , m_displayTree(HTMLDivElement::create(static_cast<Document*>(context))) , m_displayXPosition(undefinedPosition) @@ -372,9 +377,16 @@ PassRefPtr<DocumentFragment> TextTrackCue::getCueAsHTML() RefPtr<DocumentFragment> clonedFragment; Document* document; - if (!m_documentFragment) + if (!m_documentFragment) { + m_hasInnerTimestamps = false; m_documentFragment = WebVTTParser::create(0, m_scriptExecutionContext)->createDocumentFragmentFromCueText(m_content); + for (Node *child = m_documentFragment->firstChild(); !m_hasInnerTimestamps && child; child = child->nextSibling()) { + if (child->nodeName() == "timestamp") + m_hasInnerTimestamps = true; + } + } + document = static_cast<Document*>(m_scriptExecutionContext); clonedFragment = DocumentFragment::create(document); @@ -528,9 +540,53 @@ void TextTrackCue::calculateDisplayParameters() // FIXME(Bug 79916): CSS top and left properties need to be applied. } +void TextTrackCue::updateDisplayTree(float movieTime) +{ + // The display tree may contain WebVTT timestamp objects representing + // timestamps (processing instructions), along with displayable nodes. + DEFINE_STATIC_LOCAL(const String, timestampTag, ("timestamp")); + + DEFINE_STATIC_LOCAL(const AtomicString, trackPastNodesShadowPseudoId, ("-webkit-media-text-track-past-nodes")); + DEFINE_STATIC_LOCAL(const AtomicString, trackFutureNodesShadowPseudoId, ("-webkit-media-text-track-future-nodes")); + + bool isPastNode = true; + + // Clear the contents of the two sets. + m_futureDocumentNodes->removeChildren(); + m_futureDocumentNodes->setShadowPseudoId(trackFutureNodesShadowPseudoId); + + m_pastDocumentNodes->removeChildren(); + m_pastDocumentNodes->setShadowPseudoId(trackPastNodesShadowPseudoId); + + // Update the two sets containing past and future WebVTT objects. + RefPtr<DocumentFragment> referenceTree = getCueAsHTML(); + + if (!m_hasInnerTimestamps) { + m_pastDocumentNodes->appendChild(referenceTree); + return; + } + + for (Node *child = referenceTree->firstChild(); child; child = child->nextSibling()) { + if (child->nodeName() == timestampTag) { + unsigned int position = 0; + String timestamp = child->nodeValue(); + + double timestampTime = WebVTTParser::create(0, m_scriptExecutionContext)->collectTimeStamp(timestamp, &position); + ASSERT(timestampTime != -1); + + if (timestampTime > movieTime) + isPastNode = false; + } + + if (isPastNode) + m_pastDocumentNodes->appendChild(child->cloneNode(true), ASSERT_NO_EXCEPTION, false); + else + m_futureDocumentNodes->appendChild(child->cloneNode(true), ASSERT_NO_EXCEPTION, false); + } +} + PassRefPtr<HTMLDivElement> TextTrackCue::getDisplayTree() { - DEFINE_STATIC_LOCAL(const AtomicString, trackBackgroundShadowPseudoId, ("-webkit-media-text-track-background")); DEFINE_STATIC_LOCAL(const AtomicString, trackDisplayBoxShadowPseudoId, ("-webkit-media-text-track-display")); if (!m_displayTreeShouldChange) @@ -542,6 +598,7 @@ PassRefPtr<HTMLDivElement> TextTrackCue::getDisplayTree() // 10.11. Apply the terms of the CSS specifications to nodes within the // following constraints, thus obtaining a set of CSS boxes positioned // relative to an initial containing block: + m_displayTree->setShadowPseudoId(trackDisplayBoxShadowPseudoId, ASSERT_NO_EXCEPTION); m_displayTree->removeChildren(); // The document tree is the tree of WebVTT Node Objects rooted at nodes. @@ -549,13 +606,10 @@ PassRefPtr<HTMLDivElement> TextTrackCue::getDisplayTree() // The children of the nodes must be wrapped in an anonymous box whose // 'display' property has the value 'inline'. This is the WebVTT cue // background box. - RefPtr<HTMLDivElement> cueBackgroundBox = HTMLDivElement::create(static_cast<Document*>(m_scriptExecutionContext)); - cueBackgroundBox->setShadowPseudoId(trackBackgroundShadowPseudoId); - cueBackgroundBox->appendChild(getCueAsHTML(), ASSERT_NO_EXCEPTION, true); - - m_displayTree->setShadowPseudoId(trackDisplayBoxShadowPseudoId, ASSERT_NO_EXCEPTION); - m_displayTree->appendChild(cueBackgroundBox, ASSERT_NO_EXCEPTION, true); + // Note: This is contained by default in m_pastDocumentNodes. + m_displayTree->appendChild(m_pastDocumentNodes, ASSERT_NO_EXCEPTION, true); + m_displayTree->appendChild(m_futureDocumentNodes, ASSERT_NO_EXCEPTION, true); // FIXME(BUG 79916): Runs of children of WebVTT Ruby Objects that are not // WebVTT Ruby Text Objects must be wrapped in anonymous boxes whose @@ -601,6 +655,9 @@ PassRefPtr<HTMLDivElement> TextTrackCue::getDisplayTree() CSSValuePre); } + if (m_hasInnerTimestamps) + updateDisplayTree(track()->mediaElement()->currentTime()); + m_displayTreeShouldChange = false; // 10.15. Let cue's text track cue display state have the CSS boxes in diff --git a/Source/WebCore/html/track/TextTrackCue.h b/Source/WebCore/html/track/TextTrackCue.h index a2cf30b1d..9c5ba5b71 100644 --- a/Source/WebCore/html/track/TextTrackCue.h +++ b/Source/WebCore/html/track/TextTrackCue.h @@ -102,6 +102,7 @@ public: void setIsActive(bool); PassRefPtr<HTMLDivElement> getDisplayTree(); + void updateDisplayTree(float); void removeDisplayTree(); virtual const AtomicString& interfaceName() const; @@ -167,6 +168,10 @@ private: bool m_pauseOnExit; bool m_snapToLines; + bool m_hasInnerTimestamps; + RefPtr<HTMLDivElement> m_pastDocumentNodes; + RefPtr<HTMLDivElement> m_futureDocumentNodes; + bool m_displayTreeShouldChange; RefPtr<HTMLDivElement> m_displayTree; diff --git a/Source/WebCore/html/track/TextTrackList.idl b/Source/WebCore/html/track/TextTrackList.idl index aedda2e07..3f6efac0c 100644 --- a/Source/WebCore/html/track/TextTrackList.idl +++ b/Source/WebCore/html/track/TextTrackList.idl @@ -28,6 +28,7 @@ module html { interface [ Conditional=VIDEO_TRACK, V8EnabledAtRuntime=webkitVideoTrack, + V8CustomToJSObject, IndexedGetter, EventTarget, JSCustomMarkFunction, diff --git a/Source/WebCore/html/track/WebVTTParser.h b/Source/WebCore/html/track/WebVTTParser.h index 0b3b066aa..522bbffa7 100644 --- a/Source/WebCore/html/track/WebVTTParser.h +++ b/Source/WebCore/html/track/WebVTTParser.h @@ -94,6 +94,7 @@ public: void getNewCues(Vector<RefPtr<TextTrackCue> >&); PassRefPtr<DocumentFragment> createDocumentFragmentFromCueText(const String&); + double collectTimeStamp(const String&, unsigned*); protected: WebVTTParser(WebVTTParserClient*, ScriptExecutionContext*); @@ -110,7 +111,7 @@ private: void createNewCue(); void resetCueValues(); - double collectTimeStamp(const String&, unsigned*); + void skipWhiteSpace(const String&, unsigned*); static void skipLineTerminator(const char* data, unsigned length, unsigned*); static String collectNextLine(const char* data, unsigned length, unsigned*); |